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