GNU Octave  4.2.1
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-2017 Julien Bect
4 Copyright (C) 2012-2016 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 #if defined (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 {
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
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 = 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 
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 (new tree_node (0, 0)),
180  active_fcn (0), last_time (-1.0)
181 { }
182 
184 {
185  delete call_tree;
186 }
187 
188 void
190 {
191  enabled = value;
192 }
193 
194 void
196 {
197  // The enter class will check and only call us if the profiler is active.
198  assert (is_active ());
199  assert (call_tree);
200 
201  // If there is already an active function, add to its time before
202  // pushing the new one.
203  if (active_fcn && active_fcn != call_tree)
204  add_current_time ();
205 
206  // Map the function's name to its index.
207  octave_idx_type fcn_idx;
208  fcn_index_map::iterator pos = fcn_index.find (fcn);
209  if (pos == fcn_index.end ())
210  {
211  known_functions.push_back (fcn);
212  fcn_idx = known_functions.size ();
213  fcn_index[fcn] = fcn_idx;
214  }
215  else
216  fcn_idx = pos->second;
217 
218  if (! active_fcn)
220 
221  active_fcn = active_fcn->enter (fcn_idx);
222 
223  last_time = query_time ();
224 
225 }
226 
227 void
229 {
230  if (active_fcn)
231  {
232  assert (call_tree);
233  // FIXME: This assert statements doesn't make sense if profile() is called
234  // from within a function hierarchy to begin with. See bug #39587.
235  //assert (active_fcn != call_tree);
236 
237  // Usually, if we are disabled this function is not even called. But the
238  // call disabling the profiler is an exception. So also check here
239  // and only record the time if enabled.
240  if (is_active ())
241  add_current_time ();
242 
243  fcn_index_map::iterator pos = fcn_index.find (fcn);
244  // FIXME: This assert statements doesn't make sense if profile() is called
245  // from within a function hierarchy to begin with. See bug #39587.
246  //assert (pos != fcn_index.end ());
247  active_fcn = 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  last_time = query_time ();
252  }
253 }
254 
255 void
257 {
258  if (is_active ())
259  error ("Can't reset active profiler.");
260 
261  known_functions.clear ();
262  fcn_index.clear ();
263 
264  if (call_tree)
265  {
266  delete call_tree;
267  call_tree = new tree_node (0, 0);
268  active_fcn = 0;
269  }
270 
271  last_time = -1.0;
272 }
273 
276 {
278 
279  const octave_idx_type n = known_functions.size ();
280 
281  flat_profile flat (n);
282 
283  if (call_tree)
284  {
285  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 (known_functions[i]);
297  rv_times(i) = octave_value (flat[i].time);
298  rv_calls(i) = octave_value (flat[i].calls);
299  rv_recursive(i) = octave_value (flat[i].recursive);
300  rv_parents(i) = stats::function_set_value (flat[i].parents);
301  rv_children(i) = stats::function_set_value (flat[i].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  0
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 {
340 
341  if (call_tree)
342  retval = call_tree->get_hierarchical ();
343  else
344  {
345  static const char *fn[] =
346  {
347  "Index",
348  "SelfTime",
349  "NumCalls",
350  "Children",
351  0
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
364 {
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
376 {
377  if (active_fcn)
378  {
379  const double t = query_time ();
380 
382  }
383 }
384 
386 
387 // Enable or disable the profiler data collection.
388 DEFUN (__profiler_enable__, args, ,
389  doc: /* -*- texinfo -*-
390 @deftypefn {} {} __profiler_enable__ ()
391 Undocumented internal function.
392 @end deftypefn */)
393 {
394  int nargin = args.length ();
395 
396  if (nargin > 1)
397  print_usage ();
398 
399  if (nargin > 0)
400  profiler.set_active (args(0).bool_value ());
401 
402  return ovl (profiler.is_active ());
403 }
404 
405 // Clear all collected profiling data.
406 DEFUN (__profiler_reset__, args, ,
407  doc: /* -*- texinfo -*-
408 @deftypefn {} {} __profiler_reset__ ()
409 Undocumented internal function.
410 @end deftypefn */)
411 {
412  if (args.length () > 0)
413  warning ("profiler_reset: ignoring extra arguments");
414 
415  profiler.reset ();
416 
417  return ovl ();
418 }
419 
420 // Query the timings collected by the profiler.
421 DEFUN (__profiler_data__, args, nargout,
422  doc: /* -*- texinfo -*-
423 @deftypefn {} {} __profiler_data__ ()
424 Undocumented internal function.
425 @end deftypefn */)
426 {
427  if (args.length () > 0)
428  warning ("profiler_data: ignoring extra arguments");
429 
430  if (nargout > 1)
431  return ovl (profiler.get_flat (), profiler.get_hierarchical ());
432  else
433  return ovl (profiler.get_flat ());
434 }
Definition: Cell.h:37
The value of lines which begin with a space character are not saved in the history list A value of all commands are saved on the history list
Definition: oct-hist.cc:728
void assign(const std::string &k, const Cell &val)
Definition: oct-map.h:347
OCTAVE_EXPORT octave_value_list isa nd deftypefn *return ovl(args(0).is_integer_type())
OCTINTERP_API void print_usage(void)
Definition: defun.cc:52
F77_RET_T F77_REAL &F77_RET_T F77_DBLE &F77_RET_T F77_REAL &F77_RET_T F77_DBLE &F77_RET_T F77_REAL &F77_RET_T F77_DBLE &F77_RET_T const F77_REAL const F77_REAL F77_REAL &F77_RET_T const F77_DBLE const F77_DBLE F77_DBLE &F77_RET_T F77_REAL &F77_RET_T F77_DBLE &F77_RET_T F77_DBLE &F77_RET_T F77_REAL &F77_RET_T F77_REAL &F77_RET_T F77_DBLE &F77_RET_T const F77_DBLE F77_DBLE &F77_RET_T const F77_REAL F77_REAL &F77_RET_T F77_REAL F77_REAL &F77_RET_T F77_DBLE F77_DBLE &F77_RET_T const F77_DBLE const F77_DBLE * f
function_set known_functions
Definition: profiler.h:174
octave::sys::time now
Definition: data.cc:6299
octave_value get_hierarchical(void) const
Definition: profiler.cc:337
#define DEFUN(name, args_name, nargout_name, doc)
Definition: defun.h:46
void error(const char *fmt,...)
Definition: error.cc:570
tree_node * enter(octave_idx_type)
Definition: profiler.cc:69
tree_node(tree_node *, octave_idx_type)
Definition: profiler.cc:58
OCTAVE_EXPORT octave_value_list return the number of command line arguments passed to Octave If called with the optional argument the function t
Definition: ov-usr-fcn.cc:935
tree_node * exit(octave_idx_type)
Definition: profiler.cc:87
void add_current_time(void)
Definition: profiler.cc:375
tree_node * call_tree
Definition: profiler.h:179
octave_function * fcn
Definition: ov-class.cc:1743
void build_flat(flat_profile &) const
Definition: profiler.cc:98
bool is_active(void) const
Definition: profiler.h:90
JNIEnv void * args
Definition: ov-java.cc:67
double double_value(void) const
Definition: oct-time.h:93
OCTAVE_EXPORT octave_value_list return the number of command line arguments passed to Octave If called with the optional argument the function xample nargout(@histc)
Definition: ov-usr-fcn.cc:935
std::vector< stats > flat_profile
Definition: profiler.h:120
nd deftypefn *octave_map m
Definition: ov-struct.cc:2058
int nargin
Definition: graphics.cc:10115
const T * data(void) const
Definition: Array.h:582
octave_value get_flat(void) const
Definition: profiler.cc:275
is false
Definition: cellfun.cc:398
octave_value retval
Definition: data.cc:6294
fcn_index_map fcn_index
Definition: profiler.h:175
void exit_function(const std::string &)
Definition: profiler.cc:228
std::set< octave_idx_type > function_set
Definition: profiler.h:112
double query_time() const
Definition: profiler.cc:363
void warning(const char *fmt,...)
Definition: error.cc:788
virtual ~profile_data_accumulator()
Definition: profiler.cc:183
static octave_value function_set_value(const function_set &)
Definition: profiler.cc:42
=val(i)}if ode{val(i)}occurs in table i
Definition: lookup.cc:239
void enter_function(const std::string &)
Definition: profiler.cc:195
p
Definition: lu.cc:138
OCTAVE_EXPORT octave_value_list or N dimensional array whose elements are all equal to the IEEE symbol zero divided by nd tex zero divided by nd ifnottex and any operation involving another NaN value(5+NaN).Note that NaN always compares not equal to NaN(NaN!
octave_value get_hierarchical(double *total=0) const
Definition: profiler.cc:131
Vector representing the dimensions (size) of an Array.
Definition: dim-vector.h:87
If this string is the system will ring the terminal sometimes it is useful to be able to print the original representation of the string
Definition: utils.cc:854
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:385
tree_node * active_fcn
Definition: profiler.h:180