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  */
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  */
168 unconstrain() {
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  */
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  */
212 all_collectors_known() const {
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  */
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  */
239 get_top_level() {
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  */
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 }
Defines the details about the Collectors: the name, the suggested color, etc.
Contains the raw timing and level data for a single frame.
double get_end() const
Returns the time of the last data point in the frame data.
double get_time(size_t n) const
Returns the timestamp of the nth event, in seconds elapsed since some undefined epoch (which is guara...
int get_time_collector(size_t n) const
Returns the index of the collector associated with the nth event.
size_t get_num_levels() const
Returns the number of individual level values stored in the FrameData.
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.
size_t get_num_events() const
Returns the number of individual events stored in the FrameData.
double get_level(size_t n) const
Returns the height of the nth level value.
int get_level_collector(size_t n) const
Returns the index of the collector associated with the nth level value.
A collection of FrameData structures for recently-received frames within a particular thread.
const PStatClientData * get_client_data() const
Returns a pointer to the ClientData structure associated with this data.
This is a single level value, or band of color, within a View.
void sort_children(const PStatClientData *client_data)
Sorts the children of this view level into order as specified by the client's sort index.
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
const PStatViewLevel * get_top_level()
Returns a pointer to the level that corresponds to the Collector we've constrained to.
Definition: pStatView.cxx:239
void set_to_frame(const PStatFrameData &frame_data)
Supplies the View with the data for the current frame.
Definition: pStatView.cxx:194
void unconstrain()
Restores the view to the full frame.
Definition: pStatView.cxx:168
void constrain(int collector, bool show_level)
Changes the focus of the View.
Definition: pStatView.cxx:157
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
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
This is our own Panda specialization on the default STL list.
Definition: plist.h:35
This is our own Panda specialization on the default STL map.
Definition: pmap.h:49
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:42
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.