GNU Octave  9.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
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