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