Panda3D
pStatView.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file pStatView.cxx
10  * @author drose
11  * @date 2000-07-10
12  */
13 
14 #include "pStatView.h"
15 
16 #include "pStatFrameData.h"
17 #include "pStatCollectorDef.h"
18 #include "vector_int.h"
19 #include "plist.h"
20 #include "pset.h"
21 
22 #include <algorithm>
23 
24 
25 
26 /**
27  * This class is used within this module only--in fact, within
28  * PStatView::set_to_frame() only--to help collect event data out of the
29  * PStatFrameData object and boil it down to a list of elapsed times.
30  */
31 class FrameSample {
32 public:
33  typedef plist<FrameSample *> Started;
34 
35  FrameSample() {
36  _touched = false;
37  _is_started = false;
38  _pushed = false;
39  _net_time = 0.0;
40  }
41  void data_point(double time, bool is_start, Started &started) {
42  _touched = true;
43 
44  // We only consider events that change the startstop state. With two
45  // consecutive 'start' events, for instance, we ignore the second one.
46 
47 /*
48  * *** That's not quite the right thing to do. We should keep track of the
49  * nesting level and bracket things correctly, so that we ignore the second
50  * start and the *first* stop, but respect the outer startstop. For the short
51  * term, this works, because the client is already doing this logic and won't
52  * send us nested startstop pairs, but we'd like to generalize this in the
53  * future so we can deal with these nested pairs properly.
54  */
55  nassertv(is_start != _is_started);
56 
57  _is_started = is_start;
58 
59  if (_pushed) {
60  nassertv(!_is_started);
61  Started::iterator si = find(started.begin(), started.end(), this);
62  nassertv(si != started.end());
63  started.erase(si);
64 
65  } else {
66  if (_is_started) {
67  _net_time -= time;
68  push_all(time, started);
69  started.push_back(this);
70  } else {
71  _net_time += time;
72  Started::iterator si = find(started.begin(), started.end(), this);
73  nassertv(si != started.end());
74  started.erase(si);
75  pop_one(time, started);
76  }
77  }
78  }
79  void push(double time) {
80  if (!_pushed) {
81  _pushed = true;
82  if (_is_started) {
83  _net_time += time;
84  }
85  }
86  }
87  void pop(double time) {
88  if (_pushed) {
89  _pushed = false;
90  if (_is_started) {
91  _net_time -= time;
92  }
93  }
94  }
95 
96  void push_all(double time, Started &started) {
97  Started::iterator si;
98  for (si = started.begin(); si != started.end(); ++si) {
99  (*si)->push(time);
100  }
101  }
102 
103  void pop_one(double time, Started &started) {
104  Started::reverse_iterator si;
105  for (si = started.rbegin(); si != started.rend(); ++si) {
106  if ((*si)->_pushed) {
107  (*si)->pop(time);
108  return;
109  }
110  }
111  }
112 
113  bool _touched;
114  bool _is_started;
115  bool _pushed;
116  double _net_time;
117 };
118 
119 
120 
121 /**
122  *
123  */
124 PStatView::
125 PStatView() {
126  _constraint = 0;
127  _show_level = false;
128  _all_collectors_known = false;
129  _level_index = 0;
130 }
131 
132 /**
133  *
134  */
135 PStatView::
136 ~PStatView() {
137  clear_levels();
138 }
139 
140 /**
141  * Changes the focus of the View. By default, the View reports the entire
142  * time for the frame, and all of the Collectors that are directly parented to
143  * "Frame". By constraining the view to a particular collector, you cause the
144  * View to zoom in on that collector's data, reporting only the collector and
145  * its immediate parents.
146  *
147  * When you constrain the view, you may also specify whether the view should
148  * show time data or level data for the indicated collector. If level data,
149  * it reports the levels for the collector, and all of its children;
150  * otherwise, it collects the elapsed time.
151  *
152  * Changing the constraint causes the current frame's data to become
153  * invalidated; you must then call set_to_frame() again to get any useful data
154  * out.
155  */
156 void PStatView::
157 constrain(int collector, bool show_level) {
158  _constraint = collector;
159  _show_level = show_level;
160  clear_levels();
161 }
162 
163 /**
164  * Restores the view to the full frame. This is equivalent to calling
165  * constrain(0).
166  */
167 void PStatView::
169  constrain(0, false);
170 }
171 
172 /**
173  *
174  */
175 void PStatView::
176 set_thread_data(const PStatThreadData *thread_data) {
177  _thread_data = thread_data;
178  _client_data = thread_data->get_client_data();
179  clear_levels();
180  _all_collectors_known = false;
181 }
182 
183 /**
184  * Supplies the View with the data for the current frame. This causes the
185  * View to update all of its internal data to reflect the frame's data,
186  * subject to the current constraint.
187  *
188  * It is possible that calling this will increase the total number of reported
189  * levels (for instance, if this frame introduced a new collector that hadn't
190  * been active previously). In this case, the caller must update its display
191  * or whatever to account for the new level.
192  */
193 void PStatView::
194 set_to_frame(const PStatFrameData &frame_data) {
195  nassertv(!_thread_data.is_null());
196  nassertv(!_client_data.is_null());
197 
198  if (_show_level) {
199  update_level_data(frame_data);
200  } else {
201  update_time_data(frame_data);
202  }
203 }
204 
205 
206 /**
207  * After a call to set_to_frame(), this returns true if all collectors in the
208  * FrameData are known by the PStatsData object, or false if some are still
209  * unknown (even those that do not appear in the view).
210  */
211 bool PStatView::
213  return _all_collectors_known;
214 }
215 
216 /**
217  * Returns the total value accounted for by the frame (or by whatever
218  * Collector we are constrained to). This is the sum of all of the individual
219  * levels' get_net_value() value.
220  */
221 double PStatView::
222 get_net_value() const {
223  double net = 0.0;
224  Levels::const_iterator li;
225  for (li = _levels.begin(); li != _levels.end(); ++li) {
226  net += (*li).second->_value_alone;
227  }
228 
229  return net;
230 }
231 
232 /**
233  * Returns a pointer to the level that corresponds to the Collector we've
234  * constrained to. This is the top of a graph of levels; typically the next
235  * level down--the children of this level--will be the levels you want to
236  * display to the user.
237  */
240  return get_level(_constraint);
241 }
242 
243 /**
244  * Returns true if there is a level defined for the particular collector,
245  * false otherwise.
246  */
247 bool PStatView::
248 has_level(int collector) const {
249  Levels::const_iterator li;
250  li = _levels.find(collector);
251  return (li != _levels.end());
252 }
253 
254 /**
255  * Returns a pointer to the level that corresponds to the indicated Collector.
256  * If there is no such level in the view, one will be created--use with
257  * caution. Check has_level() first if you don't want this behavior.
258  */
260 get_level(int collector) {
261  Levels::const_iterator li;
262  li = _levels.find(collector);
263  if (li != _levels.end()) {
264  return (*li).second;
265  }
266 
267  PStatViewLevel *level = new PStatViewLevel;
268  level->_collector = collector;
269  level->_parent = nullptr;
270  _levels[collector] = level;
271 
272  reset_level(level);
273  return level;
274 }
275 
276 /**
277  * The implementation of set_to_frame() for views that show elapsed time.
278  */
279 void PStatView::
280 update_time_data(const PStatFrameData &frame_data) {
281  int num_events = frame_data.get_num_events();
282 
283  typedef pvector<FrameSample> Samples;
284  Samples samples(_client_data->get_num_collectors());
285 
286  FrameSample::Started started;
287 
288  _all_collectors_known = true;
289 
290 
291  // This tracks the set of samples we actually care about.
292  typedef pset<int> GotSamples;
293  GotSamples got_samples;
294 
295  int i;
296  for (i = 0; i < num_events; i++) {
297  int collector_index = frame_data.get_time_collector(i);
298  bool is_start = frame_data.is_start(i);
299 
300  if (!_client_data->has_collector(collector_index)) {
301  _all_collectors_known = false;
302 
303  } else {
304  nassertv(collector_index >= 0 && collector_index < (int)samples.size());
305 
306  if (_client_data->get_child_distance(_constraint, collector_index) >= 0) {
307  // Here's a data point we care about: anything at constraint level or
308  // below.
309  if (is_start == samples[collector_index]._is_started) {
310  if (!is_start) {
311  // A "stop" in the middle of a frame implies a "start" since time
312  // 0 (that is, since the first data point in the frame).
313  samples[collector_index].data_point(frame_data.get_time(0), true, started);
314  samples[collector_index].data_point(frame_data.get_time(i), is_start, started);
315  } else {
316  // An extra "start" for a collector that's already started is an
317  // error.
318  nout << "Unexpected data point for "
319  << _client_data->get_collector_fullname(collector_index)
320  << "\n";
321  }
322  } else {
323  samples[collector_index].data_point(frame_data.get_time(i), is_start, started);
324  got_samples.insert(collector_index);
325  }
326  }
327  }
328  }
329 
330  // Make sure everything is stopped.
331 
332  Samples::iterator si;
333  for (i = 0, si = samples.begin(); si != samples.end(); ++i, ++si) {
334  if ((*si)._is_started) {
335  (*si).data_point(frame_data.get_end(), false, started);
336  }
337  }
338 
339  nassertv(started.empty());
340 
341  bool any_new_levels = false;
342 
343  // Now match these samples we got up with those we already had in the
344  // levels.
345  Levels::iterator li, lnext;
346  li = _levels.begin();
347  while (li != _levels.end()) {
348  // Be careful while traversing a container and calling functions that
349  // could modify that container.
350  lnext = li;
351  ++lnext;
352 
353  PStatViewLevel *level = (*li).second;
354  if (reset_level(level)) {
355  any_new_levels = true;
356  }
357 
358  int collector_index = level->_collector;
359  GotSamples::iterator gi;
360  gi = got_samples.find(collector_index);
361  if (gi != got_samples.end()) {
362  level->_value_alone = samples[collector_index]._net_time;
363  got_samples.erase(gi);
364  }
365 
366  li = lnext;
367  }
368 
369  // Finally, any samples left over in the got_samples set are new collectors
370  // that we need to add to the Levels list.
371  if (!got_samples.empty()) {
372  any_new_levels = true;
373 
374  GotSamples::const_iterator gi;
375  for (gi = got_samples.begin(); gi != got_samples.end(); ++gi) {
376  int collector_index = (*gi);
377  PStatViewLevel *level = get_level(collector_index);
378  level->_value_alone = samples[*gi]._net_time;
379  }
380  }
381 
382  if (any_new_levels) {
383  _level_index++;
384  }
385 }
386 
387 /**
388  * The implementation of set_to_frame() for views that show level values.
389  */
390 void PStatView::
391 update_level_data(const PStatFrameData &frame_data) {
392  _all_collectors_known = true;
393 
394 
395  // This tracks the set of level values we got.
396  typedef pmap<int, double> GotValues;
397  GotValues net_values;
398 
399  int i;
400  int num_levels = frame_data.get_num_levels();
401  for (i = 0; i < num_levels; i++) {
402  int collector_index = frame_data.get_level_collector(i);
403  double value = frame_data.get_level(i);
404 
405  if (!_client_data->has_collector(collector_index)) {
406  _all_collectors_known = false;
407 
408  } else {
409  if (_client_data->get_child_distance(_constraint, collector_index) >= 0) {
410  net_values[collector_index] = value;
411  }
412  }
413  }
414 
415  // Now that we've counted up the net level for each collector, compute the
416  // level for each collector alone by subtracting out each child from its
417  // parents. If a parent has no data, nothing is subtracted.
418  GotValues alone_values = net_values;
419 
420  GotValues::iterator gi;
421  for (gi = net_values.begin(); gi != net_values.end(); ++gi) {
422  int collector_index = (*gi).first;
423  double value = (*gi).second;
424 
425  // Walk up to the top, but stop when we find a parent with actual data.
426  while (collector_index != 0 && collector_index != _constraint) {
427  const PStatCollectorDef &def =
428  _client_data->get_collector_def(collector_index);
429  int parent_index = def._parent_index;
430  GotValues::iterator pi = alone_values.find(parent_index);
431  if (pi != alone_values.end()) {
432  // The parent has data; subtract it.
433  (*pi).second -= value;
434  break;
435  }
436  collector_index = parent_index;
437  }
438  }
439 
440 
441  bool any_new_levels = false;
442 
443  // Now match these samples we got up with those we already had in the
444  // levels.
445  Levels::iterator li, lnext;
446  li = _levels.begin();
447  while (li != _levels.end()) {
448  // Be careful while traversing a container and calling functions that
449  // could modify that container.
450  lnext = li;
451  ++lnext;
452 
453  PStatViewLevel *level = (*li).second;
454  if (reset_level(level)) {
455  any_new_levels = true;
456  }
457 
458  int collector_index = level->_collector;
459  GotValues::iterator gi;
460  gi = alone_values.find(collector_index);
461  if (gi != alone_values.end()) {
462  level->_value_alone = (*gi).second;
463  alone_values.erase(gi);
464  }
465 
466  li = lnext;
467  }
468 
469  // Finally, any values left over in the alone_values set are new collectors
470  // that we need to add to the Levels list.
471  if (!alone_values.empty()) {
472  any_new_levels = true;
473 
474  GotValues::const_iterator gi;
475  for (gi = alone_values.begin(); gi != alone_values.end(); ++gi) {
476  int collector_index = (*gi).first;
477  PStatViewLevel *level = get_level(collector_index);
478  level->_value_alone = (*gi).second;
479  }
480  }
481 
482  if (any_new_levels) {
483  _level_index++;
484  }
485 }
486 
487 /**
488  * Resets all the levels that have been defined so far.
489  */
490 void PStatView::
491 clear_levels() {
492  Levels::iterator li;
493  for (li = _levels.begin(); li != _levels.end(); ++li) {
494  delete (*li).second;
495  }
496  _levels.clear();
497 }
498 
499 /**
500  * Resets the total value of the Level to zero, and also makes sure it is
501  * parented to the right Level corresponding to its Collector's parent. Since
502  * the client might change its mind from time to time about who the Collector
503  * is parented to, we have to update this dynamically.
504  *
505  * Returns true if any change was made to the level's hierarchy, false
506  * otherwise.
507  */
508 bool PStatView::
509 reset_level(PStatViewLevel *level) {
510  bool any_changed = false;
511  level->_value_alone = 0.0;
512 
513  if (level->_collector == _constraint) {
514  return false;
515  }
516 
517  if (_client_data->has_collector(level->_collector)) {
518  int parent_index =
519  _client_data->get_collector_def(level->_collector)._parent_index;
520 
521  if (level->_parent == nullptr) {
522  // This level didn't know its parent before, but now it does.
523  PStatViewLevel *parent_level = get_level(parent_index);
524  nassertr(parent_level != level, true);
525 
526  level->_parent = parent_level;
527  parent_level->_children.push_back(level);
528  parent_level->sort_children(_client_data);
529  any_changed = true;
530 
531  } else if (level->_parent->_collector != parent_index) {
532  // This level knew about its parent, but now it's something different.
533  PStatViewLevel *old_parent_level = level->_parent;
534  nassertr(old_parent_level != level, true);
535 
536  if (parent_index != 0) {
537  PStatViewLevel *new_parent_level = get_level(parent_index);
538  nassertr(new_parent_level != level, true);
539  level->_parent = new_parent_level;
540  new_parent_level->_children.push_back(level);
541  new_parent_level->sort_children(_client_data);
542  } else {
543  level->_parent = nullptr;
544  }
545 
546  PStatViewLevel::Children::iterator ci =
547  find(old_parent_level->_children.begin(),
548  old_parent_level->_children.end(),
549  level);
550 
551  nassertr(ci != old_parent_level->_children.end(), true);
552  old_parent_level->_children.erase(ci);
553  any_changed = true;
554  }
555  }
556 
557  return any_changed;
558 }
const PStatViewLevel * get_top_level()
Returns a pointer to the level that corresponds to the Collector we've constrained to.
Definition: pStatView.cxx:239
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void sort_children(const PStatClientData *client_data)
Sorts the children of this view level into order as specified by the client's sort index.
This is our own Panda specialization on the default STL map.
Definition: pmap.h:49
void set_to_frame(const PStatFrameData &frame_data)
Supplies the View with the data for the current frame.
Definition: pStatView.cxx:194
int get_level_collector(size_t n) const
Returns the index of the collector associated with the nth level value.
void unconstrain()
Restores the view to the full frame.
Definition: pStatView.cxx:168
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is our own Panda specialization on the default STL list.
Definition: plist.h:35
double get_level(size_t n) const
Returns the height of the nth level value.
double get_net_value() const
Returns the total value accounted for by the frame (or by whatever Collector we are constrained to).
Definition: pStatView.cxx:222
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:42
This is a single level value, or band of color, within a View.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Contains the raw timing and level data for a single frame.
double get_time(size_t n) const
Returns the timestamp of the nth event, in seconds elapsed since some undefined epoch (which is guara...
bool all_collectors_known() const
After a call to set_to_frame(), this returns true if all collectors in the FrameData are known by the...
Definition: pStatView.cxx:212
size_t get_num_levels() const
Returns the number of individual level values stored in the FrameData.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A collection of FrameData structures for recently-received frames within a particular thread.
int get_time_collector(size_t n) const
Returns the index of the collector associated with the nth event.
bool is_start(size_t n) const
Returns true if the nth event represents a start event, or false if it represents a stop event.
void constrain(int collector, bool show_level)
Changes the focus of the View.
Definition: pStatView.cxx:157
double get_end() const
Returns the time of the last data point in the frame data.
size_t get_num_events() const
Returns the number of individual events stored in the FrameData.
Defines the details about the Collectors: the name, the suggested color, etc.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool has_level(int collector) const
Returns true if there is a level defined for the particular collector, false otherwise.
Definition: pStatView.cxx:248
PStatViewLevel * get_level(int collector)
Returns a pointer to the level that corresponds to the indicated Collector.
Definition: pStatView.cxx:260
const PStatClientData * get_client_data() const
Returns a pointer to the ClientData structure associated with this data.