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