Mercurial > octave
changeset 23751:0ebc4f1a5714
move profiler.h and profiler.cc to libinterp/parse-tree directory
* libinterp/parse-tree/profiler.cc, libinterp/parse-tree/profiler.h:
Move here from libinterp/corefcn directory.
* libinterp/corefcn/module.mk, libinterp/parse-tree/module.mk:
Update.
author | John W. Eaton <jwe@octave.org> |
---|---|
date | Sat, 08 Jul 2017 10:10:37 -0400 |
parents | ea879bc55272 |
children | 6be1bf9455e3 |
files | libinterp/corefcn/module.mk libinterp/corefcn/profiler.cc libinterp/corefcn/profiler.h libinterp/parse-tree/module.mk libinterp/parse-tree/profiler.cc libinterp/parse-tree/profiler.h |
diffstat | 6 files changed, 647 insertions(+), 647 deletions(-) [+] |
line wrap: on
line diff
--- a/libinterp/corefcn/module.mk Fri Jul 07 18:43:32 2017 -0400 +++ b/libinterp/corefcn/module.mk Sat Jul 08 10:10:37 2017 -0400 @@ -75,7 +75,6 @@ %reldir%/pager.h \ %reldir%/pr-output.h \ %reldir%/procstream.h \ - %reldir%/profiler.h \ %reldir%/sighandlers.h \ %reldir%/sparse-xdiv.h \ %reldir%/sparse-xpow.h \ @@ -209,7 +208,6 @@ %reldir%/pinv.cc \ %reldir%/pr-output.cc \ %reldir%/procstream.cc \ - %reldir%/profiler.cc \ %reldir%/psi.cc \ %reldir%/quad.cc \ %reldir%/quadcc.cc \
--- a/libinterp/corefcn/profiler.cc Fri Jul 07 18:43:32 2017 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,430 +0,0 @@ -/* - -Copyright (C) 2014-2017 Julien Bect -Copyright (C) 2012-2016 Daniel Kraft - -This file is part of Octave. - -Octave is free software; you can redistribute it and/or modify it -under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 3 of the License, or -(at your option) any later version. - -Octave is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Octave; see the file COPYING. If not, see -<http://www.gnu.org/licenses/>. - -*/ - -#if defined (HAVE_CONFIG_H) -# include "config.h" -#endif - -#include <iostream> - -#include "defun.h" -#include "oct-time.h" -#include "ov-struct.h" -#include "pager.h" -#include "profiler.h" - -profile_data_accumulator::stats::stats () - : time (0.0), calls (0), recursive (false), - parents (), children () -{ } - -octave_value -profile_data_accumulator::stats::function_set_value (const function_set& list) -{ - const octave_idx_type n = list.size (); - - RowVector retval (n); - octave_idx_type i = 0; - for (const auto& nm : list) - retval(i++) = nm; - - assert (i == n); - - return retval; -} - -profile_data_accumulator::tree_node::tree_node (tree_node *p, octave_idx_type f) - : parent (p), fcn_id (f), children (), time (0.0), calls (0) -{ } - -profile_data_accumulator::tree_node::~tree_node () -{ - for (auto& idx_tnode : children) - delete idx_tnode.second; -} - -profile_data_accumulator::tree_node* -profile_data_accumulator::tree_node::enter (octave_idx_type fcn) -{ - tree_node *retval; - - child_map::iterator pos = children.find (fcn); - if (pos == children.end ()) - { - retval = new tree_node (this, fcn); - children[fcn] = retval; - } - else - retval = pos->second; - - ++retval->calls; - return retval; -} - -profile_data_accumulator::tree_node* -profile_data_accumulator::tree_node::exit (octave_idx_type /* fcn */) -{ - // FIXME: These assert statements don't make sense if profile() is called - // from within a function hierarchy to begin with. See bug #39587. - // assert (parent); - // assert (fcn_id == fcn); - - return parent; -} - -void -profile_data_accumulator::tree_node::build_flat (flat_profile& data) const -{ - // If this is not the top-level node, update profile entry for this function. - if (fcn_id != 0) - { - stats& entry = data[fcn_id - 1]; - - entry.time += time; - entry.calls += calls; - - assert (parent); - if (parent->fcn_id != 0) - { - entry.parents.insert (parent->fcn_id); - data[parent->fcn_id - 1].children.insert (fcn_id); - } - - if (! entry.recursive) - for (const tree_node *i = parent; i; i = i->parent) - if (i->fcn_id == fcn_id) - { - entry.recursive = true; - break; - } - } - - // Recurse on children. - for (const auto& idx_tnode : children) - idx_tnode.second->build_flat (data); -} - -octave_value -profile_data_accumulator::tree_node::get_hierarchical (double *total) const -{ - // Note that we don't generate the entry just for this node, but - // rather a struct-array with entries for all children. This way, the - // top-node (for which we don't want a real entry) generates already - // the final hierarchical profile data. - - const octave_idx_type n = children.size (); - - Cell rv_indices (n, 1); - Cell rv_times (n, 1); - Cell rv_totals (n, 1); - Cell rv_calls (n, 1); - Cell rv_children (n, 1); - - octave_idx_type i = 0; - for (const auto& idx_tnode : children) - { - const tree_node& entry = *idx_tnode.second; - double child_total = entry.time; - - rv_indices(i) = octave_value (idx_tnode.first); - rv_times(i) = octave_value (entry.time); - rv_calls(i) = octave_value (entry.calls); - rv_children(i) = entry.get_hierarchical (&child_total); - rv_totals(i) = octave_value (child_total); - - if (total) - *total += child_total; - - ++i; - } - assert (i == n); - - octave_map retval; - - retval.assign ("Index", rv_indices); - retval.assign ("SelfTime", rv_times); - retval.assign ("TotalTime", rv_totals); - retval.assign ("NumCalls", rv_calls); - retval.assign ("Children", rv_children); - - return retval; -} - -profile_data_accumulator::profile_data_accumulator () - : known_functions (), fcn_index (), - enabled (false), call_tree (new tree_node (0, 0)), - active_fcn (0), last_time (-1.0) -{ } - -profile_data_accumulator::~profile_data_accumulator () -{ - delete call_tree; -} - -void -profile_data_accumulator::set_active (bool value) -{ - enabled = value; -} - -void -profile_data_accumulator::enter_function (const std::string& fcn) -{ - // The enter class will check and only call us if the profiler is active. - assert (is_active ()); - assert (call_tree); - - // If there is already an active function, add to its time before - // pushing the new one. - if (active_fcn && active_fcn != call_tree) - add_current_time (); - - // Map the function's name to its index. - octave_idx_type fcn_idx; - fcn_index_map::iterator pos = fcn_index.find (fcn); - if (pos == fcn_index.end ()) - { - known_functions.push_back (fcn); - fcn_idx = known_functions.size (); - fcn_index[fcn] = fcn_idx; - } - else - fcn_idx = pos->second; - - if (! active_fcn) - active_fcn = call_tree; - - active_fcn = active_fcn->enter (fcn_idx); - - last_time = query_time (); - -} - -void -profile_data_accumulator::exit_function (const std::string& fcn) -{ - if (active_fcn) - { - assert (call_tree); - // FIXME: This assert statements doesn't make sense if profile() is called - // from within a function hierarchy to begin with. See bug #39587. - //assert (active_fcn != call_tree); - - // Usually, if we are disabled this function is not even called. But the - // call disabling the profiler is an exception. So also check here - // and only record the time if enabled. - if (is_active ()) - add_current_time (); - - fcn_index_map::iterator pos = fcn_index.find (fcn); - // FIXME: This assert statements doesn't make sense if profile() is called - // from within a function hierarchy to begin with. See bug #39587. - //assert (pos != fcn_index.end ()); - active_fcn = active_fcn->exit (pos->second); - - // If this was an "inner call", we resume executing the parent function - // up the stack. So note the start-time for this! - last_time = query_time (); - } -} - -void -profile_data_accumulator::reset (void) -{ - if (is_active ()) - error ("Can't reset active profiler."); - - known_functions.clear (); - fcn_index.clear (); - - if (call_tree) - { - delete call_tree; - call_tree = new tree_node (0, 0); - active_fcn = 0; - } - - last_time = -1.0; -} - -octave_value -profile_data_accumulator::get_flat (void) const -{ - octave_value retval; - - const octave_idx_type n = known_functions.size (); - - flat_profile flat (n); - - if (call_tree) - { - call_tree->build_flat (flat); - - Cell rv_names (n, 1); - Cell rv_times (n, 1); - Cell rv_calls (n, 1); - Cell rv_recursive (n, 1); - Cell rv_parents (n, 1); - Cell rv_children (n, 1); - - for (octave_idx_type i = 0; i != n; ++i) - { - rv_names(i) = octave_value (known_functions[i]); - rv_times(i) = octave_value (flat[i].time); - rv_calls(i) = octave_value (flat[i].calls); - rv_recursive(i) = octave_value (flat[i].recursive); - rv_parents(i) = stats::function_set_value (flat[i].parents); - rv_children(i) = stats::function_set_value (flat[i].children); - } - - octave_map m; - - m.assign ("FunctionName", rv_names); - m.assign ("TotalTime", rv_times); - m.assign ("NumCalls", rv_calls); - m.assign ("IsRecursive", rv_recursive); - m.assign ("Parents", rv_parents); - m.assign ("Children", rv_children); - - retval = m; - } - else - { - static const char *fn[] = - { - "FunctionName", - "TotalTime", - "NumCalls", - "IsRecursive", - "Parents", - "Children", - 0 - }; - - static octave_map m (dim_vector (0, 1), string_vector (fn)); - - retval = m; - } - - return retval; -} - -octave_value -profile_data_accumulator::get_hierarchical (void) const -{ - octave_value retval; - - if (call_tree) - retval = call_tree->get_hierarchical (); - else - { - static const char *fn[] = - { - "Index", - "SelfTime", - "NumCalls", - "Children", - 0 - }; - - static octave_map m (dim_vector (0, 1), string_vector (fn)); - - retval = m; - } - - return retval; -} - -double -profile_data_accumulator::query_time (void) const -{ - octave::sys::time now; - - // FIXME: is this volatile declaration really needed? - // See bug #34210 for additional details. - volatile double dnow = now.double_value (); - - return dnow; -} - -void -profile_data_accumulator::add_current_time (void) -{ - if (active_fcn) - { - const double t = query_time (); - - active_fcn->add_time (t - last_time); - } -} - -profile_data_accumulator profiler; - -// Enable or disable the profiler data collection. -DEFUN (__profiler_enable__, args, , - doc: /* -*- texinfo -*- -@deftypefn {} {} __profiler_enable__ () -Undocumented internal function. -@end deftypefn */) -{ - int nargin = args.length (); - - if (nargin > 1) - print_usage (); - - if (nargin > 0) - profiler.set_active (args(0).bool_value ()); - - return ovl (profiler.is_active ()); -} - -// Clear all collected profiling data. -DEFUN (__profiler_reset__, args, , - doc: /* -*- texinfo -*- -@deftypefn {} {} __profiler_reset__ () -Undocumented internal function. -@end deftypefn */) -{ - if (args.length () > 0) - warning ("profiler_reset: ignoring extra arguments"); - - profiler.reset (); - - return ovl (); -} - -// Query the timings collected by the profiler. -DEFUN (__profiler_data__, args, nargout, - doc: /* -*- texinfo -*- -@deftypefn {} {} __profiler_data__ () -Undocumented internal function. -@end deftypefn */) -{ - if (args.length () > 0) - warning ("profiler_data: ignoring extra arguments"); - - if (nargout > 1) - return ovl (profiler.get_flat (), profiler.get_hierarchical ()); - else - return ovl (profiler.get_flat ()); -}
--- a/libinterp/corefcn/profiler.h Fri Jul 07 18:43:32 2017 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,215 +0,0 @@ -/* - -Copyright (C) 2014-2017 Julien Bect -Copyright (C) 2012-2016 Daniel Kraft - -This file is part of Octave. - -Octave is free software; you can redistribute it and/or modify it -under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 3 of the License, or -(at your option) any later version. - -Octave is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Octave; see the file COPYING. If not, see -<http://www.gnu.org/licenses/>. - -*/ - -#if ! defined (octave_profiler_h) -#define octave_profiler_h 1 - -#include "octave-config.h" - -#include <cstddef> -#include <map> -#include <set> -#include <string> -#include <vector> - -class octave_value; - -class -OCTINTERP_API -profile_data_accumulator -{ -public: - - // This is a utility class that can be used to call the enter/exit - // functions in a manner protected from stack unwinding. - template <typename T> class enter - { - private: - - profile_data_accumulator& acc; - std::string fcn; - bool is_active; - - public: - - enter (profile_data_accumulator& a, const T& t) : acc (a) - { - // A profiling block cannot be active if the profiler is not - is_active = acc.is_active (); - - if (is_active) - { - fcn = t.profiler_name (); - - // NOTE: The test f != "" must be kept to prevent a blank line showing - // up in profiler statistics. See bug #39524. The root cause is that - // the function name is not set for the recurring readline hook function. - if (fcn == "") - is_active = false; // Inactive profiling block - else - acc.enter_function (fcn); - } - } - - // No copying! - - enter (const enter&) = delete; - - enter& operator = (const enter&) = delete; - - ~enter () - { - if (is_active) - acc.exit_function (fcn); - } - }; - - profile_data_accumulator (void); - - // No copying! - - profile_data_accumulator (const profile_data_accumulator&) = delete; - - profile_data_accumulator& - operator = (const profile_data_accumulator&) = delete; - - virtual ~profile_data_accumulator (); - - bool is_active (void) const { return enabled; } - void set_active (bool); - - void reset (void); - - octave_value get_flat (void) const; - octave_value get_hierarchical (void) const; - -private: - - // One entry in the flat profile (i.e., a collection of data for a single - // function). This is filled in when building the flat profile from the - // hierarchical call tree. - struct stats - { - stats (); - - double time; - unsigned calls; - - bool recursive; - - typedef std::set<octave_idx_type> function_set; - function_set parents; - function_set children; - - // Convert a function_set list to an Octave array of indices. - static octave_value function_set_value (const function_set&); - }; - - typedef std::vector<stats> flat_profile; - - // Store data for one node in the call-tree of the hierarchical profiler - // data we collect. - class tree_node - { - public: - - tree_node (tree_node*, octave_idx_type); - virtual ~tree_node (); - - // No copying! - - tree_node (const tree_node&) = delete; - - tree_node& operator = (const tree_node&) = delete; - - void add_time (double dt) { time += dt; } - - // Enter a child function. It is created in the list of children if it - // wasn't already there. The now-active child node is returned. - tree_node *enter (octave_idx_type); - - // Exit function. As a sanity-check, it is verified that the currently - // active function actually is the one handed in here. Returned is the - // then-active node, which is our parent. - tree_node *exit (octave_idx_type); - - void build_flat (flat_profile&) const; - - // Get the hierarchical profile for this node and its children. If total - // is set, accumulate total time of the subtree in that variable as - // additional return value. - octave_value get_hierarchical (double *total = nullptr) const; - - private: - - tree_node *parent; - octave_idx_type fcn_id; - - typedef std::map<octave_idx_type, tree_node*> child_map; - child_map children; - - // This is only time spent *directly* on this level, excluding children! - double time; - - unsigned calls; - }; - - // Each function we see in the profiler is given a unique index (which - // simply counts starting from 1). We thus have to map profiler-names to - // those indices. For all other stuff, we identify functions by their index. - - typedef std::vector<std::string> function_set; - typedef std::map<std::string, octave_idx_type> fcn_index_map; - - function_set known_functions; - fcn_index_map fcn_index; - - bool enabled; - - tree_node *call_tree; - tree_node *active_fcn; - - // Store last timestamp we had, when the currently active function was called. - double last_time; - - // These are private as only the unwind-protecting inner class enter - // should be allowed to call them. - void enter_function (const std::string&); - void exit_function (const std::string&); - - // Query a timestamp, used for timing calls (obviously). - // This is not static because in the future, maybe we want a flag - // in the profiler or something to choose between cputime, wall-time, - // user-time, system-time, ... - double query_time () const; - - // Add the time elapsed since last_time to the function we're currently in. - // This is called from two different positions, thus it is useful to have - // it as a seperate function. - void add_current_time (void); -}; - -// The instance used. -extern OCTINTERP_API profile_data_accumulator profiler; - -#endif
--- a/libinterp/parse-tree/module.mk Fri Jul 07 18:43:32 2017 -0400 +++ b/libinterp/parse-tree/module.mk Sat Jul 08 10:10:37 2017 -0400 @@ -6,6 +6,7 @@ %reldir%/jit-util.h \ %reldir%/lex.h \ %reldir%/parse.h \ + %reldir%/profiler.h \ %reldir%/pt-all.h \ %reldir%/pt-arg-list.h \ %reldir%/pt-array-list.h \ @@ -55,6 +56,7 @@ %reldir%/oct-gperf.h \ %reldir%/oct-parse.h \ %reldir%/oct-parse.yy \ + %reldir%/profiler.cc \ %reldir%/pt-arg-list.cc \ %reldir%/pt-array-list.cc \ %reldir%/pt-assign.cc \
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libinterp/parse-tree/profiler.cc Sat Jul 08 10:10:37 2017 -0400 @@ -0,0 +1,430 @@ +/* + +Copyright (C) 2014-2017 Julien Bect +Copyright (C) 2012-2016 Daniel Kraft + +This file is part of Octave. + +Octave is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3 of the License, or +(at your option) any later version. + +Octave is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Octave; see the file COPYING. If not, see +<http://www.gnu.org/licenses/>. + +*/ + +#if defined (HAVE_CONFIG_H) +# include "config.h" +#endif + +#include <iostream> + +#include "defun.h" +#include "oct-time.h" +#include "ov-struct.h" +#include "pager.h" +#include "profiler.h" + +profile_data_accumulator::stats::stats () + : time (0.0), calls (0), recursive (false), + parents (), children () +{ } + +octave_value +profile_data_accumulator::stats::function_set_value (const function_set& list) +{ + const octave_idx_type n = list.size (); + + RowVector retval (n); + octave_idx_type i = 0; + for (const auto& nm : list) + retval(i++) = nm; + + assert (i == n); + + return retval; +} + +profile_data_accumulator::tree_node::tree_node (tree_node *p, octave_idx_type f) + : parent (p), fcn_id (f), children (), time (0.0), calls (0) +{ } + +profile_data_accumulator::tree_node::~tree_node () +{ + for (auto& idx_tnode : children) + delete idx_tnode.second; +} + +profile_data_accumulator::tree_node* +profile_data_accumulator::tree_node::enter (octave_idx_type fcn) +{ + tree_node *retval; + + child_map::iterator pos = children.find (fcn); + if (pos == children.end ()) + { + retval = new tree_node (this, fcn); + children[fcn] = retval; + } + else + retval = pos->second; + + ++retval->calls; + return retval; +} + +profile_data_accumulator::tree_node* +profile_data_accumulator::tree_node::exit (octave_idx_type /* fcn */) +{ + // FIXME: These assert statements don't make sense if profile() is called + // from within a function hierarchy to begin with. See bug #39587. + // assert (parent); + // assert (fcn_id == fcn); + + return parent; +} + +void +profile_data_accumulator::tree_node::build_flat (flat_profile& data) const +{ + // If this is not the top-level node, update profile entry for this function. + if (fcn_id != 0) + { + stats& entry = data[fcn_id - 1]; + + entry.time += time; + entry.calls += calls; + + assert (parent); + if (parent->fcn_id != 0) + { + entry.parents.insert (parent->fcn_id); + data[parent->fcn_id - 1].children.insert (fcn_id); + } + + if (! entry.recursive) + for (const tree_node *i = parent; i; i = i->parent) + if (i->fcn_id == fcn_id) + { + entry.recursive = true; + break; + } + } + + // Recurse on children. + for (const auto& idx_tnode : children) + idx_tnode.second->build_flat (data); +} + +octave_value +profile_data_accumulator::tree_node::get_hierarchical (double *total) const +{ + // Note that we don't generate the entry just for this node, but + // rather a struct-array with entries for all children. This way, the + // top-node (for which we don't want a real entry) generates already + // the final hierarchical profile data. + + const octave_idx_type n = children.size (); + + Cell rv_indices (n, 1); + Cell rv_times (n, 1); + Cell rv_totals (n, 1); + Cell rv_calls (n, 1); + Cell rv_children (n, 1); + + octave_idx_type i = 0; + for (const auto& idx_tnode : children) + { + const tree_node& entry = *idx_tnode.second; + double child_total = entry.time; + + rv_indices(i) = octave_value (idx_tnode.first); + rv_times(i) = octave_value (entry.time); + rv_calls(i) = octave_value (entry.calls); + rv_children(i) = entry.get_hierarchical (&child_total); + rv_totals(i) = octave_value (child_total); + + if (total) + *total += child_total; + + ++i; + } + assert (i == n); + + octave_map retval; + + retval.assign ("Index", rv_indices); + retval.assign ("SelfTime", rv_times); + retval.assign ("TotalTime", rv_totals); + retval.assign ("NumCalls", rv_calls); + retval.assign ("Children", rv_children); + + return retval; +} + +profile_data_accumulator::profile_data_accumulator () + : known_functions (), fcn_index (), + enabled (false), call_tree (new tree_node (0, 0)), + active_fcn (0), last_time (-1.0) +{ } + +profile_data_accumulator::~profile_data_accumulator () +{ + delete call_tree; +} + +void +profile_data_accumulator::set_active (bool value) +{ + enabled = value; +} + +void +profile_data_accumulator::enter_function (const std::string& fcn) +{ + // The enter class will check and only call us if the profiler is active. + assert (is_active ()); + assert (call_tree); + + // If there is already an active function, add to its time before + // pushing the new one. + if (active_fcn && active_fcn != call_tree) + add_current_time (); + + // Map the function's name to its index. + octave_idx_type fcn_idx; + fcn_index_map::iterator pos = fcn_index.find (fcn); + if (pos == fcn_index.end ()) + { + known_functions.push_back (fcn); + fcn_idx = known_functions.size (); + fcn_index[fcn] = fcn_idx; + } + else + fcn_idx = pos->second; + + if (! active_fcn) + active_fcn = call_tree; + + active_fcn = active_fcn->enter (fcn_idx); + + last_time = query_time (); + +} + +void +profile_data_accumulator::exit_function (const std::string& fcn) +{ + if (active_fcn) + { + assert (call_tree); + // FIXME: This assert statements doesn't make sense if profile() is called + // from within a function hierarchy to begin with. See bug #39587. + //assert (active_fcn != call_tree); + + // Usually, if we are disabled this function is not even called. But the + // call disabling the profiler is an exception. So also check here + // and only record the time if enabled. + if (is_active ()) + add_current_time (); + + fcn_index_map::iterator pos = fcn_index.find (fcn); + // FIXME: This assert statements doesn't make sense if profile() is called + // from within a function hierarchy to begin with. See bug #39587. + //assert (pos != fcn_index.end ()); + active_fcn = active_fcn->exit (pos->second); + + // If this was an "inner call", we resume executing the parent function + // up the stack. So note the start-time for this! + last_time = query_time (); + } +} + +void +profile_data_accumulator::reset (void) +{ + if (is_active ()) + error ("Can't reset active profiler."); + + known_functions.clear (); + fcn_index.clear (); + + if (call_tree) + { + delete call_tree; + call_tree = new tree_node (0, 0); + active_fcn = 0; + } + + last_time = -1.0; +} + +octave_value +profile_data_accumulator::get_flat (void) const +{ + octave_value retval; + + const octave_idx_type n = known_functions.size (); + + flat_profile flat (n); + + if (call_tree) + { + call_tree->build_flat (flat); + + Cell rv_names (n, 1); + Cell rv_times (n, 1); + Cell rv_calls (n, 1); + Cell rv_recursive (n, 1); + Cell rv_parents (n, 1); + Cell rv_children (n, 1); + + for (octave_idx_type i = 0; i != n; ++i) + { + rv_names(i) = octave_value (known_functions[i]); + rv_times(i) = octave_value (flat[i].time); + rv_calls(i) = octave_value (flat[i].calls); + rv_recursive(i) = octave_value (flat[i].recursive); + rv_parents(i) = stats::function_set_value (flat[i].parents); + rv_children(i) = stats::function_set_value (flat[i].children); + } + + octave_map m; + + m.assign ("FunctionName", rv_names); + m.assign ("TotalTime", rv_times); + m.assign ("NumCalls", rv_calls); + m.assign ("IsRecursive", rv_recursive); + m.assign ("Parents", rv_parents); + m.assign ("Children", rv_children); + + retval = m; + } + else + { + static const char *fn[] = + { + "FunctionName", + "TotalTime", + "NumCalls", + "IsRecursive", + "Parents", + "Children", + 0 + }; + + static octave_map m (dim_vector (0, 1), string_vector (fn)); + + retval = m; + } + + return retval; +} + +octave_value +profile_data_accumulator::get_hierarchical (void) const +{ + octave_value retval; + + if (call_tree) + retval = call_tree->get_hierarchical (); + else + { + static const char *fn[] = + { + "Index", + "SelfTime", + "NumCalls", + "Children", + 0 + }; + + static octave_map m (dim_vector (0, 1), string_vector (fn)); + + retval = m; + } + + return retval; +} + +double +profile_data_accumulator::query_time (void) const +{ + octave::sys::time now; + + // FIXME: is this volatile declaration really needed? + // See bug #34210 for additional details. + volatile double dnow = now.double_value (); + + return dnow; +} + +void +profile_data_accumulator::add_current_time (void) +{ + if (active_fcn) + { + const double t = query_time (); + + active_fcn->add_time (t - last_time); + } +} + +profile_data_accumulator profiler; + +// Enable or disable the profiler data collection. +DEFUN (__profiler_enable__, args, , + doc: /* -*- texinfo -*- +@deftypefn {} {} __profiler_enable__ () +Undocumented internal function. +@end deftypefn */) +{ + int nargin = args.length (); + + if (nargin > 1) + print_usage (); + + if (nargin > 0) + profiler.set_active (args(0).bool_value ()); + + return ovl (profiler.is_active ()); +} + +// Clear all collected profiling data. +DEFUN (__profiler_reset__, args, , + doc: /* -*- texinfo -*- +@deftypefn {} {} __profiler_reset__ () +Undocumented internal function. +@end deftypefn */) +{ + if (args.length () > 0) + warning ("profiler_reset: ignoring extra arguments"); + + profiler.reset (); + + return ovl (); +} + +// Query the timings collected by the profiler. +DEFUN (__profiler_data__, args, nargout, + doc: /* -*- texinfo -*- +@deftypefn {} {} __profiler_data__ () +Undocumented internal function. +@end deftypefn */) +{ + if (args.length () > 0) + warning ("profiler_data: ignoring extra arguments"); + + if (nargout > 1) + return ovl (profiler.get_flat (), profiler.get_hierarchical ()); + else + return ovl (profiler.get_flat ()); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libinterp/parse-tree/profiler.h Sat Jul 08 10:10:37 2017 -0400 @@ -0,0 +1,215 @@ +/* + +Copyright (C) 2014-2017 Julien Bect +Copyright (C) 2012-2016 Daniel Kraft + +This file is part of Octave. + +Octave is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3 of the License, or +(at your option) any later version. + +Octave is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Octave; see the file COPYING. If not, see +<http://www.gnu.org/licenses/>. + +*/ + +#if ! defined (octave_profiler_h) +#define octave_profiler_h 1 + +#include "octave-config.h" + +#include <cstddef> +#include <map> +#include <set> +#include <string> +#include <vector> + +class octave_value; + +class +OCTINTERP_API +profile_data_accumulator +{ +public: + + // This is a utility class that can be used to call the enter/exit + // functions in a manner protected from stack unwinding. + template <typename T> class enter + { + private: + + profile_data_accumulator& acc; + std::string fcn; + bool is_active; + + public: + + enter (profile_data_accumulator& a, const T& t) : acc (a) + { + // A profiling block cannot be active if the profiler is not + is_active = acc.is_active (); + + if (is_active) + { + fcn = t.profiler_name (); + + // NOTE: The test f != "" must be kept to prevent a blank line showing + // up in profiler statistics. See bug #39524. The root cause is that + // the function name is not set for the recurring readline hook function. + if (fcn == "") + is_active = false; // Inactive profiling block + else + acc.enter_function (fcn); + } + } + + // No copying! + + enter (const enter&) = delete; + + enter& operator = (const enter&) = delete; + + ~enter () + { + if (is_active) + acc.exit_function (fcn); + } + }; + + profile_data_accumulator (void); + + // No copying! + + profile_data_accumulator (const profile_data_accumulator&) = delete; + + profile_data_accumulator& + operator = (const profile_data_accumulator&) = delete; + + virtual ~profile_data_accumulator (); + + bool is_active (void) const { return enabled; } + void set_active (bool); + + void reset (void); + + octave_value get_flat (void) const; + octave_value get_hierarchical (void) const; + +private: + + // One entry in the flat profile (i.e., a collection of data for a single + // function). This is filled in when building the flat profile from the + // hierarchical call tree. + struct stats + { + stats (); + + double time; + unsigned calls; + + bool recursive; + + typedef std::set<octave_idx_type> function_set; + function_set parents; + function_set children; + + // Convert a function_set list to an Octave array of indices. + static octave_value function_set_value (const function_set&); + }; + + typedef std::vector<stats> flat_profile; + + // Store data for one node in the call-tree of the hierarchical profiler + // data we collect. + class tree_node + { + public: + + tree_node (tree_node*, octave_idx_type); + virtual ~tree_node (); + + // No copying! + + tree_node (const tree_node&) = delete; + + tree_node& operator = (const tree_node&) = delete; + + void add_time (double dt) { time += dt; } + + // Enter a child function. It is created in the list of children if it + // wasn't already there. The now-active child node is returned. + tree_node *enter (octave_idx_type); + + // Exit function. As a sanity-check, it is verified that the currently + // active function actually is the one handed in here. Returned is the + // then-active node, which is our parent. + tree_node *exit (octave_idx_type); + + void build_flat (flat_profile&) const; + + // Get the hierarchical profile for this node and its children. If total + // is set, accumulate total time of the subtree in that variable as + // additional return value. + octave_value get_hierarchical (double *total = nullptr) const; + + private: + + tree_node *parent; + octave_idx_type fcn_id; + + typedef std::map<octave_idx_type, tree_node*> child_map; + child_map children; + + // This is only time spent *directly* on this level, excluding children! + double time; + + unsigned calls; + }; + + // Each function we see in the profiler is given a unique index (which + // simply counts starting from 1). We thus have to map profiler-names to + // those indices. For all other stuff, we identify functions by their index. + + typedef std::vector<std::string> function_set; + typedef std::map<std::string, octave_idx_type> fcn_index_map; + + function_set known_functions; + fcn_index_map fcn_index; + + bool enabled; + + tree_node *call_tree; + tree_node *active_fcn; + + // Store last timestamp we had, when the currently active function was called. + double last_time; + + // These are private as only the unwind-protecting inner class enter + // should be allowed to call them. + void enter_function (const std::string&); + void exit_function (const std::string&); + + // Query a timestamp, used for timing calls (obviously). + // This is not static because in the future, maybe we want a flag + // in the profiler or something to choose between cputime, wall-time, + // user-time, system-time, ... + double query_time () const; + + // Add the time elapsed since last_time to the function we're currently in. + // This is called from two different positions, thus it is useful to have + // it as a seperate function. + void add_current_time (void); +}; + +// The instance used. +extern OCTINTERP_API profile_data_accumulator profiler; + +#endif