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