GNU Octave 7.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
profiler.cc
Go to the documentation of this file.
1////////////////////////////////////////////////////////////////////////
2//
3// Copyright (C) 2012-2022 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 (HAVE_CONFIG_H)
27# include "config.h"
28#endif
29
30#include "defun.h"
31#include "event-manager.h"
32#include "interpreter.h"
33#include "oct-time.h"
34#include "ov-struct.h"
35#include "pager.h"
36#include "profiler.h"
37
38OCTAVE_NAMESPACE_BEGIN
39
41 : m_time (0.0), m_calls (0), m_recursive (false),
42 m_parents (), m_children ()
43 { }
44
47 {
48 const octave_idx_type n = list.size ();
49
50 RowVector retval (n);
51 octave_idx_type i = 0;
52 for (const auto& nm : list)
53 retval(i++) = nm;
54
55 assert (i == n);
56
57 return retval;
58 }
59
61 : m_parent (p), m_fcn_id (f), m_children (), m_time (0.0), m_calls (0)
62 { }
63
65 {
66 for (auto& idx_tnode : m_children)
67 delete idx_tnode.second;
68 }
69
72 {
73 tree_node *retval;
74
75 child_map::iterator pos = m_children.find (fcn);
76 if (pos == m_children.end ())
77 {
78 retval = new tree_node (this, fcn);
79 m_children[fcn] = retval;
80 }
81 else
82 retval = pos->second;
83
84 ++retval->m_calls;
85 return retval;
86 }
87
90 {
91 // FIXME: These assert statements don't make sense if profile() is called
92 // from within a function hierarchy to begin with. See bug #39587.
93 // assert (m_parent);
94 // assert (m_fcn_id == fcn);
95
96 return m_parent;
97 }
98
99 void
101 {
102 // If this is not the top-level node, update profile entry for this function.
103 if (m_fcn_id != 0)
104 {
105 stats& entry = data[m_fcn_id - 1];
106
107 entry.m_time += m_time;
108 entry.m_calls += m_calls;
109
110 assert (m_parent);
111 if (m_parent->m_fcn_id != 0)
112 {
113 entry.m_parents.insert (m_parent->m_fcn_id);
114 data[m_parent->m_fcn_id - 1].m_children.insert (m_fcn_id);
115 }
116
117 if (! entry.m_recursive)
118 for (const tree_node *i = m_parent; i; i = i->m_parent)
119 if (i->m_fcn_id == m_fcn_id)
120 {
121 entry.m_recursive = true;
122 break;
123 }
124 }
125
126 // Recurse on children.
127 for (const auto& idx_tnode : m_children)
128 idx_tnode.second->build_flat (data);
129 }
130
133 {
134 // Note that we don't generate the entry just for this node, but
135 // rather a struct-array with entries for all children. This way, the
136 // top-node (for which we don't want a real entry) generates already
137 // the final hierarchical profile data.
138
139 const octave_idx_type n = m_children.size ();
140
141 Cell rv_indices (n, 1);
142 Cell rv_times (n, 1);
143 Cell rv_totals (n, 1);
144 Cell rv_calls (n, 1);
145 Cell rv_children (n, 1);
146
147 octave_idx_type i = 0;
148 for (const auto& idx_tnode : m_children)
149 {
150 const tree_node& entry = *idx_tnode.second;
151 double child_total = entry.m_time;
152
153 rv_indices(i) = octave_value (idx_tnode.first);
154 rv_times(i) = octave_value (entry.m_time);
155 rv_calls(i) = octave_value (entry.m_calls);
156 rv_children(i) = entry.get_hierarchical (&child_total);
157 rv_totals(i) = octave_value (child_total);
158
159 if (total)
160 *total += child_total;
161
162 ++i;
163 }
164 assert (i == n);
165
166 octave_map retval;
167
168 retval.assign ("Index", rv_indices);
169 retval.assign ("SelfTime", rv_times);
170 retval.assign ("TotalTime", rv_totals);
171 retval.assign ("NumCalls", rv_calls);
172 retval.assign ("Children", rv_children);
173
174 return retval;
175 }
176
179 m_enabled (false), m_call_tree (new tree_node (nullptr, 0)),
180 m_active_fcn (nullptr), m_last_time (-1.0)
181 { }
182
184 {
185 delete m_call_tree;
186 }
187
188 void
190 {
191 m_enabled = value;
192 }
193
194 void
195 profiler::enter_function (const std::string& fcn)
196 {
197 // The enter class will check and only call us if the profiler is active.
198 assert (enabled ());
199 assert (m_call_tree);
200
201 // If there is already an active function, add to its time before
202 // pushing the new one.
205
206 // Map the function's name to its index.
207 octave_idx_type fcn_idx;
208 fcn_index_map::iterator pos = m_fcn_index.find (fcn);
209 if (pos == m_fcn_index.end ())
210 {
211 m_known_functions.push_back (fcn);
212 fcn_idx = m_known_functions.size ();
213 m_fcn_index[fcn] = fcn_idx;
214 }
215 else
216 fcn_idx = pos->second;
217
218 if (! m_active_fcn)
220
221 m_active_fcn = m_active_fcn->enter (fcn_idx);
222
224
225 }
226
227 void
228 profiler::exit_function (const std::string& fcn)
229 {
230 if (m_active_fcn)
231 {
232 assert (m_call_tree);
233 // FIXME: This assert statements doesn't make sense if profile() is called
234 // from within a function hierarchy to begin with. See bug #39587.
235 //assert (m_active_fcn != m_call_tree);
236
237 // Usually, if we are disabled this function is not even called. But the
238 // call disabling the profiler is an exception. So also check here
239 // and only record the time if enabled.
240 if (enabled ())
242
243 fcn_index_map::iterator pos = m_fcn_index.find (fcn);
244 // FIXME: This assert statements doesn't make sense if profile() is called
245 // from within a function hierarchy to begin with. See bug #39587.
246 //assert (pos != m_fcn_index.end ());
247 m_active_fcn = m_active_fcn->exit (pos->second);
248
249 // If this was an "inner call", we resume executing the parent function
250 // up the stack. So note the start-time for this!
252 }
253 }
254
255 void
257 {
258 if (enabled ())
259 error ("profile: can't reset active profiler");
260
261 m_known_functions.clear ();
262 m_fcn_index.clear ();
263
264 if (m_call_tree)
265 {
266 delete m_call_tree;
267 m_call_tree = new tree_node (nullptr, 0);
268 m_active_fcn = nullptr;
269 }
270
271 m_last_time = -1.0;
272 }
273
276 {
277 octave_value retval;
278
279 const octave_idx_type n = m_known_functions.size ();
280
281 flat_profile flat (n);
282
283 if (m_call_tree)
284 {
285 m_call_tree->build_flat (flat);
286
287 Cell rv_names (n, 1);
288 Cell rv_times (n, 1);
289 Cell rv_calls (n, 1);
290 Cell rv_recursive (n, 1);
291 Cell rv_parents (n, 1);
292 Cell rv_children (n, 1);
293
294 for (octave_idx_type i = 0; i != n; ++i)
295 {
296 rv_names(i) = octave_value (m_known_functions[i]);
297 rv_times(i) = octave_value (flat[i].m_time);
298 rv_calls(i) = octave_value (flat[i].m_calls);
299 rv_recursive(i) = octave_value (flat[i].m_recursive);
300 rv_parents(i) = stats::function_set_value (flat[i].m_parents);
301 rv_children(i) = stats::function_set_value (flat[i].m_children);
302 }
303
304 octave_map m;
305
306 m.assign ("FunctionName", rv_names);
307 m.assign ("TotalTime", rv_times);
308 m.assign ("NumCalls", rv_calls);
309 m.assign ("IsRecursive", rv_recursive);
310 m.assign ("Parents", rv_parents);
311 m.assign ("Children", rv_children);
312
313 retval = m;
314 }
315 else
316 {
317 static const char *fn[] =
318 {
319 "FunctionName",
320 "TotalTime",
321 "NumCalls",
322 "IsRecursive",
323 "Parents",
324 "Children",
325 nullptr
326 };
327
328 static octave_map m (dim_vector (0, 1), string_vector (fn));
329
330 retval = m;
331 }
332
333 return retval;
334 }
335
338 {
339 octave_value retval;
340
341 if (m_call_tree)
342 retval = m_call_tree->get_hierarchical ();
343 else
344 {
345 static const char *fn[] =
346 {
347 "Index",
348 "SelfTime",
349 "NumCalls",
350 "Children",
351 nullptr
352 };
353
354 static octave_map m (dim_vector (0, 1), string_vector (fn));
355
356 retval = m;
357 }
358
359 return retval;
360 }
361
362 double
364 {
365 sys::time now;
366
367 // FIXME: is this volatile declaration really needed?
368 // See bug #34210 for additional details.
369 volatile double dnow = now.double_value ();
370
371 return dnow;
372 }
373
374 void
376 {
377 if (m_active_fcn)
378 {
379 const double t = query_time ();
380
382 }
383 }
384
385// Enable or disable the profiler data collection.
386DEFMETHOD (__profiler_enable__, interp, args, ,
387 doc: /* -*- texinfo -*-
388@deftypefn {} {} __profiler_enable__ ()
389Undocumented internal function.
390@end deftypefn */)
391{
392 int nargin = args.length ();
393
394 if (nargin > 1)
395 print_usage ();
396
397 profiler& profiler = interp.get_profiler ();
398
399 if (nargin == 1)
400 {
401 profiler.set_active (args(0).bool_value ());
402
403 std::string status = "off";
404 if (args(0).bool_value ())
405 status = "on";
406
407 event_manager& evmgr = interp.get_event_manager ();
408 evmgr.gui_status_update ("profiler", status); // tell GUI
409 }
410
411 return ovl (profiler.enabled ());
412}
413
414// Clear all collected profiling data.
415DEFMETHOD (__profiler_reset__, interp, args, ,
416 doc: /* -*- texinfo -*-
417@deftypefn {} {} __profiler_reset__ ()
418Undocumented internal function.
419@end deftypefn */)
420{
421 if (args.length () != 0)
422 print_usage ();
423
424 profiler& profiler = interp.get_profiler ();
425
426 profiler.reset ();
427
428 return ovl ();
429}
430
431// Query the timings collected by the profiler.
432DEFMETHOD (__profiler_data__, interp, args, nargout,
433 doc: /* -*- texinfo -*-
434@deftypefn {} {} __profiler_data__ ()
435Undocumented internal function.
436@end deftypefn */)
437{
438 if (args.length () != 0)
439 print_usage ();
440
441 profiler& profiler = interp.get_profiler ();
442
443 if (nargout > 1)
445 else
446 return ovl (profiler.get_flat ());
447}
448
449OCTAVE_NAMESPACE_END
Definition: Cell.h:43
Vector representing the dimensions (size) of an Array.
Definition: dim-vector.h:94
Provides threadsafe access to octave.
bool gui_status_update(const std::string &feature, const std::string &status)
void assign(const std::string &k, const Cell &val)
Definition: oct-map.h:365
void build_flat(flat_profile &) const
Definition: profiler.cc:100
octave_value get_hierarchical(double *total=nullptr) const
Definition: profiler.cc:132
tree_node * exit(octave_idx_type)
Definition: profiler.cc:89
std::size_t m_calls
Definition: profiler.h:183
virtual ~tree_node(void)
Definition: profiler.cc:64
void add_time(double dt)
Definition: profiler.h:154
tree_node(tree_node *, octave_idx_type)
Definition: profiler.cc:60
tree_node * enter(octave_idx_type)
Definition: profiler.cc:71
tree_node * m_parent
Definition: profiler.h:174
octave_value get_flat(void) const
Definition: profiler.cc:275
fcn_index_map m_fcn_index
Definition: profiler.h:194
double query_time(void) const
Definition: profiler.cc:363
tree_node * m_active_fcn
Definition: profiler.h:199
tree_node * m_call_tree
Definition: profiler.h:198
void add_current_time(void)
Definition: profiler.cc:375
virtual ~profiler(void)
Definition: profiler.cc:183
profiler(void)
Definition: profiler.cc:177
void reset(void)
Definition: profiler.cc:256
bool enabled(void) const
Definition: profiler.h:102
double m_last_time
Definition: profiler.h:202
bool m_enabled
Definition: profiler.h:196
void set_active(bool)
Definition: profiler.cc:189
std::vector< stats > flat_profile
Definition: profiler.h:136
octave_value get_hierarchical(void) const
Definition: profiler.cc:337
void exit_function(const std::string &)
Definition: profiler.cc:228
function_set m_known_functions
Definition: profiler.h:193
void enter_function(const std::string &)
Definition: profiler.cc:195
OCTINTERP_API void print_usage(void)
Definition: defun-int.h:72
#define DEFMETHOD(name, interp_name, args_name, nargout_name, doc)
Macro to define a builtin method.
Definition: defun.h:111
void error(const char *fmt,...)
Definition: error.cc:980
F77_RET_T const F77_DBLE const F77_DBLE * f
return octave_value(v1.char_array_value() . concat(v2.char_array_value(), ra_idx),((a1.is_sq_string()||a2.is_sq_string()) ? '\'' :'"'))
octave_value_list ovl(const OV_Args &... args)
Construct an octave_value_list with less typing.
Definition: ovl.h:211
static octave_value function_set_value(const function_set &)
Definition: profiler.cc:46
double m_time
Definition: profiler.h:127
std::set< octave_idx_type > function_set
Definition: profiler.h:120
function_set m_parents
Definition: profiler.h:132
std::size_t m_calls
Definition: profiler.h:128
bool m_recursive
Definition: profiler.h:130