profiler.cc

Go to the documentation of this file.
00001 /*
00002 
00003 Copyright (C) 2012 Daniel Kraft
00004 
00005 This file is part of Octave.
00006 
00007 Octave is free software; you can redistribute it and/or modify it
00008 under the terms of the GNU General Public License as published by the
00009 Free Software Foundation; either version 3 of the License, or (at your
00010 option) any later version.
00011 
00012 Octave is distributed in the hope that it will be useful, but WITHOUT
00013 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00014 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
00015 for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Octave; see the file COPYING.  If not, see
00019 <http://www.gnu.org/licenses/>.
00020 
00021 */
00022 
00023 #ifdef HAVE_CONFIG_H
00024 #include <config.h>
00025 #endif
00026 
00027 #include <iostream>
00028 
00029 #include "defun.h"
00030 #include "oct-time.h"
00031 #include "ov-struct.h"
00032 #include "pager.h"
00033 #include "profiler.h"
00034 
00035 profile_data_accumulator::enter::enter (profile_data_accumulator& a,
00036                                         const std::string& f)
00037   : acc (a)
00038 {
00039   if (acc.is_active ())
00040     {
00041       fcn = f;
00042       acc.enter_function (fcn);
00043     }
00044   else
00045     fcn = "";
00046 }
00047 
00048 profile_data_accumulator::enter::~enter ()
00049 {
00050   if (fcn != "")
00051     acc.exit_function (fcn);
00052 }
00053 
00054 profile_data_accumulator::stats::stats ()
00055   : time (0.0), calls (0), recursive (false),
00056     parents (), children ()
00057 {}
00058 
00059 octave_value
00060 profile_data_accumulator::stats::function_set_value (const function_set& list)
00061 {
00062   const octave_idx_type n = list.size ();
00063 
00064   RowVector retval (n);
00065   octave_idx_type i = 0;
00066   for (function_set::const_iterator p = list.begin (); p != list.end (); ++p)
00067     {
00068       retval(i) = *p;
00069       ++i;
00070     }
00071   assert (i == n);
00072 
00073   return retval;
00074 }
00075 
00076 profile_data_accumulator::tree_node::tree_node (tree_node* p, octave_idx_type f)
00077   : parent (p), fcn_id (f), children (), time (0.0), calls (0)
00078 {}
00079 
00080 profile_data_accumulator::tree_node::~tree_node ()
00081 {
00082   for (child_map::iterator i = children.begin (); i != children.end (); ++i)
00083     delete i->second;
00084 }
00085 
00086 profile_data_accumulator::tree_node*
00087 profile_data_accumulator::tree_node::enter (octave_idx_type fcn)
00088 {
00089   tree_node* retval;
00090 
00091   child_map::iterator pos = children.find (fcn);
00092   if (pos == children.end ())
00093     {
00094       retval = new tree_node (this, fcn);
00095       children[fcn] = retval;
00096     }
00097   else
00098     retval = pos->second;
00099 
00100   ++retval->calls;
00101   return retval;
00102 }
00103 
00104 profile_data_accumulator::tree_node*
00105 profile_data_accumulator::tree_node::exit (octave_idx_type fcn)
00106 {
00107   assert (parent);
00108   assert (fcn_id == fcn);
00109 
00110   return parent;
00111 }
00112 
00113 void
00114 profile_data_accumulator::tree_node::build_flat (flat_profile& data) const
00115 {
00116   // If this is not the top-level node, update profile entry for this function.
00117   if (fcn_id != 0)
00118     {
00119       stats& entry = data[fcn_id - 1];
00120 
00121       entry.time += time;
00122       entry.calls += calls;
00123 
00124       assert (parent);
00125       if (parent->fcn_id != 0)
00126         {
00127           entry.parents.insert (parent->fcn_id);
00128           data[parent->fcn_id - 1].children.insert (fcn_id);
00129         }
00130 
00131       if (!entry.recursive)
00132         for (const tree_node* i = parent; i; i = i->parent)
00133           if (i->fcn_id == fcn_id)
00134             {
00135               entry.recursive = true;
00136               break;
00137             }
00138     }
00139 
00140   // Recurse on children.
00141   for (child_map::const_iterator i = children.begin ();
00142        i != children.end (); ++i)
00143     i->second->build_flat (data);
00144 }
00145 
00146 octave_value
00147 profile_data_accumulator::tree_node::get_hierarchical (double* total) const
00148 {
00149   /* Note that we don't generate the entry just for this node, but rather
00150      a struct-array with entries for all children.  This way, the top-node
00151      (for which we don't want a real entry) generates already the final
00152      hierarchical profile data.  */
00153 
00154   const octave_idx_type n = children.size ();
00155 
00156   Cell rv_indices (n, 1);
00157   Cell rv_times (n, 1);
00158   Cell rv_totals (n, 1);
00159   Cell rv_calls (n, 1);
00160   Cell rv_children (n, 1);
00161 
00162   octave_idx_type i = 0;
00163   for (child_map::const_iterator p = children.begin ();
00164        p != children.end (); ++p)
00165     {
00166       const tree_node& entry = *p->second;
00167       double child_total = entry.time;
00168 
00169       rv_indices(i) = octave_value (p->first);
00170       rv_times(i) = octave_value (entry.time);
00171       rv_calls(i) = octave_value (entry.calls);
00172       rv_children(i) = entry.get_hierarchical (&child_total);
00173       rv_totals(i) = octave_value (child_total);
00174 
00175       if (total)
00176         *total += child_total;
00177 
00178       ++i;
00179     }
00180   assert (i == n);
00181 
00182   octave_map retval;
00183 
00184   retval.assign ("Index", rv_indices);
00185   retval.assign ("SelfTime", rv_times);
00186   retval.assign ("TotalTime", rv_totals);
00187   retval.assign ("NumCalls", rv_calls);
00188   retval.assign ("Children", rv_children);
00189 
00190   return retval;
00191 }
00192 
00193 profile_data_accumulator::profile_data_accumulator ()
00194   : known_functions (), fcn_index (),
00195     enabled (false), call_tree (NULL), last_time (-1.0)
00196 {}
00197 
00198 profile_data_accumulator::~profile_data_accumulator ()
00199 {
00200   if (call_tree)
00201     delete call_tree;
00202 }
00203 
00204 void
00205 profile_data_accumulator::set_active (bool value)
00206 {
00207   if (value)
00208     {
00209       // Create a call-tree top-node if there isn't yet one.
00210       if (!call_tree)
00211         call_tree = new tree_node (NULL, 0);
00212 
00213       // Let the top-node be the active one.  This ensures we have a clean
00214       // fresh start collecting times.
00215       active_fcn = call_tree;
00216     }
00217   else
00218     {
00219       // Make sure we start with fresh timing if we're re-enabled later.
00220       last_time = -1.0;
00221     }
00222 
00223   enabled = value;
00224 }
00225 
00226 void
00227 profile_data_accumulator::enter_function (const std::string& fcn)
00228 {
00229   // The enter class will check and only call us if the profiler is active.
00230   assert (is_active ());
00231   assert (call_tree);
00232 
00233   // If there is already an active function, add to its time before
00234   // pushing the new one.
00235   if (active_fcn != call_tree)
00236     add_current_time ();
00237 
00238   // Map the function's name to its index.
00239   octave_idx_type fcn_idx;
00240   fcn_index_map::iterator pos = fcn_index.find (fcn);
00241   if (pos == fcn_index.end ())
00242     {
00243       known_functions.push_back (fcn);
00244       fcn_idx = known_functions.size ();
00245       fcn_index[fcn] = fcn_idx;
00246     }
00247   else
00248     fcn_idx = pos->second;
00249 
00250   active_fcn = active_fcn->enter (fcn_idx);
00251   last_time = query_time ();
00252 }
00253 
00254 void
00255 profile_data_accumulator::exit_function (const std::string& fcn)
00256 {
00257   assert (call_tree);
00258   assert (active_fcn != call_tree);
00259 
00260   // Usually, if we are disabled this function is not even called.  But the
00261   // call disabling the profiler is an exception.  So also check here
00262   // and only record the time if enabled.
00263   if (is_active ())
00264     add_current_time ();
00265 
00266   fcn_index_map::iterator pos = fcn_index.find (fcn);
00267   assert (pos != fcn_index.end ());
00268   active_fcn = active_fcn->exit (pos->second);
00269 
00270   // If this was an "inner call", we resume executing the parent function
00271   // up the stack.  So note the start-time for this!
00272   last_time = query_time ();
00273 }
00274 
00275 void
00276 profile_data_accumulator::reset (void)
00277 {
00278   if (is_active ())
00279     {
00280       error ("Can't reset active profiler.");
00281       return;
00282     }
00283 
00284   known_functions.clear ();
00285   fcn_index.clear ();
00286 
00287   if (call_tree)
00288     {
00289       delete call_tree;
00290       call_tree = NULL;
00291     }
00292 
00293   last_time = -1.0;
00294 }
00295 
00296 octave_value
00297 profile_data_accumulator::get_flat (void) const
00298 {
00299   octave_value retval;
00300 
00301   const octave_idx_type n = known_functions.size ();
00302 
00303   flat_profile flat (n);
00304 
00305   if (call_tree)
00306     {
00307       call_tree->build_flat (flat);
00308 
00309       Cell rv_names (n, 1);
00310       Cell rv_times (n, 1);
00311       Cell rv_calls (n, 1);
00312       Cell rv_recursive (n, 1);
00313       Cell rv_parents (n, 1);
00314       Cell rv_children (n, 1);
00315 
00316       for (octave_idx_type i = 0; i != n; ++i)
00317         {
00318           rv_names(i) = octave_value (known_functions[i]);
00319           rv_times(i) = octave_value (flat[i].time);
00320           rv_calls(i) = octave_value (flat[i].calls);
00321           rv_recursive(i) = octave_value (flat[i].recursive);
00322           rv_parents(i) = stats::function_set_value (flat[i].parents);
00323           rv_children(i) = stats::function_set_value (flat[i].children);
00324         }
00325 
00326       octave_map m;
00327 
00328       m.assign ("FunctionName", rv_names);
00329       m.assign ("TotalTime", rv_times);
00330       m.assign ("NumCalls", rv_calls);
00331       m.assign ("IsRecursive", rv_recursive);
00332       m.assign ("Parents", rv_parents);
00333       m.assign ("Children", rv_children);
00334 
00335       retval = m;
00336     }
00337   else
00338     {
00339       static const char *fn[] =
00340         {
00341           "FunctionName",
00342           "TotalTime",
00343           "NumCalls",
00344           "IsRecursive",
00345           "Parents",
00346           "Children",
00347           0
00348         };
00349 
00350       static octave_map m (dim_vector (0, 1), string_vector (fn));
00351 
00352       retval = m;
00353     }
00354 
00355   return retval;
00356 }
00357 
00358 octave_value
00359 profile_data_accumulator::get_hierarchical (void) const
00360 {
00361   octave_value retval;
00362 
00363   if (call_tree)
00364     retval = call_tree->get_hierarchical ();
00365   else
00366     {
00367       static const char *fn[] =
00368         {
00369           "Index",
00370           "SelfTime",
00371           "NumCalls",
00372           "Children",
00373           0
00374         };
00375 
00376       static octave_map m (dim_vector (0, 1), string_vector (fn));
00377 
00378       retval = m;
00379     }
00380 
00381   return retval;
00382 }
00383 
00384 double
00385 profile_data_accumulator::query_time (void) const
00386 {
00387   octave_time now;
00388 
00389   // FIXME -- is this volatile declaration really needed?
00390   // See bug #34210 for additional details.
00391   volatile double dnow = now.double_value ();
00392 
00393   return dnow;
00394 }
00395 
00396 void
00397 profile_data_accumulator::add_current_time (void)
00398 {
00399   const double t = query_time ();
00400   assert (last_time >= 0.0 && last_time <= t);
00401 
00402   assert (call_tree && active_fcn != call_tree);
00403   active_fcn->add_time (t - last_time);
00404 }
00405 
00406 profile_data_accumulator profiler;
00407 
00408 // Enable or disable the profiler data collection.
00409 DEFUN (__profiler_enable__, args, ,
00410   "-*- texinfo -*-\n\
00411 @deftypefn {Function File} __profiler_enable ()\n\
00412 Undocumented internal function.\n\
00413 @end deftypefn")
00414 {
00415   octave_value_list retval;
00416 
00417   const int nargin = args.length ();
00418   if (nargin > 0)
00419     {
00420       if (nargin > 1)
00421         {
00422           print_usage ();
00423           return retval;
00424         }
00425 
00426       profiler.set_active (args(0).bool_value ());
00427     }
00428 
00429   retval(0) = profiler.is_active ();
00430 
00431   return retval;
00432 }
00433 
00434 // Clear all collected profiling data.
00435 DEFUN (__profiler_reset__, args, ,
00436   "-*- texinfo -*-\n\
00437 @deftypefn {Function File} __profiler_reset ()\n\
00438 Undocumented internal function.\n\
00439 @end deftypefn")
00440 {
00441   octave_value_list retval;
00442   const int nargin = args.length ();
00443 
00444   if (nargin > 0)
00445     warning ("profiler_reset: ignoring extra arguments");
00446 
00447   profiler.reset ();
00448 
00449   return retval;
00450 }
00451 
00452 // Query the timings collected by the profiler.
00453 DEFUN (__profiler_data__, args, nargout,
00454   "-*- texinfo -*-\n\
00455 @deftypefn {Function File} __profiler_data ()\n\
00456 Undocumented internal function.\n\
00457 @end deftypefn")
00458 {
00459   octave_value_list retval;
00460   const int nargin = args.length ();
00461 
00462   if (nargin > 0)
00463     warning ("profiler_data: ignoring extra arguments");
00464 
00465   retval(0) = profiler.get_flat ();
00466   if (nargout > 1)
00467     retval(1) = profiler.get_hierarchical ();
00468 
00469   return retval;
00470 }
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Defines