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 #include "pStatCollector.h"
16 #include "pStatThread.h"
17 
18 #ifdef DO_PSTATS
19 // This file only defines anything interesting if DO_PSTATS is defined.
20 
21 #include "pStatClientImpl.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  }
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 #else // DO_PSTATS
1226 
1227 void PStatClient::
1228 set_client_name(const std::string &name) {
1229 }
1230 
1231 std::string PStatClient::
1232 get_client_name() const {
1233  return std::string();
1234 }
1235 
1236 void PStatClient::
1237 set_max_rate(double rate) {
1238 }
1239 
1240 double PStatClient::
1241 get_max_rate() const {
1242  return 0.0;
1243 }
1244 
1245 PStatCollector PStatClient::
1246 get_collector(int index) const {
1247  return PStatCollector();
1248 }
1249 
1250 std::string PStatClient::
1251 get_collector_name(int index) const {
1252  return std::string();
1253 }
1254 
1255 std::string PStatClient::
1256 get_collector_fullname(int index) const {
1257  return std::string();
1258 }
1259 
1260 PStatThread PStatClient::
1261 get_thread(int index) const {
1262  return PStatThread((PStatClient *)this, 0);
1263 }
1264 
1265 double PStatClient::
1266 get_real_time() const {
1267  return 0.0;
1268 }
1269 
1270 PStatThread PStatClient::
1271 get_main_thread() const {
1272  return PStatThread((PStatClient *)this, 0);
1273 }
1274 
1275 PStatThread PStatClient::
1276 get_current_thread() const {
1277  return get_main_thread();
1278 }
1279 
1280 PStatCollector PStatClient::
1281 make_collector_with_relname(int parent_index, std::string relname) {
1282  return PStatCollector();
1283 }
1284 
1285 PStatThread PStatClient::
1286 make_thread(Thread *thread) {
1287  return PStatThread((PStatClient *)this, 0);
1288 }
1289 
1290 void PStatClient::
1291 main_tick() {
1292 }
1293 
1294 void PStatClient::
1295 thread_tick(const std::string &) {
1296 }
1297 
1298 void PStatClient::
1299 client_main_tick() {
1300 }
1301 
1302 void PStatClient::
1303 client_thread_tick(const std::string &sync_name) {
1304 }
1305 
1306 bool PStatClient::
1307 client_connect(std::string hostname, int port) {
1308  return false;
1309 }
1310 
1311 void PStatClient::
1312 client_disconnect() {
1313  return;
1314 }
1315 
1316 bool PStatClient::
1317 client_is_connected() const {
1318  return false;
1319 }
1320 
1321 void PStatClient::
1322 client_resume_after_pause() {
1323  return;
1324 }
1325 
1326 PStatClient *PStatClient::
1327 get_global_pstats() {
1328  static PStatClient global_pstats;
1329  return &global_pstats;
1330 }
1331 
1332 bool PStatClient::
1333 is_active(int collector_index, int thread_index) const {
1334  return false;
1335 }
1336 
1337 bool PStatClient::
1338 is_started(int collector_index, int thread_index) const {
1339  return false;
1340 }
1341 
1342 void PStatClient::
1343 start(int collector_index, int thread_index) {
1344 }
1345 
1346 void PStatClient::
1347 start(int collector_index, int thread_index, double as_of) {
1348 }
1349 
1350 void PStatClient::
1351 stop(int collector_index, int thread_index) {
1352 }
1353 
1354 void PStatClient::
1355 stop(int collector_index, int thread_index, double as_of) {
1356 }
1357 
1358 void PStatClient::
1359 clear_level(int collector_index, int thread_index) {
1360 }
1361 
1362 void PStatClient::
1363 set_level(int collector_index, int thread_index, double level) {
1364 }
1365 
1366 void PStatClient::
1367 add_level(int collector_index, int thread_index, double increment) {
1368 }
1369 
1370 double PStatClient::
1371 get_level(int collector_index, int thread_index) const {
1372  return 0.0;
1373 }
1374 
1375 #endif // DO_PSTATS
LightMutexHolder
Similar to MutexHolder, but for a light mutex.
Definition: lightMutexHolder.h:25
Thread::set_pstats_callback
void set_pstats_callback(PStatsCallback *pstats_callback)
Stores a PStats callback to be associated with this thread.
Definition: thread.I:312
PStatClient::get_thread_name
std::string get_thread_name(int index) const
Returns the name of the indicated thread.
Definition: pStatClient.h:279
Thread::get_main_thread
get_main_thread
Returns a pointer to the "main" Thread object–this is the Thread that started the whole process.
Definition: thread.h:107
AtomicAdjustDummyImpl::get
static Integer get(const Integer &var)
Atomically retrieves the snapshot value of the indicated variable.
Definition: atomicAdjustDummyImpl.I:59
pvector
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:42
ReMutexHolder
Similar to MutexHolder, but for a reentrant mutex.
Definition: reMutexHolder.h:25
MemoryUsage::get_panda_heap_array_size
get_panda_heap_array_size
Returns the total number of bytes allocated from the heap from code within Panda, for arrays.
Definition: memoryUsage.h:99
pStatProperties.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
NeverFreeMemory::get_total_unused
static size_t get_total_unused()
Returns the difference between get_total_alloc() and get_total_used().
Definition: neverFreeMemory.I:49
clockObject.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PStatCollectorDef
Defines the details about the Collectors: the name, the suggested color, etc.
Definition: pStatCollectorDef.h:29
pStatThread.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
MemoryUsage::get_external_size
get_external_size
Returns the total number of bytes of allocated memory in the heap that Panda didn't seem to be respon...
Definition: memoryUsage.h:102
MemoryUsage::get_panda_heap_single_size
get_panda_heap_single_size
Returns the total number of bytes allocated from the heap from code within Panda, for individual obje...
Definition: memoryUsage.h:98
Thread::get_current_thread
get_current_thread
Returns a pointer to the currently-executing Thread object.
Definition: thread.h:109
MemoryUsage::get_panda_mmap_size
get_panda_mmap_size
Returns the total number of bytes allocated from the virtual memory pool from code within Panda.
Definition: memoryUsage.h:101
TypeHandle
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
Thread::set_pstats_index
void set_pstats_index(int pstats_index)
Stores a PStats index to be associated with this thread.
Definition: thread.I:303
neverFreeMemory.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PStatClient::disconnect
static void disconnect()
Closes the connection previously established.
Definition: pStatClient.h:290
PStatCollector
A lightweight class that represents a single element that may be timed and/or counted via stats.
Definition: pStatCollector.h:43
MemoryUsage::get_total_size
get_total_size
Returns the total size of allocated memory consumed by the process, as nearly as can be determined.
Definition: memoryUsage.h:103
PStatClient
Manages the communications to report statistics via a network connection to a remote PStatServer.
Definition: pStatClient.h:263
MemoryUsage::get_panda_heap_overhead
get_panda_heap_overhead
Returns the extra bytes allocated from the system that are not immediately used for holding allocated...
Definition: memoryUsage.h:100
AtomicAdjustDummyImpl::set
static Integer set(Integer &var, Integer new_value)
Atomically changes the indicated variable and returns the original value.
Definition: atomicAdjustDummyImpl.I:46
PStatClient::is_connected
static bool is_connected()
Returns true if the client believes it is connected to a working PStatServer, false otherwise.
Definition: pStatClient.h:291
config_pstatclient.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Thread::get_pstats_index
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
pStatServerControlMessage.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
pStatClientImpl.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
pStatClient.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
TypeRegistry::ptr
static TypeRegistry * ptr()
Returns the pointer to the global TypeRegistry object.
Definition: typeRegistry.I:30
TypeRegistry::get_num_typehandles
get_num_typehandles
Returns the total number of unique TypeHandles in the system.
Definition: typeRegistry.h:61
TypeRegistry
The TypeRegistry class maintains all the assigned TypeHandles in a given system.
Definition: typeRegistry.h:36
Thread::get_sync_name
get_sync_name
Returns the sync name of the thread.
Definition: thread.h:101
AtomicAdjustDummyImpl::set_ptr
static Pointer set_ptr(Pointer &var, Pointer new_value)
Atomically changes the indicated variable and returns the original value.
Definition: atomicAdjustDummyImpl.I:67
TypeHandle::get_memory_usage
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
PStatThread
A lightweight class that represents a single thread of execution to PStats.
Definition: pStatThread.h:28
Thread
A thread; that is, a lightweight process.
Definition: thread.h:46
thread.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
TypeRegistry::get_typehandle
get_typehandle
Returns the nth TypeHandle in the system.
Definition: typeRegistry.h:61
pStatClientControlMessage.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
pStatCollector.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
AtomicAdjustDummyImpl::inc
static void inc(Integer &var)
Atomically increments the indicated variable.
Definition: atomicAdjustDummyImpl.I:18