1 //////////////////////////////////////////////////////////////////////// 2 // 3 // Copyright (C) 2012-2021 The Octave Project Developers 4 // 5 // See the file COPYRIGHT.md in the top-level directory of this 6 // distribution or <https://octave.org/copyright/>. 7 // 8 // This file is part of Octave. 9 // 10 // Octave is free software: you can redistribute it and/or modify it 11 // under the terms of the GNU General Public License as published by 12 // the Free Software Foundation, either version 3 of the License, or 13 // (at your option) any later version. 14 // 15 // Octave is distributed in the hope that it will be useful, but 16 // WITHOUT ANY WARRANTY; without even the implied warranty of 17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 // GNU General Public License for more details. 19 // 20 // You should have received a copy of the GNU General Public License 21 // along with Octave; see the file COPYING. If not, see 22 // <https://www.gnu.org/licenses/>. 23 // 24 //////////////////////////////////////////////////////////////////////// 25 26 #if ! defined (octave_profiler_h) 27 #define octave_profiler_h 1 28 29 #include "octave-config.h" 30 31 #include <cstddef> 32 #include <map> 33 #include <set> 34 #include <string> 35 #include <vector> 36 37 class octave_value; 38 39 namespace octave 40 { 41 class 42 OCTINTERP_API 43 profiler 44 { 45 public: 46 47 // This is a utility class that can be used to call the enter/exit 48 // functions in a manner protected from stack unwinding. 49 template <typename T> class enter 50 { 51 private: 52 53 profiler& m_profiler; 54 std::string m_fcn; 55 bool m_enabled; 56 57 public: 58 enter(profiler & p,const T & t)59 enter (profiler& p, const T& t) : m_profiler (p) 60 { 61 // A profiling block cannot be active if the profiler is not 62 m_enabled = m_profiler.enabled (); 63 64 if (m_enabled) 65 { 66 m_fcn = t.profiler_name (); 67 68 // NOTE: The test f != "" must be kept to prevent a blank 69 // line showing up in profiler statistics. See bug 70 // #39524. The root cause is that the function name is 71 // not set for the recurring readline hook function. 72 if (m_fcn == "") 73 m_enabled = false; // Inactive profiling block 74 else 75 m_profiler.enter_function (m_fcn); 76 } 77 } 78 79 // No copying! 80 81 enter (const enter&) = delete; 82 83 enter& operator = (const enter&) = delete; 84 ~enter(void)85 ~enter (void) 86 { 87 if (m_enabled) 88 m_profiler.exit_function (m_fcn); 89 } 90 }; 91 92 profiler (void); 93 94 // No copying! 95 96 profiler (const profiler&) = delete; 97 98 profiler& operator = (const profiler&) = delete; 99 100 virtual ~profiler (void); 101 enabled(void)102 bool enabled (void) const { return m_enabled; } 103 void set_active (bool); 104 105 void reset (void); 106 107 octave_value get_flat (void) const; 108 octave_value get_hierarchical (void) const; 109 110 private: 111 112 // One entry in the flat profile (i.e., a collection of data for a single 113 // function). This is filled in when building the flat profile from the 114 // hierarchical call tree. 115 struct stats 116 { 117 stats (void); 118 119 double m_time; 120 std::size_t m_calls; 121 122 bool m_recursive; 123 124 typedef std::set<octave_idx_type> function_set; 125 function_set m_parents; 126 function_set m_children; 127 128 // Convert a function_set list to an Octave array of indices. 129 static octave_value function_set_value (const function_set&); 130 }; 131 132 typedef std::vector<stats> flat_profile; 133 134 // Store data for one node in the call-tree of the hierarchical profiler 135 // data we collect. 136 class tree_node 137 { 138 public: 139 140 tree_node (tree_node*, octave_idx_type); 141 142 virtual ~tree_node (void); 143 144 // No copying! 145 146 tree_node (const tree_node&) = delete; 147 148 tree_node& operator = (const tree_node&) = delete; 149 add_time(double dt)150 void add_time (double dt) { m_time += dt; } 151 152 // Enter a child function. It is created in the list of children if it 153 // wasn't already there. The now-active child node is returned. 154 tree_node *enter (octave_idx_type); 155 156 // Exit function. As a sanity-check, it is verified that the currently 157 // active function actually is the one handed in here. Returned is the 158 // then-active node, which is our parent. 159 tree_node *exit (octave_idx_type); 160 161 void build_flat (flat_profile&) const; 162 163 // Get the hierarchical profile for this node and its children. If total 164 // is set, accumulate total time of the subtree in that variable as 165 // additional return value. 166 octave_value get_hierarchical (double *total = nullptr) const; 167 168 private: 169 170 tree_node *m_parent; 171 octave_idx_type m_fcn_id; 172 173 typedef std::map<octave_idx_type, tree_node*> child_map; 174 child_map m_children; 175 176 // This is only time spent *directly* on this level, excluding children! 177 double m_time; 178 179 std::size_t m_calls; 180 }; 181 182 // Each function we see in the profiler is given a unique index (which 183 // simply counts starting from 1). We thus have to map profiler-names to 184 // those indices. For all other stuff, we identify functions by their index. 185 186 typedef std::vector<std::string> function_set; 187 typedef std::map<std::string, octave_idx_type> fcn_index_map; 188 189 function_set m_known_functions; 190 fcn_index_map m_fcn_index; 191 192 bool m_enabled; 193 194 tree_node *m_call_tree; 195 tree_node *m_active_fcn; 196 197 // Store last timestamp we had, when the currently active function was called. 198 double m_last_time; 199 200 // These are private as only the unwind-protecting inner class enter 201 // should be allowed to call them. 202 void enter_function (const std::string&); 203 void exit_function (const std::string&); 204 205 // Query a timestamp, used for timing calls (obviously). 206 // This is not static because in the future, maybe we want a flag 207 // in the profiler or something to choose between cputime, wall-time, 208 // user-time, system-time, ... 209 double query_time (void) const; 210 211 // Add the time elapsed since last_time to the function we're currently in. 212 // This is called from two different positions, thus it is useful to have 213 // it as a separate function. 214 void add_current_time (void); 215 }; 216 } 217 218 #endif 219