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