Panda3D
pStatClient.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 pStatClient.cxx
10  * @author drose
11  * @date 2000-07-09
12  */
13 
14 #include "pStatClient.h"
15 
16 #ifdef DO_PSTATS
17 // This file only defines anything interesting if DO_PSTATS is defined.
18 
19 #include "pStatClientImpl.h"
22 #include "pStatCollector.h"
23 #include "pStatThread.h"
24 #include "config_pstatclient.h"
25 #include "pStatProperties.h"
26 #include "thread.h"
27 #include "clockObject.h"
28 #include "neverFreeMemory.h"
29 
30 using std::string;
31 
32 PStatCollector PStatClient::_heap_total_size_pcollector("System memory:Heap");
33 PStatCollector PStatClient::_heap_overhead_size_pcollector("System memory:Heap:Overhead");
34 PStatCollector PStatClient::_heap_single_size_pcollector("System memory:Heap:Single");
35 PStatCollector PStatClient::_heap_single_other_size_pcollector("System memory:Heap:Single:Other");
36 PStatCollector PStatClient::_heap_array_size_pcollector("System memory:Heap:Array");
37 PStatCollector PStatClient::_heap_array_other_size_pcollector("System memory:Heap:Array:Other");
38 PStatCollector PStatClient::_heap_external_size_pcollector("System memory:Heap:External");
39 PStatCollector PStatClient::_mmap_size_pcollector("System memory:MMap");
40 
41 PStatCollector PStatClient::_mmap_nf_unused_size_pcollector("System memory:MMap:NeverFree:Unused");
42 PStatCollector PStatClient::_mmap_dc_active_other_size_pcollector("System memory:MMap:NeverFree:Active:Other");
43 PStatCollector PStatClient::_mmap_dc_inactive_other_size_pcollector("System memory:MMap:NeverFree:Inactive:Other");
44 PStatCollector PStatClient::_pstats_pcollector("*:PStats");
45 PStatCollector PStatClient::_clock_wait_pcollector("Wait:Clock Wait:Sleep");
46 PStatCollector PStatClient::_clock_busy_wait_pcollector("Wait:Clock Wait:Spin");
47 PStatCollector PStatClient::_thread_block_pcollector("Wait:Thread block");
48 
49 PStatClient *PStatClient::_global_pstats = nullptr;
50 
51 
52 // This class is used to report memory usage per TypeHandle. We create one of
53 // these for each TypeHandle in the system.
54 class TypeHandleCollector {
55 public:
56  PStatCollector _mem_class[TypeHandle::MC_limit];
57 };
58 typedef pvector<TypeHandleCollector> TypeHandleCols;
59 static TypeHandleCols type_handle_cols;
60 
61 
62 /**
63  *
64  */
65 PStatClient::PerThreadData::
66 PerThreadData() {
67  _has_level = false;
68  _level = 0.0;
69  _nested_count = 0;
70 }
71 
72 /**
73  *
74  */
75 PStatClient::
76 PStatClient() :
77  _lock("PStatClient::_lock"),
78  _impl(nullptr)
79 {
80  _collectors = nullptr;
81  _collectors_size = 0;
82  _num_collectors = 0;
83 
84  _threads = nullptr;
85  _threads_size = 0;
86  _num_threads = 0;
87 
88  // We always have a collector at index 0 named "Frame". This tracks the
89  // total frame time and is the root of all other collectors. We have to
90  // make this one by hand since it's the root.
91  Collector *collector = new Collector(0, "Frame");
92  // collector->_def = new PStatCollectorDef(0, "Frame");
93  // collector->_def->_parent_index = 0;
94  // collector->_def->_suggested_color.set(0.5, 0.5, 0.5);
95  add_collector(collector);
96 
97  // The main thread is always at index 0.
98  make_thread(Thread::get_main_thread());
99 }
100 
101 /**
102  *
103  */
104 PStatClient::
105 ~PStatClient() {
106  disconnect();
107 }
108 
109 /**
110  * Sets the name of the client. This is reported to the PStatsServer, and
111  * will presumably be written in the title bar or something.
112  */
113 void PStatClient::
114 set_client_name(const string &name) {
115  get_impl()->set_client_name(name);
116 }
117 
118 /**
119  * Retrieves the name of the client as set.
120  */
121 string PStatClient::
122 get_client_name() const {
123  return get_impl()->get_client_name();
124 }
125 
126 /**
127  * Controls the number of packets that will be sent to the server. Normally,
128  * one packet is sent per frame, but this can flood the server with more
129  * packets than it can handle if the frame rate is especially good (e.g. if
130  * nothing is onscreen at the moment). Set this parameter to a reasonable
131  * number to prevent this from happening.
132  *
133  * This number specifies the maximum number of packets that will be sent to
134  * the server per second, per thread.
135  */
136 void PStatClient::
137 set_max_rate(double rate) {
138  get_impl()->set_max_rate(rate);
139 }
140 
141 /**
142  * Returns the maximum number of packets that will be sent to the server per
143  * second, per thread. See set_max_rate().
144  */
145 double PStatClient::
146 get_max_rate() const {
147  return get_impl()->get_max_rate();
148 }
149 
150 /**
151  * Returns the nth collector.
152  */
153 PStatCollector PStatClient::
154 get_collector(int index) const {
155  nassertr(index >= 0 && index < AtomicAdjust::get(_num_collectors), PStatCollector());
156  return PStatCollector((PStatClient *)this, index);
157 }
158 
159 /**
160  * Returns the name of the indicated collector.
161  */
162 string PStatClient::
163 get_collector_name(int index) const {
164  nassertr(index >= 0 && index < AtomicAdjust::get(_num_collectors), string());
165 
166  return get_collector_ptr(index)->get_name();
167 }
168 
169 /**
170  * Returns the "full name" of the indicated collector. This will be the
171  * concatenation of all of the collector's parents' names (except Frame) and
172  * the collector's own name.
173  */
174 string PStatClient::
175 get_collector_fullname(int index) const {
176  nassertr(index >= 0 && index < AtomicAdjust::get(_num_collectors), string());
177 
178  Collector *collector = get_collector_ptr(index);
179  int parent_index = collector->get_parent_index();
180  if (parent_index == 0) {
181  return collector->get_name();
182  } else {
183  return get_collector_fullname(parent_index) + ":" +
184  collector->get_name();
185  }
186 }
187 
188 /**
189  * Returns the nth thread.
190  */
191 PStatThread PStatClient::
192 get_thread(int index) const {
193  ReMutexHolder holder(_lock);
194  nassertr(index >= 0 && index < _num_threads, PStatThread());
195  return PStatThread((PStatClient *)this, index);
196 }
197 
198 /**
199  * Returns a handle to the client's Main thread. This is the thread that
200  * started the application.
201  */
202 PStatThread PStatClient::
203 get_main_thread() const {
204  return PStatThread((PStatClient *)this, 0);
205 }
206 
207 /**
208  * Returns a handle to the currently-executing thread. This is the thread
209  * that PStatCollectors will be counted in if they do not specify otherwise.
210  */
211 PStatThread PStatClient::
212 get_current_thread() const {
213  if (!client_is_connected()) {
214  // No need to make the relatively expensive call to
215  // Thread::get_current_thread() if we're not even connected.
216  return get_main_thread();
217  }
218  return PStatThread(Thread::get_current_thread(), (PStatClient *)this);
219 }
220 
221 /**
222  * Returns the time according to to the PStatClient's clock object. It keeps
223  * its own clock, instead of using the global clock object, so the stats won't
224  * get mucked up if you put the global clock in non-real-time mode or
225  * something.
226  */
227 double PStatClient::
228 get_real_time() const {
229  if (has_impl()) {
230  return _impl->get_real_time();
231  }
232  return 0.0f;
233 }
234 
235 /**
236  * A convenience function to call new_frame() on the global PStatClient's main
237  * thread, and any other threads with a sync_name of "Main".
238  */
239 void PStatClient::
240 main_tick() {
241  // We have code here to report the memory usage. We can't put this code
242  // inside the MemoryUsage class, where it fits a little better, simply
243  // because MemoryUsage is a very low-level class that doesn't know about
244  // PStatClient.
245 
246 #ifdef DO_MEMORY_USAGE
247  if (is_connected()) {
248  _heap_total_size_pcollector.set_level(MemoryUsage::get_total_size());
249  _heap_overhead_size_pcollector.set_level(MemoryUsage::get_panda_heap_overhead());
250  _heap_single_size_pcollector.set_level(MemoryUsage::get_panda_heap_single_size());
251  _heap_array_size_pcollector.set_level(MemoryUsage::get_panda_heap_array_size());
252  _heap_external_size_pcollector.set_level(MemoryUsage::get_external_size());
253 
254 
255  _mmap_size_pcollector.set_level(MemoryUsage::get_panda_mmap_size());
256 
257  TypeRegistry *type_reg = TypeRegistry::ptr();
258  int num_typehandles = type_reg->get_num_typehandles();
259 
260  while ((int)type_handle_cols.size() < num_typehandles) {
261  type_handle_cols.push_back(TypeHandleCollector());
262  }
263 
264  size_t single_total_usage = 0;
265  size_t array_total_usage = 0;
266  size_t dc_active_total_usage = 0;
267  size_t dc_inactive_total_usage = 0;
268  int i;
269  for (i = 0; i < num_typehandles; ++i) {
270  TypeHandle type = type_reg->get_typehandle(i);
271  for (int mi = 0; mi < (int)TypeHandle::MC_limit; ++mi) {
272  TypeHandle::MemoryClass mc = (TypeHandle::MemoryClass)mi;
273  size_t usage = type.get_memory_usage(mc);
274 
275  switch (mc) {
276  case TypeHandle::MC_singleton:
277  single_total_usage += usage;
278  break;
279 
280  case TypeHandle::MC_array:
281  array_total_usage += usage;
282  break;
283 
284  case TypeHandle::MC_deleted_chain_active:
285  dc_active_total_usage += usage;
286  break;
287 
288  case TypeHandle::MC_deleted_chain_inactive:
289  dc_inactive_total_usage += usage;
290  break;
291 
292  case TypeHandle::MC_limit:
293  // Not used.
294  break;
295  }
296  }
297  }
298  size_t min_usage = (single_total_usage + array_total_usage + dc_active_total_usage + dc_inactive_total_usage) / 1024;
299  if (!pstats_mem_other) {
300  min_usage = 0;
301  }
302  size_t single_other_usage = single_total_usage;
303  size_t array_other_usage = array_total_usage;
304  size_t dc_active_other_usage = dc_active_total_usage;
305  size_t dc_inactive_other_usage = dc_inactive_total_usage;
306 
307  for (i = 0; i < num_typehandles; ++i) {
308  TypeHandle type = type_reg->get_typehandle(i);
309  for (int mi = 0; mi < (int)TypeHandle::MC_limit; ++mi) {
310  TypeHandle::MemoryClass mc = (TypeHandle::MemoryClass)mi;
311  PStatCollector &col = type_handle_cols[i]._mem_class[mi];
312  size_t usage = type.get_memory_usage(mc);
313  if (usage > min_usage || col.is_valid()) {
314  // We have some memory usage on this TypeHandle. See if we have a
315  // collector for it.
316  if (!col.is_valid()) {
317  const char *category = "";
318  switch (mc) {
319  case TypeHandle::MC_singleton:
320  category = "Heap:Single";
321  break;
322 
323  case TypeHandle::MC_array:
324  category = "Heap:Array";
325  break;
326 
327  case TypeHandle::MC_deleted_chain_active:
328  category = "MMap:NeverFree:Active";
329  break;
330 
331  case TypeHandle::MC_deleted_chain_inactive:
332  category = "MMap:NeverFree:Inactive";
333  break;
334 
335  case TypeHandle::MC_limit:
336  // Not used.
337  break;
338  }
339  std::ostringstream strm;
340  strm << "System memory:" << category << ":" << type;
341  col = PStatCollector(strm.str());
342  }
343  col.set_level(usage);
344 
345  switch (mc) {
346  case TypeHandle::MC_singleton:
347  single_other_usage -= usage;
348  break;
349 
350  case TypeHandle::MC_array:
351  array_other_usage -= usage;
352  break;
353 
354  case TypeHandle::MC_deleted_chain_active:
355  dc_active_other_usage -= usage;
356  break;
357 
358  case TypeHandle::MC_deleted_chain_inactive:
359  dc_inactive_other_usage -= usage;
360  break;
361 
362  case TypeHandle::MC_limit:
363  // Not used.
364  break;
365  }
366  }
367  }
368  }
369 
370  _mmap_nf_unused_size_pcollector.set_level(NeverFreeMemory::get_total_unused());
371 
372  // The remaining amount--all collectors smaller than 0.1% of the total--go
373  // into "other".
374  _heap_single_other_size_pcollector.set_level(single_other_usage);
375  _heap_array_other_size_pcollector.set_level(array_other_usage);
376  _mmap_dc_active_other_size_pcollector.set_level(dc_active_other_usage);
377  _mmap_dc_inactive_other_size_pcollector.set_level(dc_inactive_other_usage);
378  }
379 #endif // DO_MEMORY_USAGE
380 
381  get_global_pstats()->client_main_tick();
382 }
383 
384 /**
385  * A convenience function to call new_frame() on any threads with the
386  * indicated sync_name
387  */
388 void PStatClient::
389 thread_tick(const string &sync_name) {
390  get_global_pstats()->client_thread_tick(sync_name);
391 }
392 
393 /**
394  * A convenience function to call new_frame() on the given PStatClient's main
395  * thread, and any other threads with a sync_name of "Main".
396  */
397 void PStatClient::
398 client_main_tick() {
399  ReMutexHolder holder(_lock);
400  if (has_impl()) {
401  if (!_impl->client_is_connected()) {
402  client_disconnect();
403  return;
404  }
405 
406  _impl->client_main_tick();
407 
408  MultiThingsByName::const_iterator ni =
409  _threads_by_sync_name.find("Main");
410  if (ni != _threads_by_sync_name.end()) {
411  const vector_int &indices = (*ni).second;
412  for (vector_int::const_iterator vi = indices.begin();
413  vi != indices.end();
414  ++vi) {
415  _impl->new_frame(*vi);
416  }
417  }
418  }
419 }
420 
421 /**
422  * A convenience function to call new_frame() on all of the threads with the
423  * indicated sync name.
424  */
425 void PStatClient::
426 client_thread_tick(const string &sync_name) {
427  ReMutexHolder holder(_lock);
428 
429  if (has_impl()) {
430  MultiThingsByName::const_iterator ni =
431  _threads_by_sync_name.find(sync_name);
432  if (ni != _threads_by_sync_name.end()) {
433  const vector_int &indices = (*ni).second;
434  for (vector_int::const_iterator vi = indices.begin();
435  vi != indices.end();
436  ++vi) {
437  _impl->new_frame(*vi);
438  }
439  }
440  }
441 }
442 
443 /**
444  * The nonstatic implementation of connect().
445  */
446 bool PStatClient::
447 client_connect(string hostname, int port) {
448  ReMutexHolder holder(_lock);
449  client_disconnect();
450  return get_impl()->client_connect(hostname, port);
451 }
452 
453 /**
454  * The nonstatic implementation of disconnect().
455  */
456 void PStatClient::
457 client_disconnect() {
458  ReMutexHolder holder(_lock);
459  if (has_impl()) {
460  _impl->client_disconnect();
461  delete _impl;
462  _impl = nullptr;
463  }
464 
465  ThreadPointer *threads = (ThreadPointer *)_threads;
466  for (int ti = 0; ti < _num_threads; ++ti) {
467  InternalThread *thread = threads[ti];
468  thread->_frame_number = 0;
469  thread->_is_active = false;
470  thread->_next_packet = 0.0;
471  thread->_frame_data.clear();
472  }
473 
474  CollectorPointer *collectors = (CollectorPointer *)_collectors;
475  for (int ci = 0; ci < _num_collectors; ++ci) {
476  Collector *collector = collectors[ci];
477  PerThread::iterator ii;
478  for (ii = collector->_per_thread.begin();
479  ii != collector->_per_thread.end();
480  ++ii) {
481  (*ii)._nested_count = 0;
482  }
483  }
484 }
485 
486 /**
487  * The nonstatic implementation of is_connected().
488  */
489 bool PStatClient::
490 client_is_connected() const {
491  return has_impl() && _impl->client_is_connected();
492 }
493 
494 /**
495  * Resumes the PStatClient after the simulation has been paused for a while.
496  * This allows the stats to continue exactly where it left off, instead of
497  * leaving a big gap that would represent a chug.
498  */
499 void PStatClient::
500 client_resume_after_pause() {
501  if (has_impl()) {
502  _impl->client_resume_after_pause();
503  }
504 }
505 
506 /**
507  * Returns a pointer to the global PStatClient object. It's legal to declare
508  * your own PStatClient locally, but it's also convenient to have a global one
509  * that everyone can register with. This is the global one.
510  */
511 PStatClient *PStatClient::
512 get_global_pstats() {
513  if (_global_pstats == nullptr) {
514  _global_pstats = new PStatClient;
515 
516  ClockObject::_start_clock_wait = start_clock_wait;
517  ClockObject::_start_clock_busy_wait = start_clock_busy_wait;
518  ClockObject::_stop_clock_wait = stop_clock_wait;
519  }
520  return _global_pstats;
521 }
522 
523 /**
524  * Creates the PStatClientImpl class for this PStatClient.
525  */
526 void PStatClient::
527 make_impl() const {
528  _impl = new PStatClientImpl((PStatClient *)this);
529 }
530 
531 /**
532  * Returns a PStatCollector suitable for measuring categories with the
533  * indicated name. This is normally called by a PStatCollector constructor.
534  *
535  * The name may contain colons; if it does, it specifies a relative path to
536  * the client indicated by the parent index.
537  */
538 PStatCollector PStatClient::
539 make_collector_with_relname(int parent_index, string relname) {
540  ReMutexHolder holder(_lock);
541 
542  if (relname.empty()) {
543  relname = "Unnamed";
544  }
545 
546  // Skip any colons at the beginning of the name.
547  size_t start = 0;
548  while (start < relname.size() && relname[start] == ':') {
549  start++;
550  }
551 
552  // If the name contains a colon (after the initial colon), it means we are
553  // making a nested collector.
554  size_t colon = relname.find(':', start);
555  while (colon != string::npos) {
556  string parent_name = relname.substr(start, colon - start);
557  PStatCollector parent_collector =
558  make_collector_with_name(parent_index, parent_name);
559  parent_index = parent_collector._index;
560  relname = relname.substr(colon + 1);
561  start = 0;
562  colon = relname.find(':');
563  }
564 
565  string name = relname.substr(start);
566  return make_collector_with_name(parent_index, name);
567 }
568 
569 /**
570  * Returns a PStatCollector suitable for measuring categories with the
571  * indicated name. This is normally called by a PStatCollector constructor.
572  *
573  * The name should not contain colons.
574  */
575 PStatCollector PStatClient::
576 make_collector_with_name(int parent_index, const string &name) {
577  ReMutexHolder holder(_lock);
578 
579  nassertr(parent_index >= 0 && parent_index < _num_collectors,
580  PStatCollector());
581 
582  Collector *parent = get_collector_ptr(parent_index);
583 
584  // A special case: if we asked for a child the same name as its parent, we
585  // really meant the parent. That is, "Frame:Frame" is really the same
586  // collector as "Frame".
587  if (parent->get_name() == name) {
588  return PStatCollector(this, parent_index);
589  }
590 
591  ThingsByName::const_iterator ni = parent->_children.find(name);
592 
593  if (ni != parent->_children.end()) {
594  // We already had a collector by this name; return it.
595  int index = (*ni).second;
596  nassertr(index >= 0 && index < _num_collectors, PStatCollector());
597  return PStatCollector(this, (*ni).second);
598  }
599 
600  // Create a new collector for this name.
601  int new_index = _num_collectors;
602  parent->_children.insert(ThingsByName::value_type(name, new_index));
603 
604  Collector *collector = new Collector(parent_index, name);
605  // collector->_def = new PStatCollectorDef(new_index, name);
606  // collector->_def->set_parent(*_collectors[parent_index]._def);
607  // initialize_collector_def(this, collector->_def);
608 
609  // We need one PerThreadData for each thread.
610  while ((int)collector->_per_thread.size() < _num_threads) {
611  collector->_per_thread.push_back(PerThreadData());
612  }
613  add_collector(collector);
614 
615  return PStatCollector(this, new_index);
616 }
617 
618 /**
619  * Similar to get_current_thread, but does not grab the lock.
620  */
621 PStatThread PStatClient::
622 do_get_current_thread() const {
624  int thread_index = thread->get_pstats_index();
625  if (thread_index != -1) {
626  return PStatThread((PStatClient *)this, thread_index);
627  }
628 
629  // This is the first time we have encountered this current Thread. Make a
630  // new PStatThread object for it.
631  return ((PStatClient *)this)->do_make_thread(thread);
632 }
633 
634 /**
635  * Returns a PStatThread for the indicated Panda Thread object. This is
636  * normally called by a PStatThread constructor.
637  */
638 PStatThread PStatClient::
639 make_thread(Thread *thread) {
640  ReMutexHolder holder(_lock);
641  return do_make_thread(thread);
642 }
643 
644 /**
645  * As above, but assumes the lock is already held.
646  */
647 PStatThread PStatClient::
648 do_make_thread(Thread *thread) {
649  int thread_index = thread->get_pstats_index();
650  if (thread_index != -1) {
651  return PStatThread((PStatClient *)this, thread_index);
652  }
653 
654  MultiThingsByName::const_iterator ni =
655  _threads_by_name.find(thread->get_name());
656 
657  if (ni != _threads_by_name.end()) {
658  // We have seen a thread with this name before. Can we re-use any of
659  // them?
660  const vector_int &indices = (*ni).second;
661  for (vector_int::const_iterator vi = indices.begin();
662  vi != indices.end();
663  ++vi) {
664  int index = (*vi);
665  nassertr(index >= 0 && index < _num_threads, PStatThread());
666  ThreadPointer *threads = (ThreadPointer *)_threads;
667  if (threads[index]->_thread.was_deleted() &&
668  threads[index]->_sync_name == thread->get_sync_name()) {
669  // Yes, re-use this one.
670  threads[index]->_thread = thread;
671  thread->set_pstats_index(index);
672  thread->set_pstats_callback(this);
673  return PStatThread(this, index);
674  }
675  }
676  }
677 
678  // Create a new PStatsThread for this thread pointer.
679  int new_index = _num_threads;
680  thread->set_pstats_index(new_index);
681  thread->set_pstats_callback(this);
682 
683  InternalThread *pthread = new InternalThread(thread);
684  add_thread(pthread);
685 
686  return PStatThread(this, new_index);
687 }
688 
689 /**
690  * Returns a PStatThread representing the GPU. This is normally called by the
691  * GSG only.
692  */
693 PStatThread PStatClient::
694 make_gpu_thread(const string &name) {
695  ReMutexHolder holder(_lock);
696  int new_index = _num_threads;
697 
698  InternalThread *pthread = new InternalThread(name, "GPU");
699  add_thread(pthread);
700 
701  return PStatThread(this, new_index);
702 }
703 
704 /**
705  * Returns true if the indicated collector/thread combination is active, and
706  * we are transmitting stats data, or false otherwise.
707  *
708  * Normally you would not use this interface directly; instead, call
709  * PStatCollector::is_active().
710  */
711 bool PStatClient::
712 is_active(int collector_index, int thread_index) const {
713  nassertr(collector_index >= 0 && collector_index < AtomicAdjust::get(_num_collectors), false);
714  nassertr(thread_index >= 0 && thread_index < AtomicAdjust::get(_num_threads), false);
715 
716  return (client_is_connected() &&
717  get_collector_ptr(collector_index)->is_active() &&
718  get_thread_ptr(thread_index)->_is_active);
719 }
720 
721 /**
722  * Returns true if the indicated collector/thread combination has been
723  * started, or false otherwise.
724  *
725  * Normally you would not use this interface directly; instead, call
726  * PStatCollector::is_started().
727  */
728 bool PStatClient::
729 is_started(int collector_index, int thread_index) const {
730  nassertr(collector_index >= 0 && collector_index < AtomicAdjust::get(_num_collectors), false);
731  nassertr(thread_index >= 0 && thread_index < AtomicAdjust::get(_num_threads), false);
732 
733  Collector *collector = get_collector_ptr(collector_index);
734  InternalThread *thread = get_thread_ptr(thread_index);
735 
736  if (client_is_connected() && collector->is_active() && thread->_is_active) {
737  LightMutexHolder holder(thread->_thread_lock);
738  if (collector->_per_thread[thread_index]._nested_count == 0) {
739  // Not started.
740  return false;
741  }
742  // Started.
743  return true;
744  }
745 
746  // Not even connected.
747  return false;
748 }
749 
750 /**
751  * Marks the indicated collector index as started. Normally you would not use
752  * this interface directly; instead, call PStatCollector::start().
753  */
754 void PStatClient::
755 start(int collector_index, int thread_index) {
756  if (!client_is_connected()) {
757  return;
758  }
759 
760 #ifdef _DEBUG
761  nassertv(collector_index >= 0 && collector_index < AtomicAdjust::get(_num_collectors));
762  nassertv(thread_index >= 0 && thread_index < AtomicAdjust::get(_num_threads));
763 #endif
764 
765  Collector *collector = get_collector_ptr(collector_index);
766  InternalThread *thread = get_thread_ptr(thread_index);
767 
768  if (collector->is_active() && thread->_is_active) {
769  LightMutexHolder holder(thread->_thread_lock);
770  if (collector->_per_thread[thread_index]._nested_count == 0) {
771  // This collector wasn't already started in this thread; record a new
772  // data point.
773  if (thread->_thread_active) {
774  thread->_frame_data.add_start(collector_index, get_real_time());
775  }
776  }
777  collector->_per_thread[thread_index]._nested_count++;
778  }
779 }
780 
781 /**
782  * Marks the indicated collector index as started. Normally you would not use
783  * this interface directly; instead, call PStatCollector::start().
784  */
785 void PStatClient::
786 start(int collector_index, int thread_index, double as_of) {
787  if (!client_is_connected()) {
788  return;
789  }
790 
791 #ifdef _DEBUG
792  nassertv(collector_index >= 0 && collector_index < AtomicAdjust::get(_num_collectors));
793  nassertv(thread_index >= 0 && thread_index < AtomicAdjust::get(_num_threads));
794 #endif
795 
796  Collector *collector = get_collector_ptr(collector_index);
797  InternalThread *thread = get_thread_ptr(thread_index);
798 
799  if (collector->is_active() && thread->_is_active) {
800  LightMutexHolder holder(thread->_thread_lock);
801  if (collector->_per_thread[thread_index]._nested_count == 0) {
802  // This collector wasn't already started in this thread; record a new
803  // data point.
804  if (thread->_thread_active) {
805  thread->_frame_data.add_start(collector_index, as_of);
806  }
807  }
808  collector->_per_thread[thread_index]._nested_count++;
809  }
810 }
811 
812 /**
813  * Marks the indicated collector index as stopped. Normally you would not use
814  * this interface directly; instead, call PStatCollector::stop().
815  */
816 void PStatClient::
817 stop(int collector_index, int thread_index) {
818  if (!client_is_connected()) {
819  return;
820  }
821 
822 #ifdef _DEBUG
823  nassertv(collector_index >= 0 && collector_index < AtomicAdjust::get(_num_collectors));
824  nassertv(thread_index >= 0 && thread_index < AtomicAdjust::get(_num_threads));
825 #endif
826 
827  Collector *collector = get_collector_ptr(collector_index);
828  InternalThread *thread = get_thread_ptr(thread_index);
829 
830  if (collector->is_active() && thread->_is_active) {
831  LightMutexHolder holder(thread->_thread_lock);
832  if (collector->_per_thread[thread_index]._nested_count == 0) {
833  if (pstats_cat.is_debug()) {
834  pstats_cat.debug()
835  << "Collector " << get_collector_fullname(collector_index)
836  << " was already stopped in thread " << get_thread_name(thread_index)
837  << "!\n";
838  }
839  return;
840  }
841 
842  collector->_per_thread[thread_index]._nested_count--;
843 
844  if (collector->_per_thread[thread_index]._nested_count == 0) {
845  // This collector has now been completely stopped; record a new data
846  // point.
847  if (thread->_thread_active) {
848  thread->_frame_data.add_stop(collector_index, get_real_time());
849  }
850  }
851  }
852 }
853 
854 /**
855  * Marks the indicated collector index as stopped. Normally you would not use
856  * this interface directly; instead, call PStatCollector::stop().
857  */
858 void PStatClient::
859 stop(int collector_index, int thread_index, double as_of) {
860  if (!client_is_connected()) {
861  return;
862  }
863 
864 #ifdef _DEBUG
865  nassertv(collector_index >= 0 && collector_index < AtomicAdjust::get(_num_collectors));
866  nassertv(thread_index >= 0 && thread_index < AtomicAdjust::get(_num_threads));
867 #endif
868 
869  Collector *collector = get_collector_ptr(collector_index);
870  InternalThread *thread = get_thread_ptr(thread_index);
871 
872  if (collector->is_active() && thread->_is_active) {
873  LightMutexHolder holder(thread->_thread_lock);
874  if (collector->_per_thread[thread_index]._nested_count == 0) {
875  if (pstats_cat.is_debug()) {
876  pstats_cat.debug()
877  << "Collector " << get_collector_fullname(collector_index)
878  << " was already stopped in thread " << get_thread_name(thread_index)
879  << "!\n";
880  }
881  return;
882  }
883 
884  collector->_per_thread[thread_index]._nested_count--;
885 
886  if (collector->_per_thread[thread_index]._nested_count == 0) {
887  // This collector has now been completely stopped; record a new data
888  // point.
889  thread->_frame_data.add_stop(collector_index, as_of);
890  }
891  }
892 }
893 
894 /**
895  * Removes the level value from the indicated collector. The collector will
896  * no longer be reported as having any particular level value.
897  *
898  * Normally you would not use this interface directly; instead, call
899  * PStatCollector::clear_level().
900  */
901 void PStatClient::
902 clear_level(int collector_index, int thread_index) {
903  if (!client_is_connected()) {
904  return;
905  }
906 
907 #ifdef _DEBUG
908  nassertv(collector_index >= 0 && collector_index < AtomicAdjust::get(_num_collectors));
909  nassertv(thread_index >= 0 && thread_index < AtomicAdjust::get(_num_threads));
910 #endif
911 
912  Collector *collector = get_collector_ptr(collector_index);
913  InternalThread *thread = get_thread_ptr(thread_index);
914  LightMutexHolder holder(thread->_thread_lock);
915 
916  collector->_per_thread[thread_index]._has_level = true;
917  collector->_per_thread[thread_index]._level = 0.0;
918 }
919 
920 /**
921  * Sets the level value for the indicated collector to the given amount.
922  *
923  * Normally you would not use this interface directly; instead, call
924  * PStatCollector::set_level().
925  */
926 void PStatClient::
927 set_level(int collector_index, int thread_index, double level) {
928  if (!client_is_connected()) {
929  return;
930  }
931 
932 #ifdef _DEBUG
933  nassertv(collector_index >= 0 && collector_index < AtomicAdjust::get(_num_collectors));
934  nassertv(thread_index >= 0 && thread_index < AtomicAdjust::get(_num_threads));
935 #endif
936 
937  Collector *collector = get_collector_ptr(collector_index);
938  InternalThread *thread = get_thread_ptr(thread_index);
939 
940  // We don't want to condition this on whether the client is already
941  // connected or the collector is already active, since we might connect the
942  // client later, and we will want to have an accurate value at that time.
943  LightMutexHolder holder(thread->_thread_lock);
944 
945  level *= collector->get_def(this, collector_index)->_factor;
946 
947  collector->_per_thread[thread_index]._has_level = true;
948  collector->_per_thread[thread_index]._level = level;
949 }
950 
951 /**
952  * Adds the given value (which may be negative) to the current value for the
953  * given collector. If the collector does not already have a level value, it
954  * is initialized to 0.
955  *
956  * Normally you would not use this interface directly; instead, call
957  * PStatCollector::add_level().
958  */
959 void PStatClient::
960 add_level(int collector_index, int thread_index, double increment) {
961  if (!client_is_connected()) {
962  return;
963  }
964 
965 #ifdef _DEBUG
966  nassertv(collector_index >= 0 && collector_index < AtomicAdjust::get(_num_collectors));
967  nassertv(thread_index >= 0 && thread_index < AtomicAdjust::get(_num_threads));
968 #endif
969 
970  Collector *collector = get_collector_ptr(collector_index);
971  InternalThread *thread = get_thread_ptr(thread_index);
972  LightMutexHolder holder(thread->_thread_lock);
973 
974  increment *= collector->get_def(this, collector_index)->_factor;
975 
976  collector->_per_thread[thread_index]._has_level = true;
977  collector->_per_thread[thread_index]._level += increment;
978 }
979 
980 /**
981  * Returns the current level value of the given collector. Returns 0.0 if the
982  * pstats client is not connected.
983  *
984  * Normally you would not use this interface directly; instead, call
985  * PStatCollector::get_level().
986  */
987 double PStatClient::
988 get_level(int collector_index, int thread_index) const {
989  if (!client_is_connected()) {
990  return 0.0;
991  }
992 
993 #ifdef _DEBUG
994  nassertr(collector_index >= 0 && collector_index < AtomicAdjust::get(_num_collectors), 0.0f);
995  nassertr(thread_index >= 0 && thread_index < AtomicAdjust::get(_num_threads), 0.0f);
996 #endif
997 
998  Collector *collector = get_collector_ptr(collector_index);
999  InternalThread *thread = get_thread_ptr(thread_index);
1000  LightMutexHolder holder(thread->_thread_lock);
1001 
1002  double factor = collector->get_def(this, collector_index)->_factor;
1003 
1004  return collector->_per_thread[thread_index]._level / factor;
1005 }
1006 
1007 /**
1008  * This function is added as a hook into ClockObject, so that we may time the
1009  * delay for ClockObject::wait_until(), used for certain special clock modes.
1010  *
1011  * This callback is a hack around the fact that we can't let the ClockObject
1012  * directly create a PStatCollector, because the pstatclient module depends on
1013  * putil.
1014  */
1015 void PStatClient::
1016 start_clock_wait() {
1017  _clock_wait_pcollector.start();
1018 }
1019 
1020 /**
1021  * This function is added as a hook into ClockObject, so that we may time the
1022  * delay for ClockObject::wait_until(), used for certain special clock modes.
1023  *
1024  * This callback is a hack around the fact that we can't let the ClockObject
1025  * directly create a PStatCollector, because the pstatclient module depends on
1026  * putil.
1027  */
1028 void PStatClient::
1029 start_clock_busy_wait() {
1030  _clock_wait_pcollector.stop();
1031  _clock_busy_wait_pcollector.start();
1032 }
1033 
1034 /**
1035  * This function is added as a hook into ClockObject, so that we may time the
1036  * delay for ClockObject::wait_until(), used for certain special clock modes.
1037  *
1038  * This callback is a hack around the fact that we can't let the ClockObject
1039  * directly create a PStatCollector, because the pstatclient module depends on
1040  * putil.
1041  */
1042 void PStatClient::
1043 stop_clock_wait() {
1044  _clock_busy_wait_pcollector.stop();
1045 }
1046 
1047 /**
1048  * Adds a new Collector entry to the _collectors array, in a thread-safe
1049  * manner. Assumes _lock is already held.
1050  */
1051 void PStatClient::
1052 add_collector(PStatClient::Collector *collector) {
1053  if (_num_collectors >= _collectors_size) {
1054  // We need to grow the array. We have to be careful here, because there
1055  // might be clients accessing the array right now who are not protected by
1056  // the lock.
1057  int new_collectors_size = (_collectors_size == 0) ? 128 : _collectors_size * 2;
1058  CollectorPointer *new_collectors = new CollectorPointer[new_collectors_size];
1059  if (_collectors != nullptr) {
1060  memcpy(new_collectors, _collectors, _num_collectors * sizeof(CollectorPointer));
1061  }
1062  AtomicAdjust::set_ptr(_collectors, new_collectors);
1063  AtomicAdjust::set(_collectors_size, new_collectors_size);
1064 
1065  // Now, we still have the old array, which we allow to leak. We should
1066  // delete it, but there might be a thread out there that's still trying to
1067  // access it, so we can't safely delete it; and it doesn't really matter
1068  // much, since it's not a big leak. (We will only reallocate the array so
1069  // many times in an application, and then no more.)
1070 
1071  new_collectors[_num_collectors] = collector;
1072  AtomicAdjust::inc(_num_collectors);
1073 
1074  } else {
1075  CollectorPointer *collectors = (CollectorPointer *)_collectors;
1076  collectors[_num_collectors] = collector;
1077  AtomicAdjust::inc(_num_collectors);
1078  }
1079 }
1080 
1081 /**
1082  * Adds a new InternalThread entry to the _threads array, in a thread-safe
1083  * manner. Assumes _lock is already held.
1084  */
1085 void PStatClient::
1086 add_thread(PStatClient::InternalThread *thread) {
1087  _threads_by_name[thread->_name].push_back(_num_threads);
1088  _threads_by_sync_name[thread->_sync_name].push_back(_num_threads);
1089 
1090  if (_num_threads >= _threads_size) {
1091  // We need to grow the array. We have to be careful here, because there
1092  // might be clients accessing the array right now who are not protected by
1093  // the lock.
1094  int new_threads_size = (_threads_size == 0) ? 128 : _threads_size * 2;
1095  ThreadPointer *new_threads = new ThreadPointer[new_threads_size];
1096  if (_threads != nullptr) {
1097  memcpy(new_threads, _threads, _num_threads * sizeof(ThreadPointer));
1098  }
1099  // We assume that assignment to a pointer and to an int are each atomic.
1100  AtomicAdjust::set_ptr(_threads, new_threads);
1101  AtomicAdjust::set(_threads_size, new_threads_size);
1102 
1103  // Now, we still have the old array, which we allow to leak. We should
1104  // delete it, but there might be a thread out there that's still trying to
1105  // access it, so we can't safely delete it; and it doesn't really matter
1106  // much, since it's not a big leak. (We will only reallocate the array so
1107  // many times in an application, and then no more.)
1108 
1109  new_threads[_num_threads] = thread;
1110 
1111  } else {
1112  ThreadPointer *threads = (ThreadPointer *)_threads;
1113  threads[_num_threads] = thread;
1114  }
1115 
1116  AtomicAdjust::inc(_num_threads);
1117 
1118  // We need an additional PerThreadData for this thread in all of the
1119  // collectors.
1120  CollectorPointer *collectors = (CollectorPointer *)_collectors;
1121  for (int ci = 0; ci < _num_collectors; ++ci) {
1122  Collector *collector = collectors[ci];
1123  collector->_per_thread.push_back(PerThreadData());
1124  nassertv((int)collector->_per_thread.size() == _num_threads);
1125  }
1126 }
1127 
1128 /**
1129  * Called when the thread is deactivated (swapped for another running thread).
1130  * This is intended to provide a callback hook for PStats to assign time to
1131  * individual threads properly, particularly in the SIMPLE_THREADS case.
1132  */
1133 void PStatClient::
1134 deactivate_hook(Thread *thread) {
1135  // We shouldn't use a mutex here, because this code is only called during
1136  // the SIMPLE_THREADS case, so a mutex isn't necessary; and because we are
1137  // called during a context switch, so a mutex might be dangerous.
1138  if (_impl == nullptr) {
1139  return;
1140  }
1141  int thread_index = thread->get_pstats_index();
1142  InternalThread *ithread = get_thread_ptr(thread_index);
1143 
1144  if (ithread->_thread_active) {
1145  // Start _thread_block_pcollector, by hand, being careful not to grab any
1146  // mutexes while we do it.
1147  double now = _impl->get_real_time();
1148  ithread->_frame_data.add_start(_thread_block_pcollector.get_index(), now);
1149  ithread->_thread_active = false;
1150  }
1151 }
1152 
1153 /**
1154  * Called when the thread is activated (resumes execution). This is intended
1155  * to provide a callback hook for PStats to assign time to individual threads
1156  * properly, particularly in the SIMPLE_THREADS case.
1157  */
1158 void PStatClient::
1159 activate_hook(Thread *thread) {
1160  // We shouldn't use a mutex here, because this code is only called during
1161  // the SIMPLE_THREADS case, so a mutex isn't necessary; and because we are
1162  // called during a context switch, so a mutex might be dangerous.
1163  if (_impl == nullptr) {
1164  return;
1165  }
1166 
1167  InternalThread *ithread = get_thread_ptr(thread->get_pstats_index());
1168 
1169  if (!ithread->_thread_active) {
1170  double now = _impl->get_real_time();
1171  ithread->_frame_data.add_stop(_thread_block_pcollector.get_index(), now);
1172  ithread->_thread_active = true;
1173  }
1174 }
1175 
1176 /**
1177  * Creates the new PStatCollectorDef for this collector.
1178  */
1179 void PStatClient::Collector::
1180 make_def(const PStatClient *client, int this_index) {
1181  ReMutexHolder holder(client->_lock);
1182  if (_def == nullptr) {
1183  _def = new PStatCollectorDef(this_index, _name);
1184  if (_parent_index != this_index) {
1185  const PStatCollectorDef *parent_def =
1186  client->get_collector_def(_parent_index);
1187  _def->set_parent(*parent_def);
1188  }
1189  initialize_collector_def(client, _def);
1190  }
1191 }
1192 
1193 /**
1194  *
1195  */
1196 PStatClient::InternalThread::
1197 InternalThread(Thread *thread) :
1198  _thread(thread),
1199  _name(thread->get_name()),
1200  _sync_name(thread->get_sync_name()),
1201  _is_active(false),
1202  _frame_number(0),
1203  _next_packet(0.0),
1204  _thread_active(true),
1205  _thread_lock(string("PStatClient::InternalThread ") + thread->get_name())
1206 {
1207 }
1208 
1209 /**
1210  *
1211  */
1212 PStatClient::InternalThread::
1213 InternalThread(const string &name, const string &sync_name) :
1214  _thread(nullptr),
1215  _name(name),
1216  _sync_name(sync_name),
1217  _is_active(false),
1218  _frame_number(0),
1219  _next_packet(0.0),
1220  _thread_active(true),
1221  _thread_lock(string("PStatClient::InternalThread ") + name)
1222 {
1223 }
1224 
1225 #endif // DO_PSTATS
void set_pstats_index(int pstats_index)
Stores a PStats index to be associated with this thread.
Definition: thread.I:303
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.
static void inc(Integer &var)
Atomically increments the indicated variable.
static size_t get_total_unused()
Returns the difference between get_total_alloc() and get_total_used().
static void disconnect()
Closes the connection previously established.
Definition: pStatClient.h:270
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:42
A lightweight class that represents a single element that may be timed and/or counted via stats.
get_current_thread
Returns a pointer to the currently-executing Thread object.
Definition: thread.h:109
get_num_typehandles
Returns the total number of unique TypeHandles in the system.
Definition: typeRegistry.h:61
Similar to MutexHolder, but for a light mutex.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static Integer get(const Integer &var)
Atomically retrieves the snapshot value of the indicated variable.
void set_pstats_callback(PStatsCallback *pstats_callback)
Stores a PStats callback to be associated with this thread.
Definition: thread.I:312
A lightweight class that represents a single thread of execution to PStats.
Definition: pStatThread.h:28
size_t get_memory_usage(MemoryClass memory_class) const
Returns the total allocated memory used by objects of this type, for the indicated memory class.
Definition: typeHandle.cxx:24
Similar to MutexHolder, but for a reentrant mutex.
Definition: reMutexHolder.h:25
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_typehandle
Returns the nth TypeHandle in the system.
Definition: typeRegistry.h:61
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static TypeRegistry * ptr()
Returns the pointer to the global TypeRegistry object.
Definition: typeRegistry.I:30
The TypeRegistry class maintains all the assigned TypeHandles in a given system.
Definition: typeRegistry.h:36
A thread; that is, a lightweight process.
Definition: thread.h:46
get_pstats_index
Returns the PStats index associated with this thread, or -1 if no index has yet been associated with ...
Definition: thread.h:102
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static bool is_connected()
Returns true if the client believes it is connected to a working PStatServer, false otherwise.
Definition: pStatClient.h:271
static Pointer set_ptr(Pointer &var, Pointer new_value)
Atomically changes the indicated variable and returns the original value.
static Integer set(Integer &var, Integer new_value)
Atomically changes the indicated variable and returns the original value.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
Defines the details about the Collectors: the name, the suggested color, etc.
Manages the communications to report statistics via a network connection to a remote PStatServer.
Definition: pStatClient.h:263
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_sync_name
Returns the sync name of the thread.
Definition: thread.h:101