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