GNU Octave  9.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
profiler.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 2012-2024 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software: you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING. If not, see
22 // <https://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25 
26 #if defined (HAVE_CONFIG_H)
27 # include "config.h"
28 #endif
29 
30 #include "defun.h"
31 #include "event-manager.h"
32 #include "interpreter.h"
33 #include "oct-time.h"
34 #include "ov-struct.h"
35 #include "pager.h"
36 #include "profiler.h"
37 
39 
40 profiler::stats::stats ()
41  : m_time (0.0), m_calls (0), m_recursive (false),
42  m_parents (), m_children ()
43 { }
44 
46 profiler::stats::function_set_value (const function_set& list)
47 {
48  const octave_idx_type n = list.size ();
49 
50  RowVector retval (n);
51  octave_idx_type i = 0;
52  for (const auto& nm : list)
53  retval(i++) = nm;
54 
55  return retval;
56 }
57 
58 profiler::tree_node::tree_node (tree_node *p, octave_idx_type f)
59  : m_parent (p), m_fcn_id (f), m_children (), m_time (0.0), m_calls (0)
60 { }
61 
62 profiler::tree_node::~tree_node ()
63 {
64  for (auto& idx_tnode : m_children)
65  delete idx_tnode.second;
66 }
67 
68 profiler::tree_node *
69 profiler::tree_node::enter (octave_idx_type fcn)
70 {
71  tree_node *retval;
72 
73  child_map::iterator pos = m_children.find (fcn);
74  if (pos == m_children.end ())
75  {
76  retval = new tree_node (this, fcn);
77  m_children[fcn] = retval;
78  }
79  else
80  retval = pos->second;
81 
82  ++retval->m_calls;
83  return retval;
84 }
85 
86 profiler::tree_node *
87 profiler::tree_node::exit (octave_idx_type /* fcn */)
88 {
89  // FIXME: These panic_unless statements don't make sense if profile() is
90  // called from within a function hierarchy to begin with. See bug #39587.
91  // panic_unless (m_parent);
92  // panic_unless (m_fcn_id == fcn);
93 
94  return m_parent;
95 }
96 
97 void
98 profiler::tree_node::build_flat (flat_profile& data) const
99 {
100  // If this is not the top-level node,
101  // update profile entry for this function.
102  if (m_fcn_id != 0)
103  {
104  stats& entry = data[m_fcn_id - 1];
105 
106  entry.m_time += m_time;
107  entry.m_calls += m_calls;
108 
109  panic_unless (m_parent);
110  if (m_parent->m_fcn_id != 0)
111  {
112  entry.m_parents.insert (m_parent->m_fcn_id);
113  data[m_parent->m_fcn_id - 1].m_children.insert (m_fcn_id);
114  }
115 
116  if (! entry.m_recursive)
117  for (const tree_node *i = m_parent; i; i = i->m_parent)
118  if (i->m_fcn_id == m_fcn_id)
119  {
120  entry.m_recursive = true;
121  break;
122  }
123  }
124 
125  // Recurse on children.
126  for (const auto& idx_tnode : m_children)
127  idx_tnode.second->build_flat (data);
128 }
129 
131 profiler::tree_node::get_hierarchical (double *total) const
132 {
133  // Note that we don't generate the entry just for this node, but
134  // rather a struct-array with entries for all children. This way, the
135  // top-node (for which we don't want a real entry) generates already
136  // the final hierarchical profile data.
137 
138  const octave_idx_type n = m_children.size ();
139 
140  Cell rv_indices (n, 1);
141  Cell rv_times (n, 1);
142  Cell rv_totals (n, 1);
143  Cell rv_calls (n, 1);
144  Cell rv_children (n, 1);
145 
146  octave_idx_type i = 0;
147  for (const auto& idx_tnode : m_children)
148  {
149  const tree_node& entry = *idx_tnode.second;
150  double child_total = entry.m_time;
151 
152  rv_indices(i) = octave_value (idx_tnode.first);
153  rv_times(i) = octave_value (entry.m_time);
154  rv_calls(i) = octave_value (entry.m_calls);
155  rv_children(i) = entry.get_hierarchical (&child_total);
156  rv_totals(i) = octave_value (child_total);
157 
158  if (total)
159  *total += child_total;
160 
161  ++i;
162  }
163 
164  octave_map retval;
165 
166  retval.assign ("Index", rv_indices);
167  retval.assign ("SelfTime", rv_times);
168  retval.assign ("TotalTime", rv_totals);
169  retval.assign ("NumCalls", rv_calls);
170  retval.assign ("Children", rv_children);
171 
172  return retval;
173 }
174 
176  : m_known_functions (), m_fcn_index (),
177  m_enabled (false), m_call_tree (new tree_node (nullptr, 0)),
178  m_active_fcn (nullptr), m_last_time (-1.0)
179 { }
180 
182 {
183  delete m_call_tree;
184 }
185 
186 void
188 {
189  m_enabled = value;
190 }
191 
192 void
193 profiler::enter_function (const std::string& fcn)
194 {
195  // The enter class will check and only call us if the profiler is active.
196  panic_unless (enabled ());
197  panic_unless (m_call_tree);
198 
199  // If there is already an active function, add to its time before
200  // pushing the new one.
201  if (m_active_fcn && m_active_fcn != m_call_tree)
202  add_current_time ();
203 
204  // Map the function's name to its index.
205  octave_idx_type fcn_idx;
206  fcn_index_map::iterator pos = m_fcn_index.find (fcn);
207  if (pos == m_fcn_index.end ())
208  {
209  m_known_functions.push_back (fcn);
210  fcn_idx = m_known_functions.size ();
211  m_fcn_index[fcn] = fcn_idx;
212  }
213  else
214  fcn_idx = pos->second;
215 
216  if (! m_active_fcn)
217  m_active_fcn = m_call_tree;
218 
219  m_active_fcn = m_active_fcn->enter (fcn_idx);
220 
221  m_last_time = query_time ();
222 
223 }
224 
225 void
226 profiler::exit_function (const std::string& fcn)
227 {
228  if (m_active_fcn)
229  {
230  panic_unless (m_call_tree);
231  // FIXME: This panic_unless statements doesn't make sense if profile()
232  // is called from within a function hierarchy to begin with.
233  // See bug #39587.
234  // panic_unless (m_active_fcn != m_call_tree);
235 
236  // Usually, if we are disabled this function is not even called. But
237  // the call disabling the profiler is an exception. So also check here
238  // and only record the time if enabled.
239  if (enabled ())
240  add_current_time ();
241 
242  fcn_index_map::iterator pos = m_fcn_index.find (fcn);
243  // FIXME: This panic_unless statements doesn't make sense if profile()
244  // is called from within a function hierarchy to begin with.
245  // See bug #39587.
246  // panic_unless (pos != m_fcn_index.end ());
247  m_active_fcn = m_active_fcn->exit (pos->second);
248 
249  // If this was an "inner call", we resume executing the parent function
250  // up the stack. So note the start-time for this!
251  m_last_time = query_time ();
252  }
253 }
254 
255 void
257 {
258  if (enabled ())
259  error ("profile: can't reset active profiler");
260 
261  m_known_functions.clear ();
262  m_fcn_index.clear ();
263 
264  if (m_call_tree)
265  {
266  delete m_call_tree;
267  m_call_tree = new tree_node (nullptr, 0);
268  m_active_fcn = nullptr;
269  }
270 
271  m_last_time = -1.0;
272 }
273 
276 {
277  octave_value retval;
278 
279  const octave_idx_type n = m_known_functions.size ();
280 
281  flat_profile flat (n);
282 
283  if (m_call_tree)
284  {
285  m_call_tree->build_flat (flat);
286 
287  Cell rv_names (n, 1);
288  Cell rv_times (n, 1);
289  Cell rv_calls (n, 1);
290  Cell rv_recursive (n, 1);
291  Cell rv_parents (n, 1);
292  Cell rv_children (n, 1);
293 
294  for (octave_idx_type i = 0; i != n; ++i)
295  {
296  rv_names(i) = octave_value (m_known_functions[i]);
297  rv_times(i) = octave_value (flat[i].m_time);
298  rv_calls(i) = octave_value (flat[i].m_calls);
299  rv_recursive(i) = octave_value (flat[i].m_recursive);
300  rv_parents(i) = stats::function_set_value (flat[i].m_parents);
301  rv_children(i) = stats::function_set_value (flat[i].m_children);
302  }
303 
304  octave_map m;
305 
306  m.assign ("FunctionName", rv_names);
307  m.assign ("TotalTime", rv_times);
308  m.assign ("NumCalls", rv_calls);
309  m.assign ("IsRecursive", rv_recursive);
310  m.assign ("Parents", rv_parents);
311  m.assign ("Children", rv_children);
312 
313  retval = m;
314  }
315  else
316  {
317  static const char *fn[] =
318  {
319  "FunctionName",
320  "TotalTime",
321  "NumCalls",
322  "IsRecursive",
323  "Parents",
324  "Children",
325  nullptr
326  };
327 
328  static octave_map m (dim_vector (0, 1), string_vector (fn));
329 
330  retval = m;
331  }
332 
333  return retval;
334 }
335 
338 {
339  octave_value retval;
340 
341  if (m_call_tree)
342  retval = m_call_tree->get_hierarchical ();
343  else
344  {
345  static const char *fn[] =
346  {
347  "Index",
348  "SelfTime",
349  "NumCalls",
350  "Children",
351  nullptr
352  };
353 
354  static octave_map m (dim_vector (0, 1), string_vector (fn));
355 
356  retval = m;
357  }
358 
359  return retval;
360 }
361 
362 double
363 profiler::query_time () const
364 {
365  sys::time now;
366 
367  // FIXME: is this volatile declaration really needed?
368  // See bug #34210 for additional details.
369  volatile double dnow = now.double_value ();
370 
371  return dnow;
372 }
373 
374 void
375 profiler::add_current_time ()
376 {
377  if (m_active_fcn)
378  {
379  const double t = query_time ();
380 
381  m_active_fcn->add_time (t - m_last_time);
382  }
383 }
384 
385 // Enable or disable the profiler data collection.
386 DEFMETHOD (__profiler_enable__, interp, args, ,
387  doc: /* -*- texinfo -*-
388 @deftypefn {} {@var{state} =} __profiler_enable__ ()
389 Undocumented internal function.
390 @end deftypefn */)
391 {
392  int nargin = args.length ();
393 
394  if (nargin > 1)
395  print_usage ();
396 
397  profiler& profiler = interp.get_profiler ();
398 
399  if (nargin == 1)
400  {
401  profiler.set_active (args(0).bool_value ());
402 
403  std::string status = "off";
404  if (args(0).bool_value ())
405  status = "on";
406 
407  event_manager& evmgr = interp.get_event_manager ();
408  evmgr.gui_status_update ("profiler", status); // tell GUI
409  }
410 
411  return ovl (profiler.enabled ());
412 }
413 
414 // Clear all collected profiling data.
415 DEFMETHOD (__profiler_reset__, interp, args, ,
416  doc: /* -*- texinfo -*-
417 @deftypefn {} {} __profiler_reset__ ()
418 Undocumented internal function.
419 @end deftypefn */)
420 {
421  if (args.length () != 0)
422  print_usage ();
423 
424  profiler& profiler = interp.get_profiler ();
425 
426  profiler.reset ();
427 
428  return ovl ();
429 }
430 
431 // Query the timings collected by the profiler.
432 DEFMETHOD (__profiler_data__, interp, args, nargout,
433  doc: /* -*- texinfo -*-
434 @deftypefn {} {@var{data} =} __profiler_data__ ()
435 Undocumented internal function.
436 @end deftypefn */)
437 {
438  if (args.length () != 0)
439  print_usage ();
440 
441  profiler& profiler = interp.get_profiler ();
442 
443  if (nargout > 1)
445  else
446  return ovl (profiler.get_flat ());
447 }
448 
449 OCTAVE_END_NAMESPACE(octave)
Definition: Cell.h:43
Vector representing the dimensions (size) of an Array.
Definition: dim-vector.h:94
Provides threadsafe access to octave.
bool gui_status_update(const std::string &feature, const std::string &status)
void assign(const std::string &k, const Cell &val)
Definition: oct-map.h:344
bool enabled() const
Definition: profiler.h:94
octave_value get_flat() const
Definition: profiler.cc:275
void set_active(bool)
Definition: profiler.cc:187
octave_value get_hierarchical() const
Definition: profiler.cc:337
void reset()
Definition: profiler.cc:256
profiler()
Definition: profiler.cc:175
virtual ~profiler()
Definition: profiler.cc:181
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
void print_usage(void)
Definition: defun-int.h:72
#define DEFMETHOD(name, interp_name, args_name, nargout_name, doc)
Macro to define a builtin method.
Definition: defun.h:111
void() error(const char *fmt,...)
Definition: error.cc:988
#define panic_unless(cond)
Definition: error.h:515
F77_RET_T const F77_DBLE const F77_DBLE * f
T octave_idx_type m
Definition: mx-inlines.cc:781
octave_idx_type n
Definition: mx-inlines.cc:761
return octave_value(v1.char_array_value() . concat(v2.char_array_value(), ra_idx),((a1.is_sq_string()||a2.is_sq_string()) ? '\'' :'"'))
octave_value_list ovl(const OV_Args &... args)
Construct an octave_value_list with less typing.
Definition: ovl.h:219