Panda3D

pStatView.cxx

00001 // Filename: pStatView.cxx
00002 // Created by:  drose (10Jul00)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "pStatView.h"
00016 
00017 #include "pStatFrameData.h"
00018 #include "pStatCollectorDef.h"
00019 #include "vector_int.h"
00020 #include "plist.h"
00021 #include "pset.h"
00022 
00023 #include <algorithm>
00024 
00025 
00026 
00027 ////////////////////////////////////////////////////////////////////
00028 //       Class : FrameSample
00029 // Description : This class is used within this module only--in fact,
00030 //               within PStatView::set_to_frame() only--to help
00031 //               collect event data out of the PStatFrameData object
00032 //               and boil it down to a list of elapsed times.
00033 ////////////////////////////////////////////////////////////////////
00034 class FrameSample {
00035 public:
00036   typedef plist<FrameSample *> Started;
00037 
00038   FrameSample() {
00039     _touched = false;
00040     _is_started = false;
00041     _pushed = false;
00042     _net_time = 0.0;
00043   }
00044   void data_point(double time, bool is_start, Started &started) {
00045     _touched = true;
00046 
00047     // We only consider events that change the start/stop state.
00048     // With two consecutive 'start' events, for instance, we ignore
00049     // the second one.
00050 
00051     // *** That's not quite the right thing to do.  We should keep
00052     // track of the nesting level and bracket things correctly, so
00053     // that we ignore the second start and the *first* stop, but
00054     // respect the outer start/stop.  For the short term, this
00055     // works, because the client is already doing this logic and
00056     // won't send us nested start/stop pairs, but we'd like to
00057     // generalize this in the future so we can deal with these
00058     // nested pairs properly.
00059     nassertv(is_start != _is_started);
00060 
00061     _is_started = is_start;
00062 
00063     if (_pushed) {
00064       nassertv(!_is_started);
00065       Started::iterator si = find(started.begin(), started.end(), this);
00066       nassertv(si != started.end());
00067       started.erase(si);
00068 
00069     } else {
00070       if (_is_started) {
00071         _net_time -= time;
00072         push_all(time, started);
00073         started.push_back(this);
00074       } else {
00075         _net_time += time;
00076         Started::iterator si = find(started.begin(), started.end(), this);
00077         nassertv(si != started.end());
00078         started.erase(si);
00079         pop_one(time, started);
00080       }
00081     }
00082   }
00083   void push(double time) {
00084     if (!_pushed) {
00085       _pushed = true;
00086       if (_is_started) {
00087         _net_time += time;
00088       }
00089     }
00090   }
00091   void pop(double time) {
00092     if (_pushed) {
00093       _pushed = false;
00094       if (_is_started) {
00095         _net_time -= time;
00096       }
00097     }
00098   }
00099 
00100   void push_all(double time, Started &started) {
00101     Started::iterator si;
00102     for (si = started.begin(); si != started.end(); ++si) {
00103       (*si)->push(time);
00104     }
00105   }
00106 
00107   void pop_one(double time, Started &started) {
00108     Started::reverse_iterator si;
00109     for (si = started.rbegin(); si != started.rend(); ++si) {
00110       if ((*si)->_pushed) {
00111         (*si)->pop(time);
00112         return;
00113       }
00114     }
00115   }
00116 
00117   bool _touched;
00118   bool _is_started;
00119   bool _pushed;
00120   double _net_time;
00121 };
00122 
00123 
00124 
00125 ////////////////////////////////////////////////////////////////////
00126 //     Function: PStatView::Constructor
00127 //       Access: Public
00128 //  Description:
00129 ////////////////////////////////////////////////////////////////////
00130 PStatView::
00131 PStatView() {
00132   _constraint = 0;
00133   _show_level = false;
00134   _all_collectors_known = false;
00135   _level_index = 0;
00136 }
00137 
00138 ////////////////////////////////////////////////////////////////////
00139 //     Function: PStatView::Destructor
00140 //       Access: Public
00141 //  Description:
00142 ////////////////////////////////////////////////////////////////////
00143 PStatView::
00144 ~PStatView() {
00145   clear_levels();
00146 }
00147 
00148 ////////////////////////////////////////////////////////////////////
00149 //     Function: PStatView::constrain
00150 //       Access: Public
00151 //  Description: Changes the focus of the View.  By default, the View
00152 //               reports the entire time for the frame, and all of the
00153 //               Collectors that are directly parented to "Frame".  By
00154 //               constraining the view to a particular collector, you
00155 //               cause the View to zoom in on that collector's data,
00156 //               reporting only the collector and its immediate
00157 //               parents.
00158 //
00159 //               When you constrain the view, you may also specify
00160 //               whether the view should show time data or level data
00161 //               for the indicated collector.  If level data, it
00162 //               reports the levels for the collector, and all of its
00163 //               children; otherwise, it collects the elapsed time.
00164 //
00165 //               Changing the constraint causes the current frame's
00166 //               data to become invalidated; you must then call
00167 //               set_to_frame() again to get any useful data out.
00168 ////////////////////////////////////////////////////////////////////
00169 void PStatView::
00170 constrain(int collector, bool show_level) {
00171   _constraint = collector;
00172   _show_level = show_level;
00173   clear_levels();
00174 }
00175 
00176 ////////////////////////////////////////////////////////////////////
00177 //     Function: PStatView::unconstrain
00178 //       Access: Public
00179 //  Description: Restores the view to the full frame.  This is
00180 //               equivalent to calling constrain(0).
00181 ////////////////////////////////////////////////////////////////////
00182 void PStatView::
00183 unconstrain() {
00184   constrain(0, false);
00185 }
00186 
00187 ////////////////////////////////////////////////////////////////////
00188 //     Function: PStatView::set_thread_data
00189 //       Access: Public
00190 //  Description:
00191 ////////////////////////////////////////////////////////////////////
00192 void PStatView::
00193 set_thread_data(const PStatThreadData *thread_data) {
00194   _thread_data = thread_data;
00195   _client_data = thread_data->get_client_data();
00196   clear_levels();
00197   _all_collectors_known = false;
00198 }
00199 
00200 ////////////////////////////////////////////////////////////////////
00201 //     Function: PStatView::set_to_frame
00202 //       Access: Public
00203 //  Description: Supplies the View with the data for the current
00204 //               frame.  This causes the View to update all of its
00205 //               internal data to reflect the frame's data, subject to
00206 //               the current constraint.
00207 //
00208 //               It is possible that calling this will increase the
00209 //               total number of reported levels (for instance, if
00210 //               this frame introduced a new collector that hadn't
00211 //               been active previously).  In this case, the caller
00212 //               must update its display or whatever to account for
00213 //               the new level.
00214 ////////////////////////////////////////////////////////////////////
00215 void PStatView::
00216 set_to_frame(const PStatFrameData &frame_data) {
00217   nassertv(!_thread_data.is_null());
00218   nassertv(!_client_data.is_null());
00219 
00220   if (_show_level) {
00221     update_level_data(frame_data);
00222   } else {
00223     update_time_data(frame_data);
00224   }
00225 }
00226 
00227 
00228 ////////////////////////////////////////////////////////////////////
00229 //     Function: PStatView::all_collectors_known
00230 //       Access: Public
00231 //  Description: After a call to set_to_frame(), this returns true if
00232 //               all collectors in the FrameData are known by the
00233 //               PStatsData object, or false if some are still unknown
00234 //               (even those that do not appear in the view).
00235 ////////////////////////////////////////////////////////////////////
00236 bool PStatView::
00237 all_collectors_known() const {
00238   return _all_collectors_known;
00239 }
00240 
00241 ////////////////////////////////////////////////////////////////////
00242 //     Function: PStatView::get_net_value
00243 //       Access: Public
00244 //  Description: Returns the total value accounted for by the frame (or
00245 //               by whatever Collector we are constrained to).  This
00246 //               is the sum of all of the individual levels'
00247 //               get_net_value() value.
00248 ////////////////////////////////////////////////////////////////////
00249 double PStatView::
00250 get_net_value() const {
00251   double net = 0.0;
00252   Levels::const_iterator li;
00253   for (li = _levels.begin(); li != _levels.end(); ++li) {
00254     net += (*li).second->_value_alone;
00255   }
00256 
00257   return net;
00258 }
00259 
00260 ////////////////////////////////////////////////////////////////////
00261 //     Function: PStatView::get_top_level
00262 //       Access: Public
00263 //  Description: Returns a pointer to the level that corresponds to
00264 //               the Collector we've constrained to.  This is the top
00265 //               of a graph of levels; typically the next level
00266 //               down--the children of this level--will be the levels
00267 //               you want to display to the user.
00268 ////////////////////////////////////////////////////////////////////
00269 const PStatViewLevel *PStatView::
00270 get_top_level() {
00271   return get_level(_constraint);
00272 }
00273 
00274 ////////////////////////////////////////////////////////////////////
00275 //     Function: PStatView::has_level
00276 //       Access: Public
00277 //  Description: Returns true if there is a level defined for the
00278 //               particular collector, false otherwise.
00279 ////////////////////////////////////////////////////////////////////
00280 bool PStatView::
00281 has_level(int collector) const {
00282   Levels::const_iterator li;
00283   li = _levels.find(collector);
00284   return (li != _levels.end());
00285 }
00286 
00287 ////////////////////////////////////////////////////////////////////
00288 //     Function: PStatView::get_level
00289 //       Access: Public
00290 //  Description: Returns a pointer to the level that corresponds to
00291 //               the indicated Collector.  If there is no such level
00292 //               in the view, one will be created--use with caution.
00293 //               Check has_level() first if you don't want this
00294 //               behavior.
00295 ////////////////////////////////////////////////////////////////////
00296 PStatViewLevel *PStatView::
00297 get_level(int collector) {
00298   Levels::const_iterator li;
00299   li = _levels.find(collector);
00300   if (li != _levels.end()) {
00301     return (*li).second;
00302   }
00303 
00304   PStatViewLevel *level = new PStatViewLevel;
00305   level->_collector = collector;
00306   level->_parent = NULL;
00307   _levels[collector] = level;
00308 
00309   reset_level(level);
00310   return level;
00311 }
00312 
00313 ////////////////////////////////////////////////////////////////////
00314 //     Function: PStatView::update_time_data
00315 //       Access: Private
00316 //  Description: The implementation of set_to_frame() for views that
00317 //               show elapsed time.
00318 ////////////////////////////////////////////////////////////////////
00319 void PStatView::
00320 update_time_data(const PStatFrameData &frame_data) {
00321   int num_events = frame_data.get_num_events();
00322 
00323   typedef pvector<FrameSample> Samples;
00324   Samples samples(_client_data->get_num_collectors());
00325 
00326   FrameSample::Started started;
00327 
00328   _all_collectors_known = true;
00329 
00330 
00331   // This tracks the set of samples we actually care about.
00332   typedef pset<int> GotSamples;
00333   GotSamples got_samples;
00334 
00335   int i;
00336   for (i = 0; i < num_events; i++) {
00337     int collector_index = frame_data.get_time_collector(i);
00338     bool is_start = frame_data.is_start(i);
00339 
00340     if (!_client_data->has_collector(collector_index)) {
00341       _all_collectors_known = false;
00342 
00343     } else {
00344       nassertv(collector_index >= 0 && collector_index < (int)samples.size());
00345 
00346       if (_client_data->get_child_distance(_constraint, collector_index) >= 0) {
00347         // Here's a data point we care about: anything at constraint
00348         // level or below.
00349         if (is_start == samples[collector_index]._is_started) {
00350           if (!is_start) {
00351             // A "stop" in the middle of a frame implies a "start"
00352             // since time 0 (that is, since the first data point in
00353             // the frame).
00354             samples[collector_index].data_point(frame_data.get_time(0), true, started);
00355             samples[collector_index].data_point(frame_data.get_time(i), is_start, started);
00356           } else {
00357             // An extra "start" for a collector that's already started
00358             // is an error.
00359             nout << "Unexpected data point for " 
00360                  << _client_data->get_collector_fullname(collector_index)
00361                  << "\n";
00362           }
00363         } else {
00364           samples[collector_index].data_point(frame_data.get_time(i), is_start, started);
00365           got_samples.insert(collector_index);
00366         }
00367       }
00368     }
00369   }
00370 
00371   // Make sure everything is stopped.
00372 
00373   Samples::iterator si;
00374   for (i = 0, si = samples.begin(); si != samples.end(); ++i, ++si) {
00375     if ((*si)._is_started) {
00376       (*si).data_point(frame_data.get_end(), false, started);
00377     }
00378   }
00379 
00380   nassertv(started.empty());
00381 
00382   bool any_new_levels = false;
00383 
00384   // Now match these samples we got up with those we already had in
00385   // the levels.
00386   Levels::iterator li, lnext;
00387   li = _levels.begin();
00388   while (li != _levels.end()) {
00389     // Be careful while traversing a container and calling functions
00390     // that could modify that container.
00391     lnext = li;
00392     ++lnext;
00393 
00394     PStatViewLevel *level = (*li).second;
00395     if (reset_level(level)) {
00396       any_new_levels = true;
00397     }
00398 
00399     int collector_index = level->_collector;
00400     GotSamples::iterator gi;
00401     gi = got_samples.find(collector_index);
00402     if (gi != got_samples.end()) {
00403       level->_value_alone = samples[collector_index]._net_time;
00404       got_samples.erase(gi);
00405     }
00406 
00407     li = lnext;
00408   }
00409 
00410   // Finally, any samples left over in the got_samples set are new
00411   // collectors that we need to add to the Levels list.
00412   if (!got_samples.empty()) {
00413     any_new_levels = true;
00414 
00415     GotSamples::const_iterator gi;
00416     for (gi = got_samples.begin(); gi != got_samples.end(); ++gi) {
00417       int collector_index = (*gi);
00418       PStatViewLevel *level = get_level(collector_index);
00419       level->_value_alone = samples[*gi]._net_time;
00420     }
00421   }
00422 
00423   if (any_new_levels) {
00424     _level_index++;
00425   }
00426 }
00427 
00428 ////////////////////////////////////////////////////////////////////
00429 //     Function: PStatView::update_level_data
00430 //       Access: Private
00431 //  Description: The implementation of set_to_frame() for views that
00432 //               show level values.
00433 ////////////////////////////////////////////////////////////////////
00434 void PStatView::
00435 update_level_data(const PStatFrameData &frame_data) {
00436   _all_collectors_known = true;
00437 
00438 
00439   // This tracks the set of level values we got.
00440   typedef pmap<int, double> GotValues;
00441   GotValues net_values;
00442 
00443   int i;
00444   int num_levels = frame_data.get_num_levels();
00445   for (i = 0; i < num_levels; i++) {
00446     int collector_index = frame_data.get_level_collector(i);
00447     double value = frame_data.get_level(i);
00448 
00449     if (!_client_data->has_collector(collector_index)) {
00450       _all_collectors_known = false;
00451 
00452     } else {
00453       if (_client_data->get_child_distance(_constraint, collector_index) >= 0) {
00454         net_values[collector_index] = value;
00455       }
00456     }
00457   }
00458 
00459   // Now that we've counted up the net level for each collector,
00460   // compute the level for each collector alone by subtracting out
00461   // each child from its parents.  If a parent has no data, nothing is
00462   // subtracted.
00463   GotValues alone_values = net_values;
00464 
00465   GotValues::iterator gi;
00466   for (gi = net_values.begin(); gi != net_values.end(); ++gi) {
00467     int collector_index = (*gi).first;
00468     double value = (*gi).second;
00469 
00470     // Walk up to the top, but stop when we find a parent with actual
00471     // data.
00472     while (collector_index != 0 && collector_index != _constraint) {
00473       const PStatCollectorDef &def =
00474         _client_data->get_collector_def(collector_index);
00475       int parent_index = def._parent_index;
00476       GotValues::iterator pi = alone_values.find(parent_index);
00477       if (pi != alone_values.end()) {
00478         // The parent has data; subtract it.
00479         (*pi).second -= value;
00480         break;
00481       }
00482       collector_index = parent_index;
00483     }
00484   }
00485 
00486 
00487   bool any_new_levels = false;
00488 
00489   // Now match these samples we got up with those we already had in
00490   // the levels.
00491   Levels::iterator li, lnext;
00492   li = _levels.begin();
00493   while (li != _levels.end()) {
00494     // Be careful while traversing a container and calling functions
00495     // that could modify that container.
00496     lnext = li;
00497     ++lnext;
00498 
00499     PStatViewLevel *level = (*li).second;
00500     if (reset_level(level)) {
00501       any_new_levels = true;
00502     }
00503 
00504     int collector_index = level->_collector;
00505     GotValues::iterator gi;
00506     gi = alone_values.find(collector_index);
00507     if (gi != alone_values.end()) {
00508       level->_value_alone = (*gi).second;
00509       alone_values.erase(gi);
00510     }
00511 
00512     li = lnext;
00513   }
00514 
00515   // Finally, any values left over in the alone_values set are new
00516   // collectors that we need to add to the Levels list.
00517   if (!alone_values.empty()) {
00518     any_new_levels = true;
00519 
00520     GotValues::const_iterator gi;
00521     for (gi = alone_values.begin(); gi != alone_values.end(); ++gi) {
00522       int collector_index = (*gi).first;
00523       PStatViewLevel *level = get_level(collector_index);
00524       level->_value_alone = (*gi).second;
00525     }
00526   }
00527 
00528   if (any_new_levels) {
00529     _level_index++;
00530   }
00531 }
00532 
00533 ////////////////////////////////////////////////////////////////////
00534 //     Function: PStatView::clear_levels
00535 //       Access: Private
00536 //  Description: Resets all the levels that have been defined so far.
00537 ////////////////////////////////////////////////////////////////////
00538 void PStatView::
00539 clear_levels() {
00540   Levels::iterator li;
00541   for (li = _levels.begin(); li != _levels.end(); ++li) {
00542     delete (*li).second;
00543   }
00544   _levels.clear();
00545 }
00546 
00547 ////////////////////////////////////////////////////////////////////
00548 //     Function: PStatView::reset_level
00549 //       Access: Private
00550 //  Description: Resets the total value of the Level to zero, and also
00551 //               makes sure it is parented to the right Level
00552 //               corresponding to its Collector's parent.  Since the
00553 //               client might change its mind from time to time about
00554 //               who the Collector is parented to, we have to update
00555 //               this dynamically.
00556 //
00557 //               Returns true if any change was made to the level's
00558 //               hierarchy, false otherwise.
00559 ////////////////////////////////////////////////////////////////////
00560 bool PStatView::
00561 reset_level(PStatViewLevel *level) {
00562   bool any_changed = false;
00563   level->_value_alone = 0.0;
00564 
00565   if (level->_collector == _constraint) {
00566     return false;
00567   }
00568 
00569   if (_client_data->has_collector(level->_collector)) {
00570     int parent_index =
00571       _client_data->get_collector_def(level->_collector)._parent_index;
00572 
00573     if (level->_parent == (PStatViewLevel *)NULL) {
00574       // This level didn't know its parent before, but now it does.
00575       PStatViewLevel *parent_level = get_level(parent_index);
00576       nassertr(parent_level != level, true);
00577 
00578       level->_parent = parent_level;
00579       parent_level->_children.push_back(level);
00580       parent_level->sort_children(_client_data);
00581       any_changed = true;
00582 
00583     } else if (level->_parent->_collector != parent_index) {
00584       // This level knew about its parent, but now it's something
00585       // different.
00586       PStatViewLevel *old_parent_level = level->_parent;
00587       nassertr(old_parent_level != level, true);
00588 
00589       if (parent_index != 0) {
00590         PStatViewLevel *new_parent_level = get_level(parent_index);
00591         nassertr(new_parent_level != level, true);
00592         level->_parent = new_parent_level;
00593         new_parent_level->_children.push_back(level);
00594         new_parent_level->sort_children(_client_data);
00595       } else {
00596         level->_parent = NULL;
00597       }
00598 
00599       PStatViewLevel::Children::iterator ci =
00600         find(old_parent_level->_children.begin(),
00601              old_parent_level->_children.end(),
00602              level);
00603 
00604       nassertr(ci != old_parent_level->_children.end(), true);
00605       old_parent_level->_children.erase(ci);
00606       any_changed = true;
00607     }
00608   }
00609 
00610   return any_changed;
00611 }
00612 
00613 
 All Classes Functions Variables Enumerations