Panda3D
Loading...
Searching...
No Matches
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
30using std::string;
31
32PStatCollector PStatClient::_heap_total_size_pcollector("System memory:Heap");
33PStatCollector PStatClient::_heap_overhead_size_pcollector("System memory:Heap:Overhead");
34PStatCollector PStatClient::_heap_single_size_pcollector("System memory:Heap:Single");
35PStatCollector PStatClient::_heap_single_other_size_pcollector("System memory:Heap:Single:Other");
36PStatCollector PStatClient::_heap_array_size_pcollector("System memory:Heap:Array");
37PStatCollector PStatClient::_heap_array_other_size_pcollector("System memory:Heap:Array:Other");
38PStatCollector PStatClient::_heap_external_size_pcollector("System memory:Heap:External");
39PStatCollector PStatClient::_mmap_size_pcollector("System memory:MMap");
40
41PStatCollector PStatClient::_mmap_nf_unused_size_pcollector("System memory:MMap:NeverFree:Unused");
42PStatCollector PStatClient::_mmap_dc_active_other_size_pcollector("System memory:MMap:NeverFree:Active:Other");
43PStatCollector PStatClient::_mmap_dc_inactive_other_size_pcollector("System memory:MMap:NeverFree:Inactive:Other");
44PStatCollector PStatClient::_pstats_pcollector("*:PStats");
45PStatCollector PStatClient::_clock_wait_pcollector("Wait:Clock Wait:Sleep");
46PStatCollector PStatClient::_clock_busy_wait_pcollector("Wait:Clock Wait:Spin");
47PStatCollector PStatClient::_thread_block_pcollector("Wait:Thread block");
48
49PStatClient *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.
54class TypeHandleCollector {
55public:
56 PStatCollector _mem_class[TypeHandle::MC_limit];
57};
58typedef pvector<TypeHandleCollector> TypeHandleCols;
59static TypeHandleCols type_handle_cols;
60
61
62/**
63 *
64 */
65PStatClient::PerThreadData::
66PerThreadData() {
67 _has_level = false;
68 _level = 0.0;
69 _nested_count = 0;
70}
71
72/**
73 *
74 */
75PStatClient::
76PStatClient() :
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 */
104PStatClient::
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 */
113void PStatClient::
114set_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 */
121string PStatClient::
122get_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 */
136void PStatClient::
137set_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 */
145double PStatClient::
146get_max_rate() const {
147 return get_impl()->get_max_rate();
148}
149
150/**
151 * Returns the nth collector.
152 */
153PStatCollector PStatClient::
154get_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 */
162string PStatClient::
163get_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 */
174string PStatClient::
175get_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 */
191PStatThread PStatClient::
192get_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 */
202PStatThread PStatClient::
203get_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 */
211PStatThread PStatClient::
212get_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 */
227double PStatClient::
228get_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 */
239void PStatClient::
240main_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 */
388void PStatClient::
389thread_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 */
397void PStatClient::
398client_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 */
425void PStatClient::
426client_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 */
446bool PStatClient::
447client_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 */
456void PStatClient::
457client_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 */
489bool PStatClient::
490client_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 */
499void PStatClient::
500client_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 */
511PStatClient *PStatClient::
512get_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 */
526void PStatClient::
527make_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 */
538PStatCollector PStatClient::
539make_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 */
575PStatCollector PStatClient::
576make_collector_with_name(int parent_index, const string &name) {
577 ReMutexHolder holder(_lock);
578
579 nassertr(parent_index >= 0 && parent_index < _num_collectors,
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 */
621PStatThread PStatClient::
622do_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 */
638PStatThread PStatClient::
639make_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 */
647PStatThread PStatClient::
648do_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 */
693PStatThread PStatClient::
694make_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 */
711bool PStatClient::
712is_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 */
728bool PStatClient::
729is_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 */
754void PStatClient::
755start(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 */
785void PStatClient::
786start(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 */
816void PStatClient::
817stop(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 */
858void PStatClient::
859stop(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 */
901void PStatClient::
902clear_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 */
926void PStatClient::
927set_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 */
959void PStatClient::
960add_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 */
987double PStatClient::
988get_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 */
1015void PStatClient::
1016start_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 */
1028void PStatClient::
1029start_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 */
1042void PStatClient::
1043stop_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 */
1051void PStatClient::
1052add_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 */
1085void PStatClient::
1086add_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 */
1133void PStatClient::
1134deactivate_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 */
1158void PStatClient::
1159activate_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 */
1179void PStatClient::Collector::
1180make_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 */
1196PStatClient::InternalThread::
1197InternalThread(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 */
1212PStatClient::InternalThread::
1213InternalThread(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
1227void PStatClient::
1228set_client_name(const std::string &name) {
1229}
1230
1231std::string PStatClient::
1232get_client_name() const {
1233 return std::string();
1234}
1235
1236void PStatClient::
1237set_max_rate(double rate) {
1238}
1239
1240double PStatClient::
1241get_max_rate() const {
1242 return 0.0;
1243}
1244
1245PStatCollector PStatClient::
1246get_collector(int index) const {
1247 return PStatCollector();
1248}
1249
1250std::string PStatClient::
1251get_collector_name(int index) const {
1252 return std::string();
1253}
1254
1255std::string PStatClient::
1256get_collector_fullname(int index) const {
1257 return std::string();
1258}
1259
1260PStatThread PStatClient::
1261get_thread(int index) const {
1262 return PStatThread((PStatClient *)this, 0);
1263}
1264
1265double PStatClient::
1266get_real_time() const {
1267 return 0.0;
1268}
1269
1270PStatThread PStatClient::
1271get_main_thread() const {
1272 return PStatThread((PStatClient *)this, 0);
1273}
1274
1275PStatThread PStatClient::
1276get_current_thread() const {
1277 return get_main_thread();
1278}
1279
1280PStatCollector PStatClient::
1281make_collector_with_relname(int parent_index, std::string relname) {
1282 return PStatCollector();
1283}
1284
1285PStatThread PStatClient::
1286make_thread(Thread *thread) {
1287 return PStatThread((PStatClient *)this, 0);
1288}
1289
1290void PStatClient::
1291main_tick() {
1292}
1293
1294void PStatClient::
1295thread_tick(const std::string &) {
1296}
1297
1298void PStatClient::
1299client_main_tick() {
1300}
1301
1302void PStatClient::
1303client_thread_tick(const std::string &sync_name) {
1304}
1305
1306bool PStatClient::
1307client_connect(std::string hostname, int port) {
1308 return false;
1309}
1310
1311void PStatClient::
1312client_disconnect() {
1313 return;
1314}
1315
1316bool PStatClient::
1317client_is_connected() const {
1318 return false;
1319}
1320
1321void PStatClient::
1322client_resume_after_pause() {
1323 return;
1324}
1325
1326PStatClient *PStatClient::
1327get_global_pstats() {
1328 static PStatClient global_pstats;
1329 return &global_pstats;
1330}
1331
1332bool PStatClient::
1333is_active(int collector_index, int thread_index) const {
1334 return false;
1335}
1336
1337bool PStatClient::
1338is_started(int collector_index, int thread_index) const {
1339 return false;
1340}
1341
1342void PStatClient::
1343start(int collector_index, int thread_index) {
1344}
1345
1346void PStatClient::
1347start(int collector_index, int thread_index, double as_of) {
1348}
1349
1350void PStatClient::
1351stop(int collector_index, int thread_index) {
1352}
1353
1354void PStatClient::
1355stop(int collector_index, int thread_index, double as_of) {
1356}
1357
1358void PStatClient::
1359clear_level(int collector_index, int thread_index) {
1360}
1361
1362void PStatClient::
1363set_level(int collector_index, int thread_index, double level) {
1364}
1365
1366void PStatClient::
1367add_level(int collector_index, int thread_index, double increment) {
1368}
1369
1370double PStatClient::
1371get_level(int collector_index, int thread_index) const {
1372 return 0.0;
1373}
1374
1375#endif // DO_PSTATS
static Integer set(Integer &var, Integer new_value)
Atomically changes the indicated variable and returns the original value.
static Pointer set_ptr(Pointer &var, Pointer new_value)
Atomically changes the indicated variable and returns the original value.
static void inc(Integer &var)
Atomically increments the indicated variable.
static Integer get(const Integer &var)
Atomically retrieves the snapshot value of the indicated variable.
Similar to MutexHolder, but for a light mutex.
get_external_size
Returns the total number of bytes of allocated memory in the heap that Panda didn't seem to be respon...
get_panda_mmap_size
Returns the total number of bytes allocated from the virtual memory pool from code within Panda.
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
get_total_size
Returns the total size of allocated memory consumed by the process, as nearly as can be determined.
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
get_panda_heap_overhead
Returns the extra bytes allocated from the system that are not immediately used for holding allocated...
static size_t get_total_unused()
Returns the difference between get_total_alloc() and get_total_used().
Manages the communications to report statistics via a network connection to a remote PStatServer.
static void disconnect()
Closes the connection previously established.
static bool is_connected()
Returns true if the client believes it is connected to a working PStatServer, false otherwise.
std::string get_thread_name(int index) const
Returns the name of the indicated thread.
Defines the details about the Collectors: the name, the suggested color, etc.
A lightweight class that represents a single element that may be timed and/or counted via stats.
A lightweight class that represents a single thread of execution to PStats.
Definition pStatThread.h:28
Similar to MutexHolder, but for a reentrant mutex.
A thread; that is, a lightweight process.
Definition thread.h:46
get_sync_name
Returns the sync name of the thread.
Definition thread.h:101
void set_pstats_index(int pstats_index)
Stores a PStats index to be associated with this thread.
Definition thread.I:303
get_main_thread
Returns a pointer to the "main" Thread object–this is the Thread that started the whole process.
Definition thread.h:107
get_current_thread
Returns a pointer to the currently-executing Thread object.
Definition thread.h:109
void set_pstats_callback(PStatsCallback *pstats_callback)
Stores a PStats callback to be associated with this thread.
Definition thread.I:312
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
TypeHandle is the identifier used to differentiate C++ class types.
Definition typeHandle.h:81
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.
The TypeRegistry class maintains all the assigned TypeHandles in a given system.
get_typehandle
Returns the nth TypeHandle in the system.
get_num_typehandles
Returns the total number of unique TypeHandles in the system.
static TypeRegistry * ptr()
Returns the pointer to the global TypeRegistry object.
This is our own Panda specialization on the default STL vector.
Definition pvector.h:42
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.