GNU Octave  6.2.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-2021 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 "interpreter.h"
32 #include "oct-time.h"
33 #include "ov-struct.h"
34 #include "pager.h"
35 #include "profiler.h"
36 
37 namespace octave
38 {
40  : m_time (0.0), m_calls (0), m_recursive (false),
41  m_parents (), m_children ()
42  { }
43 
46  {
47  const octave_idx_type n = list.size ();
48 
49  RowVector retval (n);
50  octave_idx_type i = 0;
51  for (const auto& nm : list)
52  retval(i++) = nm;
53 
54  assert (i == n);
55 
56  return retval;
57  }
58 
60  : m_parent (p), m_fcn_id (f), m_children (), m_time (0.0), m_calls (0)
61  { }
62 
64  {
65  for (auto& idx_tnode : m_children)
66  delete idx_tnode.second;
67  }
68 
71  {
73 
74  child_map::iterator pos = m_children.find (fcn);
75  if (pos == m_children.end ())
76  {
77  retval = new tree_node (this, fcn);
78  m_children[fcn] = retval;
79  }
80  else
81  retval = pos->second;
82 
83  ++retval->m_calls;
84  return retval;
85  }
86 
89  {
90  // FIXME: These assert statements don't make sense if profile() is called
91  // from within a function hierarchy to begin with. See bug #39587.
92  // assert (m_parent);
93  // assert (m_fcn_id == fcn);
94 
95  return m_parent;
96  }
97 
98  void
100  {
101  // If this is not the top-level node, 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  assert (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 
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  assert (i == n);
164 
166 
167  retval.assign ("Index", rv_indices);
168  retval.assign ("SelfTime", rv_times);
169  retval.assign ("TotalTime", rv_totals);
170  retval.assign ("NumCalls", rv_calls);
171  retval.assign ("Children", rv_children);
172 
173  return retval;
174  }
175 
178  m_enabled (false), m_call_tree (new tree_node (nullptr, 0)),
179  m_active_fcn (nullptr), m_last_time (-1.0)
180  { }
181 
183  {
184  delete m_call_tree;
185  }
186 
187  void
188  profiler::set_active (bool value)
189  {
190  m_enabled = value;
191  }
192 
193  void
194  profiler::enter_function (const std::string& fcn)
195  {
196  // The enter class will check and only call us if the profiler is active.
197  assert (enabled ());
198  assert (m_call_tree);
199 
200  // If there is already an active function, add to its time before
201  // pushing the new one.
203  add_current_time ();
204 
205  // Map the function's name to its index.
206  octave_idx_type fcn_idx;
207  fcn_index_map::iterator pos = m_fcn_index.find (fcn);
208  if (pos == m_fcn_index.end ())
209  {
210  m_known_functions.push_back (fcn);
211  fcn_idx = m_known_functions.size ();
212  m_fcn_index[fcn] = fcn_idx;
213  }
214  else
215  fcn_idx = pos->second;
216 
217  if (! m_active_fcn)
219 
220  m_active_fcn = m_active_fcn->enter (fcn_idx);
221 
222  m_last_time = query_time ();
223 
224  }
225 
226  void
227  profiler::exit_function (const std::string& fcn)
228  {
229  if (m_active_fcn)
230  {
231  assert (m_call_tree);
232  // FIXME: This assert statements doesn't make sense if profile() is called
233  // from within a function hierarchy to begin with. See bug #39587.
234  //assert (m_active_fcn != m_call_tree);
235 
236  // Usually, if we are disabled this function is not even called. But the
237  // 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 assert statements doesn't make sense if profile() is called
244  // from within a function hierarchy to begin with. See bug #39587.
245  //assert (pos != m_fcn_index.end ());
246  m_active_fcn = m_active_fcn->exit (pos->second);
247 
248  // If this was an "inner call", we resume executing the parent function
249  // up the stack. So note the start-time for this!
250  m_last_time = query_time ();
251  }
252  }
253 
254  void
256  {
257  if (enabled ())
258  error ("Can't reset active profiler.");
259 
260  m_known_functions.clear ();
261  m_fcn_index.clear ();
262 
263  if (m_call_tree)
264  {
265  delete m_call_tree;
266  m_call_tree = new tree_node (nullptr, 0);
267  m_active_fcn = nullptr;
268  }
269 
270  m_last_time = -1.0;
271  }
272 
274  profiler::get_flat (void) const
275  {
277 
278  const octave_idx_type n = m_known_functions.size ();
279 
280  flat_profile flat (n);
281 
282  if (m_call_tree)
283  {
284  m_call_tree->build_flat (flat);
285 
286  Cell rv_names (n, 1);
287  Cell rv_times (n, 1);
288  Cell rv_calls (n, 1);
289  Cell rv_recursive (n, 1);
290  Cell rv_parents (n, 1);
291  Cell rv_children (n, 1);
292 
293  for (octave_idx_type i = 0; i != n; ++i)
294  {
295  rv_names(i) = octave_value (m_known_functions[i]);
296  rv_times(i) = octave_value (flat[i].m_time);
297  rv_calls(i) = octave_value (flat[i].m_calls);
298  rv_recursive(i) = octave_value (flat[i].m_recursive);
299  rv_parents(i) = stats::function_set_value (flat[i].m_parents);
300  rv_children(i) = stats::function_set_value (flat[i].m_children);
301  }
302 
303  octave_map m;
304 
305  m.assign ("FunctionName", rv_names);
306  m.assign ("TotalTime", rv_times);
307  m.assign ("NumCalls", rv_calls);
308  m.assign ("IsRecursive", rv_recursive);
309  m.assign ("Parents", rv_parents);
310  m.assign ("Children", rv_children);
311 
312  retval = m;
313  }
314  else
315  {
316  static const char *fn[] =
317  {
318  "FunctionName",
319  "TotalTime",
320  "NumCalls",
321  "IsRecursive",
322  "Parents",
323  "Children",
324  nullptr
325  };
326 
327  static octave_map m (dim_vector (0, 1), string_vector (fn));
328 
329  retval = m;
330  }
331 
332  return retval;
333  }
334 
337  {
339 
340  if (m_call_tree)
342  else
343  {
344  static const char *fn[] =
345  {
346  "Index",
347  "SelfTime",
348  "NumCalls",
349  "Children",
350  nullptr
351  };
352 
353  static octave_map m (dim_vector (0, 1), string_vector (fn));
354 
355  retval = m;
356  }
357 
358  return retval;
359  }
360 
361  double
362  profiler::query_time (void) const
363  {
364  sys::time now;
365 
366  // FIXME: is this volatile declaration really needed?
367  // See bug #34210 for additional details.
368  volatile double dnow = now.double_value ();
369 
370  return dnow;
371  }
372 
373  void
375  {
376  if (m_active_fcn)
377  {
378  const double t = query_time ();
379 
381  }
382  }
383 }
384 
385 // Enable or disable the profiler data collection.
386 DEFMETHOD (__profiler_enable__, interp, args, ,
387  doc: /* -*- texinfo -*-
388 @deftypefn {} {} __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  octave::profiler& profiler = interp.get_profiler ();
398 
399  if (nargin == 1)
400  profiler.set_active (args(0).bool_value ());
401 
402  return ovl (profiler.enabled ());
403 }
404 
405 // Clear all collected profiling data.
406 DEFMETHOD (__profiler_reset__, interp, args, ,
407  doc: /* -*- texinfo -*-
408 @deftypefn {} {} __profiler_reset__ ()
409 Undocumented internal function.
410 @end deftypefn */)
411 {
412  if (args.length () != 0)
413  print_usage ();
414 
415  octave::profiler& profiler = interp.get_profiler ();
416 
417  profiler.reset ();
418 
419  return ovl ();
420 }
421 
422 // Query the timings collected by the profiler.
423 DEFMETHOD (__profiler_data__, interp, args, nargout,
424  doc: /* -*- texinfo -*-
425 @deftypefn {} {} __profiler_data__ ()
426 Undocumented internal function.
427 @end deftypefn */)
428 {
429  if (args.length () != 0)
430  print_usage ();
431 
432  octave::profiler& profiler = interp.get_profiler ();
433 
434  if (nargout > 1)
435  return ovl (profiler.get_flat (), profiler.get_hierarchical ());
436  else
437  return ovl (profiler.get_flat ());
438 }
void assign(const idx_vector &i, const Array< T > &rhs, const T &rfv)
Indexed assignment (always with resize & fill).
Definition: Array.cc:1116
Array< octave_idx_type > find(octave_idx_type n=-1, bool backward=false) const
Find indices of (at most n) nonzero elements.
Definition: Array.cc:2225
Definition: Cell.h:43
Vector representing the dimensions (size) of an Array.
Definition: dim-vector.h:95
tree_node(tree_node *, octave_idx_type)
Definition: profiler.cc:59
void build_flat(flat_profile &) const
Definition: profiler.cc:99
tree_node * enter(octave_idx_type)
Definition: profiler.cc:70
tree_node * exit(octave_idx_type)
Definition: profiler.cc:88
void add_time(double dt)
Definition: profiler.h:150
octave_value get_hierarchical(double *total=nullptr) const
Definition: profiler.cc:131
virtual ~tree_node(void)
Definition: profiler.cc:63
octave_value get_flat(void) const
Definition: profiler.cc:274
void exit_function(const std::string &)
Definition: profiler.cc:227
void set_active(bool)
Definition: profiler.cc:188
fcn_index_map m_fcn_index
Definition: profiler.h:190
function_set m_known_functions
Definition: profiler.h:189
double m_last_time
Definition: profiler.h:198
void reset(void)
Definition: profiler.cc:255
std::vector< stats > flat_profile
Definition: profiler.h:132
bool enabled(void) const
Definition: profiler.h:102
tree_node * m_active_fcn
Definition: profiler.h:195
tree_node * m_call_tree
Definition: profiler.h:194
virtual ~profiler(void)
Definition: profiler.cc:182
double query_time(void) const
Definition: profiler.cc:362
octave_value get_hierarchical(void) const
Definition: profiler.cc:336
void enter_function(const std::string &)
Definition: profiler.cc:194
void add_current_time(void)
Definition: profiler.cc:374
double double_value(void) const
Definition: oct-time.h:108
OCTINTERP_API void print_usage(void)
Definition: defun.cc:53
#define DEFMETHOD(name, interp_name, args_name, nargout_name, doc)
Macro to define a builtin method.
Definition: defun.h:138
void error(const char *fmt,...)
Definition: error.cc:968
T octave_idx_type m
Definition: mx-inlines.cc:773
octave_idx_type n
Definition: mx-inlines.cc:753
static double f(double k, double l_nu, double c_pm)
Definition: randpoisson.cc:118
return octave_value(v1.char_array_value() . concat(v2.char_array_value(), ra_idx),((a1.is_sq_string()||a2.is_sq_string()) ? '\'' :'"'))
octave_value::octave_value(const Array< char > &chm, char type) return retval
Definition: ov.cc:811
octave_value_list ovl(const OV_Args &... args)
Construct an octave_value_list with less typing.
Definition: ovl.h:211
function_set m_parents
Definition: profiler.h:125
static octave_value function_set_value(const function_set &)
Definition: profiler.cc:45
std::set< octave_idx_type > function_set
Definition: profiler.h:124