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 */
31class FrameSample {
32public:
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 */
124PStatView::
125PStatView() {
126 _constraint = 0;
127 _show_level = false;
128 _all_collectors_known = false;
129 _level_index = 0;
130}
131
132/**
133 *
134 */
135PStatView::
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 */
157constrain(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 */
168unconstrain() {
169 constrain(0, false);
170}
171
172/**
173 *
174 */
175void PStatView::
176set_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 */
194set_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 */
212all_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 */
222get_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 */
248has_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 */
260get_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 */
279void PStatView::
280update_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 */
390void PStatView::
391update_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 */
490void PStatView::
491clear_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 */
508bool PStatView::
509reset_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.