Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
Profile.cpp
Go to the documentation of this file.
1 /* Copyright (C) 2011 Wildfire Games.
2  * This file is part of 0 A.D.
3  *
4  * 0 A.D. is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * 0 A.D. is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 /*
19  * GPG3-style hierarchical profiler
20  */
21 
22 #include "precompiled.h"
23 
24 #include "Profile.h"
25 #include "ProfileViewer.h"
26 #include "lib/timer.h"
27 
28 #if OS_WIN && !defined(NDEBUG)
29 # define USE_CRT_SET_ALLOC_HOOK
30 #endif
31 
32 #if defined(__GLIBC__) && !defined(NDEBUG)
33 //# define USE_GLIBC_MALLOC_HOOK
34 # define USE_GLIBC_MALLOC_OVERRIDE
35 # include <dlfcn.h>
36 # include <malloc.h>
37 # include "lib/sysdep/cpu.h"
38 #endif
39 
40 #include <numeric>
41 
42 ///////////////////////////////////////////////////////////////////////////////////////////////
43 // CProfileNodeTable
44 
45 
46 
47 /**
48  * Class CProfileNodeTable: Implement ProfileViewer's AbstractProfileTable
49  * interface in order to display profiling data in-game.
50  */
52 {
53 public:
55  virtual ~CProfileNodeTable();
56 
57  // Implementation of AbstractProfileTable interface
58  virtual CStr GetName();
59  virtual CStr GetTitle();
60  virtual size_t GetNumberRows();
61  virtual const std::vector<ProfileColumn>& GetColumns();
62 
63  virtual CStr GetCellText(size_t row, size_t col);
64  virtual AbstractProfileTable* GetChild(size_t row);
65  virtual bool IsHighlightRow(size_t row);
66 
67 private:
68  /**
69  * struct ColumnDescription: The only purpose of this helper structure
70  * is to provide the global constructor that sets up the column
71  * description.
72  */
74  {
75  std::vector<ProfileColumn> columns;
76 
78  {
79  columns.push_back(ProfileColumn("Name", 230));
80  columns.push_back(ProfileColumn("calls/frame", 80));
81  columns.push_back(ProfileColumn("msec/frame", 80));
82  columns.push_back(ProfileColumn("mallocs/frame", 120));
83  columns.push_back(ProfileColumn("calls/turn", 80));
84  columns.push_back(ProfileColumn("msec/turn", 80));
85  columns.push_back(ProfileColumn("mallocs/turn", 80));
86  }
87  };
88 
89  /// The node represented by this table
91 
92  /// Columns description (shared by all instances)
94 };
95 
97 
98 
99 // Constructor/Destructor
101 {
102  node = n;
103 }
104 
106 {
107 }
108 
109 // Short name (= name of profile node)
111 {
112  return node->GetName();
113 }
114 
115 // Title (= explanatory text plus time totals)
117 {
118  char buf[512];
119  sprintf_s(buf, ARRAY_SIZE(buf), "Profiling Information for: %s (Time in node: %.3f msec/frame)", node->GetName(), node->GetFrameTime() * 1000.0f );
120  return buf;
121 }
122 
123 // Total number of children
125 {
126  return node->GetChildren()->size() + node->GetScriptChildren()->size() + 1;
127 }
128 
129 // Column description
130 const std::vector<ProfileColumn>& CProfileNodeTable::GetColumns()
131 {
132  return columnDescription.columns;
133 }
134 
135 // Retrieve cell text
136 CStr CProfileNodeTable::GetCellText(size_t row, size_t col)
137 {
138  CProfileNode* child;
139  size_t nrchildren = node->GetChildren()->size();
140  size_t nrscriptchildren = node->GetScriptChildren()->size();
141  char buf[256] = "?";
142 
143  if (row < nrchildren)
144  child = (*node->GetChildren())[row];
145  else if (row < nrchildren + nrscriptchildren)
146  child = (*node->GetScriptChildren())[row - nrchildren];
147  else if (row > nrchildren + nrscriptchildren)
148  return "!bad row!";
149  else
150  {
151  // "unlogged" row
152  if (col == 0)
153  return "unlogged";
154  else if (col == 1)
155  return "";
156  else if (col == 4)
157  return "";
158 
159  double unlogged_time_frame = node->GetFrameTime();
160  double unlogged_time_turn = node->GetTurnTime();
161  double unlogged_mallocs_frame = node->GetFrameMallocs();
162  double unlogged_mallocs_turn = node->GetTurnMallocs();
164 
165  for (it = node->GetChildren()->begin(); it != node->GetChildren()->end(); ++it)
166  {
167  unlogged_time_frame -= (*it)->GetFrameTime();
168  unlogged_time_turn -= (*it)->GetTurnTime();
169  unlogged_mallocs_frame -= (*it)->GetFrameMallocs();
170  unlogged_mallocs_turn -= (*it)->GetTurnMallocs();
171  }
172  for (it = node->GetScriptChildren()->begin(); it != node->GetScriptChildren()->end(); ++it)
173  {
174  unlogged_time_frame -= (*it)->GetFrameTime();
175  unlogged_time_turn -= (*it)->GetTurnTime();
176  unlogged_mallocs_frame -= (*it)->GetFrameMallocs();
177  unlogged_mallocs_turn -= (*it)->GetTurnMallocs();
178  }
179 
180  // The root node can't easily count per-turn values (since Turn isn't called until
181  // halfway though a frame), so just reset them the zero to prevent weird displays
182  if (!node->GetParent())
183  {
184  unlogged_time_turn = 0.0;
185  unlogged_mallocs_turn = 0.0;
186  }
187 
188  if (col == 2)
189  sprintf_s(buf, ARRAY_SIZE(buf), "%.3f", unlogged_time_frame * 1000.0f);
190  else if (col == 3)
191  sprintf_s(buf, ARRAY_SIZE(buf), "%.1f", unlogged_mallocs_frame);
192  else if (col == 5)
193  sprintf_s(buf, ARRAY_SIZE(buf), "%.3f", unlogged_time_turn * 1000.f);
194  else if (col == 6)
195  sprintf_s(buf, ARRAY_SIZE(buf), "%.1f", unlogged_mallocs_turn);
196 
197  return CStr(buf);
198  }
199 
200  switch(col)
201  {
202  default:
203  case 0:
204  return child->GetName();
205 
206  case 1:
207  sprintf_s(buf, ARRAY_SIZE(buf), "%.1f", child->GetFrameCalls());
208  break;
209  case 2:
210  sprintf_s(buf, ARRAY_SIZE(buf), "%.3f", child->GetFrameTime() * 1000.0f);
211  break;
212  case 3:
213  sprintf_s(buf, ARRAY_SIZE(buf), "%.1f", child->GetFrameMallocs());
214  break;
215  case 4:
216  sprintf_s(buf, ARRAY_SIZE(buf), "%.1f", child->GetTurnCalls());
217  break;
218  case 5:
219  sprintf_s(buf, ARRAY_SIZE(buf), "%.3f", child->GetTurnTime() * 1000.0f);
220  break;
221  case 6:
222  sprintf_s(buf, ARRAY_SIZE(buf), "%.1f", child->GetTurnMallocs());
223  break;
224  }
225  return CStr(buf);
226 }
227 
228 // Return a pointer to the child table if the child node is expandable
230 {
231  CProfileNode* child;
232  size_t nrchildren = node->GetChildren()->size();
233  size_t nrscriptchildren = node->GetScriptChildren()->size();
234 
235  if (row < nrchildren)
236  child = (*node->GetChildren())[row];
237  else if (row < nrchildren + nrscriptchildren)
238  child = (*node->GetScriptChildren())[row - nrchildren];
239  else
240  return 0;
241 
242  if (child->CanExpand())
243  return child->display_table;
244 
245  return 0;
246 }
247 
248 // Highlight all script nodes
250 {
251  size_t nrchildren = node->GetChildren()->size();
252  size_t nrscriptchildren = node->GetScriptChildren()->size();
253 
254  return (row >= nrchildren && row < (nrchildren + nrscriptchildren));
255 }
256 
257 ///////////////////////////////////////////////////////////////////////////////////////////////
258 // CProfileNode implementation
259 
260 
261 // Note: As with the GPG profiler, name is assumed to be a pointer to a constant string; only pointer equality is checked.
262 CProfileNode::CProfileNode( const char* _name, CProfileNode* _parent )
263 {
264  name = _name;
265  recursion = 0;
266 
267  Reset();
268 
269  parent = _parent;
270 
271  display_table = new CProfileNodeTable(this);
272 }
273 
275 {
276  profile_iterator it;
277  for( it = children.begin(); it != children.end(); ++it )
278  delete( *it );
279  for( it = script_children.begin(); it != script_children.end(); ++it )
280  delete( *it );
281 
282  delete display_table;
283 }
284 
285 template<typename T>
286 static double average(const T& collection)
287 {
288  if (collection.empty())
289  return 0.0;
290  return std::accumulate(collection.begin(), collection.end(), 0.0) / collection.size();
291 }
292 
294 {
295  return average(calls_per_frame);
296 }
297 
299 {
300  return average(time_per_frame);
301 }
302 
304 {
305  return average(calls_per_turn);
306 }
307 
309 {
310  return average(time_per_turn);
311 }
312 
314 {
315  return average(mallocs_per_frame);
316 }
317 
319 {
320  return average(mallocs_per_turn);
321 }
322 
323 const CProfileNode* CProfileNode::GetChild( const char* childName ) const
324 {
326  for( it = children.begin(); it != children.end(); ++it )
327  if( (*it)->name == childName )
328  return( *it );
329 
330  return( NULL );
331 }
332 
333 const CProfileNode* CProfileNode::GetScriptChild( const char* childName ) const
334 {
336  for( it = script_children.begin(); it != script_children.end(); ++it )
337  if( (*it)->name == childName )
338  return( *it );
339 
340  return( NULL );
341 }
342 
343 CProfileNode* CProfileNode::GetChild( const char* childName )
344 {
345  profile_iterator it;
346  for( it = children.begin(); it != children.end(); ++it )
347  if( (*it)->name == childName )
348  return( *it );
349 
350  CProfileNode* newNode = new CProfileNode( childName, this );
351  children.push_back( newNode );
352  return( newNode );
353 }
354 
356 {
357  profile_iterator it;
358  for( it = script_children.begin(); it != script_children.end(); ++it )
359  if( (*it)->name == childName )
360  return( *it );
361 
362  CProfileNode* newNode = new CProfileNode( childName, this );
363  script_children.push_back( newNode );
364  return( newNode );
365 }
366 
368 {
369  return( !( children.empty() && script_children.empty() ) );
370 }
371 
373 {
377  calls_turn_current = 0;
378 
381  time_frame_current = 0.0;
382  time_turn_current = 0.0;
383 
388 
389  profile_iterator it;
390  for (it = children.begin(); it != children.end(); ++it)
391  (*it)->Reset();
392  for (it = script_children.begin(); it != script_children.end(); ++it)
393  (*it)->Reset();
394 }
395 
397 {
401 
403  time_frame_current = 0.0;
405 
406  profile_iterator it;
407  for (it = children.begin(); it != children.end(); ++it)
408  (*it)->Frame();
409  for (it = script_children.begin(); it != script_children.end(); ++it)
410  (*it)->Frame();
411 }
412 
414 {
418 
419  calls_turn_current = 0;
420  time_turn_current = 0.0;
422 
423  profile_iterator it;
424  for (it = children.begin(); it != children.end(); ++it)
425  (*it)->Turn();
426  for (it = script_children.begin(); it != script_children.end(); ++it)
427  (*it)->Turn();
428 }
429 
430 // TODO: these should probably only count allocations that occur in the thread being profiled
431 #if defined(USE_CRT_SET_ALLOC_HOOK)
432 
433 static long malloc_count = 0;
434 static _CRT_ALLOC_HOOK prev_hook;
435 
436 static int crt_alloc_hook(int allocType, void* userData, size_t size, int blockType,
437  long requestNumber, const unsigned char* filename, int lineNumber)
438 {
439  if (allocType == _HOOK_ALLOC && ThreadUtil::IsMainThread())
440  ++malloc_count;
441 
442  if (prev_hook)
443  return prev_hook(allocType, userData, size, blockType, requestNumber, filename, lineNumber);
444  else
445  return 1;
446 }
447 
448 static void alloc_hook_initialize()
449 {
450  prev_hook = _CrtSetAllocHook(crt_alloc_hook);
451 }
452 
453 static long get_memory_alloc_count()
454 {
455  return malloc_count;
456 }
457 
458 #elif defined(USE_GLIBC_MALLOC_HOOK)
459 
460 // Set up malloc hooks to count allocations - see
461 // http://www.gnu.org/software/libc/manual/html_node/Hooks-for-Malloc.html
462 static intptr_t malloc_count = 0;
463 static void *(*old_malloc_hook) (size_t, const void*);
464 static pthread_mutex_t alloc_hook_mutex = PTHREAD_MUTEX_INITIALIZER;
465 static void *malloc_hook(size_t size, const void* UNUSED(caller))
466 {
467  // This doesn't really work across threads. The hooks are global variables, and
468  // we have to temporarily unhook in order to call the real malloc, and during that
469  // time period another thread may perform an unhooked (hence uncounted) allocation
470  // which we will miss.
471 
472  // Two threads may execute the hook simultaneously, so we need to do the
473  // temporary unhooking in a thread-safe way, so for simplicity we just use a mutex.
474  pthread_mutex_lock(&alloc_hook_mutex);
475  ++malloc_count;
476  __malloc_hook = old_malloc_hook;
477  void* result = malloc(size);
478  old_malloc_hook = __malloc_hook;
479  __malloc_hook = malloc_hook;
480  pthread_mutex_unlock(&alloc_hook_mutex);
481  return result;
482 }
483 
484 static void alloc_hook_initialize()
485 {
486  pthread_mutex_lock(&alloc_hook_mutex);
487  old_malloc_hook = __malloc_hook;
488  __malloc_hook = malloc_hook;
489  // (we don't want to bother hooking realloc and memalign, because if they allocate
490  // new memory then they'll be caught by the malloc hook anyway)
491  pthread_mutex_unlock(&alloc_hook_mutex);
492 }
493 /*
494 It would be nice to do:
495  __attribute__ ((visibility ("default"))) void (*__malloc_initialize_hook)() = malloc_initialize_hook;
496 except that doesn't seem to work in practice, since something (?) resets the
497 hook to NULL some time while loading the game, after we've set it here - so
498 we just call malloc_initialize_hook once inside CProfileManager::Frame instead
499 and hope nobody deletes our hook after that.
500 */
501 static long get_memory_alloc_count()
502 {
503  return malloc_count;
504 }
505 
506 #elif defined(USE_GLIBC_MALLOC_OVERRIDE)
507 
508 static intptr_t alloc_count = 0;
509 
510 // We override the malloc/realloc/calloc/free functions and then use dlsym to
511 // defer the actual allocation to the real libc implementation.
512 // The dlsym call will (in glibc 2.9/2.10) call calloc once (to allocate an error
513 // message structure), so we have a bootstrapping problem when trying to
514 // get the first called function via dlsym. So we kludge it by returning a statically-allocated
515 // buffer for the very first call to calloc after we've called dlsym.
516 // This is quite hacky but it seems to just about work in practice...
517 static bool alloc_bootstrapped = false;
518 static char alloc_bootstrap_buffer[32]; // sufficient for x86_64
519 static bool alloc_has_called_dlsym = false;
520 // (We'll only be running a single thread at this point so no need for locking these variables)
521 
522 //#define ALLOC_DEBUG
523 
524 void* malloc(size_t sz)
525 {
526  cpu_AtomicAdd(&alloc_count, 1);
527 
528  static void *(*libc_malloc)(size_t);
529  if (libc_malloc == NULL)
530  {
531  alloc_has_called_dlsym = true;
532  libc_malloc = (void *(*)(size_t)) dlsym(RTLD_NEXT, "malloc");
533  }
534  void* ret = libc_malloc(sz);
535 #ifdef ALLOC_DEBUG
536  printf("### malloc(%d) = %p\n", sz, ret);
537 #endif
538  return ret;
539 }
540 
541 void* realloc(void* ptr, size_t sz)
542 {
543  cpu_AtomicAdd(&alloc_count, 1);
544 
545  static void *(*libc_realloc)(void*, size_t);
546  if (libc_realloc == NULL)
547  {
548  alloc_has_called_dlsym = true;
549  libc_realloc = (void *(*)(void*, size_t)) dlsym(RTLD_NEXT, "realloc");
550  }
551  void* ret = libc_realloc(ptr, sz);
552 #ifdef ALLOC_DEBUG
553  printf("### realloc(%p, %d) = %p\n", ptr, sz, ret);
554 #endif
555  return ret;
556 }
557 
558 void* calloc(size_t nm, size_t sz)
559 {
560  cpu_AtomicAdd(&alloc_count, 1);
561 
562  static void *(*libc_calloc)(size_t, size_t);
563  if (libc_calloc == NULL)
564  {
565  if (alloc_has_called_dlsym && !alloc_bootstrapped)
566  {
567  ENSURE(nm*sz <= ARRAY_SIZE(alloc_bootstrap_buffer));
568 #ifdef ALLOC_DEBUG
569  printf("### calloc-bs(%d, %d) = %p\n", nm, sz, alloc_bootstrap_buffer);
570 #endif
571  alloc_bootstrapped = true;
572  return alloc_bootstrap_buffer;
573  }
574  alloc_has_called_dlsym = true;
575  libc_calloc = (void *(*)(size_t, size_t)) dlsym(RTLD_NEXT, "calloc");
576  }
577  void* ret = libc_calloc(nm, sz);
578 #ifdef ALLOC_DEBUG
579  printf("### calloc(%d, %d) = %p\n", nm, sz, ret);
580 #endif
581  return ret;
582 }
583 
584 void free(void* ptr)
585 {
586  static void (*libc_free)(void*);
587  if (libc_free == NULL)
588  {
589  alloc_has_called_dlsym = true;
590  libc_free = (void (*)(void*)) dlsym(RTLD_NEXT, "free");
591  }
592 
593  libc_free(ptr);
594 #ifdef ALLOC_DEBUG
595  printf("### free(%p)\n", ptr);
596 #endif
597 }
598 
599 static void alloc_hook_initialize()
600 {
601 }
602 
603 static long get_memory_alloc_count()
604 {
605  return alloc_count;
606 }
607 
608 #else
609 
611 {
612 }
614 {
615  // TODO: don't show this column of data when we don't have sensible values
616  // to display.
617  return 0;
618 }
619 #endif
620 
622 {
625  if (recursion++ == 0)
626  {
627  start = timer_Time();
629  }
630 }
631 
633 {
634  if (--recursion != 0)
635  return false;
636 
637  double now = timer_Time();
638  long allocs = get_memory_alloc_count();
639  time_frame_current += (now - start);
640  time_turn_current += (now - start);
641  mallocs_frame_current += (allocs - start_mallocs);
642  mallocs_turn_current += (allocs - start_mallocs);
643  return true;
644 }
645 
647  root(NULL), current(NULL), needs_structural_reset(false)
648 {
650 }
651 
653 {
654  delete root;
655 }
656 
657 void CProfileManager::Start( const char* name )
658 {
659  if( name != current->GetName() )
660  current = current->GetChild( name );
661  current->Call();
662 }
663 
664 void CProfileManager::StartScript( const char* name )
665 {
666  if( name != current->GetName() )
667  current = current->GetScriptChild( name );
668  current->Call();
669 }
670 
672 {
673  if (current->Return())
675 }
676 
678 {
679  root->Reset();
680 }
681 
683 {
685 
688 
689  root->Frame();
690 
692  {
694  needs_structural_reset = false;
695  }
696 
697  root->start = timer_Time();
699 }
700 
702 {
703  root->Turn();
704 }
705 
707 {
708  // We can't immediately perform the reset, because we're probably already
709  // nested inside the profile tree and it will get very confused if we delete
710  // the tree when we're not currently at the root.
711  // So just set a flag to perform the reset at the end of the frame.
712 
713  needs_structural_reset = true;
714 }
715 
717 {
718  delete root;
719  root = new CProfileNode("root", NULL);
720  root->Call();
721  current = root;
722  g_ProfileViewer.AddRootTable(root->display_table, true);
723 }
RingBuf< double, PROFILE_AMORTIZE_TURNS > time_per_turn
Definition: Profile.h:58
const CProfileNode * GetChild(const char *name) const
Definition: Profile.cpp:323
RingBuf< int, PROFILE_AMORTIZE_TURNS > calls_per_turn
Definition: Profile.h:53
void * pthread_mutex_t
Definition: wpthread.h:82
static long get_memory_alloc_count()
Definition: Profile.cpp:613
static double average(const T &collection)
Definition: Profile.cpp:286
void Turn()
Definition: Profile.cpp:413
virtual CStr GetCellText(size_t row, size_t col)
GetCellText.
Definition: Profile.cpp:136
#define UNUSED(param)
mark a function parameter as unused and avoid the corresponding compiler warning. ...
double GetFrameCalls() const
Definition: Profile.cpp:293
const char * GetName() const
Definition: Profile.h:81
CProfileNode * node
The node represented by this table.
Definition: Profile.cpp:90
static ColumnDescription columnDescription
Columns description (shared by all instances)
Definition: Profile.cpp:93
bool Return()
Definition: Profile.cpp:632
const std::vector< CProfileNode * > * GetScriptChildren() const
Definition: Profile.h:93
RingBuf< int, PROFILE_AMORTIZE_FRAMES > calls_per_frame
Definition: Profile.h:52
void StartScript(const char *name)
Definition: Profile.cpp:664
static void alloc_hook_initialize()
Definition: Profile.cpp:610
CProfileNode * parent
Definition: Profile.h:69
Class AbstractProfileTable: Profile table data model.
Definition: ProfileViewer.h:60
std::vector< CProfileNode * > children
Definition: Profile.h:70
double time_turn_current
Definition: Profile.h:56
long mallocs_turn_current
Definition: Profile.h:61
double start
Definition: Profile.h:65
int calls_turn_current
Definition: Profile.h:51
CProfileNode * GetParent() const
Definition: Profile.h:99
intptr_t cpu_AtomicAdd(volatile intptr_t *location, intptr_t increment)
add a signed value to a variable without the possibility of interference from other threads/CPUs...
Definition: arm.cpp:31
Class CProfileNodeTable: Implement ProfileViewer&#39;s AbstractProfileTable interface in order to display...
Definition: Profile.cpp:51
void clear()
Definition: ring_buf.h:39
void Frame()
Definition: Profile.cpp:396
int sprintf_s(char *buf, size_t max_chars, const char *fmt,...) PRINTF_ARGS(3)
CProfileNodeTable * display_table
Definition: Profile.h:72
CProfileNode * root
Definition: Profile.h:115
#define ARRAY_SIZE(name)
friend class CProfileNodeTable
Definition: Profile.h:46
void push_back(const T &item)
Definition: ring_buf.h:78
virtual CStr GetTitle()
GetTitle: Longer, explanatory text (can be dynamic).
Definition: Profile.cpp:116
#define ENSURE(expr)
ensure the expression &lt;expr&gt; evaluates to non-zero.
Definition: debug.h:282
int recursion
Definition: Profile.h:67
std::vector< CProfileNode * >::const_iterator const_profile_iterator
Definition: Profile.h:76
void PerformStructuralReset()
Definition: Profile.cpp:716
std::vector< ProfileColumn > columns
Definition: Profile.cpp:75
struct ColumnDescription: The only purpose of this helper structure is to provide the global construc...
Definition: Profile.cpp:73
virtual const std::vector< ProfileColumn > & GetColumns()
GetColumnDescriptions.
Definition: Profile.cpp:130
bool needs_structural_reset
Definition: Profile.h:118
#define ONCE(ONCE_code__)
execute the code passed as a parameter only the first time this is reached.
int pthread_mutex_lock(pthread_mutex_t *m)
Definition: wpthread.cpp:329
void * dlsym(void *handle, const char *sym_name)
Definition: wdlfcn.cpp:74
void StructuralReset()
Definition: Profile.cpp:706
CProfileNode * current
Definition: Profile.h:116
#define T(string_literal)
Definition: secure_crt.cpp:70
double GetFrameMallocs() const
Definition: Profile.cpp:313
const std::vector< CProfileNode * > * GetChildren() const
Definition: Profile.h:92
double timer_Time()
Definition: timer.cpp:98
virtual AbstractProfileTable * GetChild(size_t row)
GetChild: Return a row&#39;s child table if the child is expandable.
Definition: Profile.cpp:229
long mallocs_frame_current
Definition: Profile.h:60
void Call()
Definition: Profile.cpp:621
const CProfileNode * GetScriptChild(const char *name) const
Definition: Profile.cpp:333
virtual ~CProfileNodeTable()
Definition: Profile.cpp:105
double GetTurnTime() const
Definition: Profile.cpp:308
double time_frame_current
Definition: Profile.h:55
int pthread_mutex_unlock(pthread_mutex_t *m)
Definition: wpthread.cpp:347
RingBuf< double, PROFILE_AMORTIZE_FRAMES > time_per_frame
Definition: Profile.h:57
CProfileNode(const char *name, CProfileNode *parent)
Definition: Profile.cpp:262
Struct ProfileColumn: Describes one column of an AbstractProfileTable.
Definition: ProfileViewer.h:35
#define g_ProfileViewer
void Reset()
Definition: Profile.cpp:372
int calls_frame_current
Definition: Profile.h:50
double GetFrameTime() const
Definition: Profile.cpp:298
bool IsMainThread()
Returns whether the current thread is the &#39;main&#39; thread (i.e.
Definition: ThreadUtil.cpp:25
const char * name
Definition: Profile.h:48
long start_mallocs
Definition: Profile.h:66
RingBuf< long, PROFILE_AMORTIZE_FRAMES > mallocs_per_frame
Definition: Profile.h:62
RingBuf< long, PROFILE_AMORTIZE_TURNS > mallocs_per_turn
Definition: Profile.h:63
std::vector< CProfileNode * > script_children
Definition: Profile.h:71
double GetTurnMallocs() const
Definition: Profile.cpp:318
bool CanExpand()
Definition: Profile.cpp:367
#define PTHREAD_MUTEX_INITIALIZER
Definition: wpthread.h:84
std::vector< CProfileNode * >::iterator profile_iterator
Definition: Profile.h:75
virtual bool IsHighlightRow(size_t row)
IsHighlightRow.
Definition: Profile.cpp:249
CProfileNodeTable(CProfileNode *n)
Definition: Profile.cpp:100
double GetTurnCalls() const
Definition: Profile.cpp:303
virtual CStr GetName()
GetName: Short descriptive name of this table (should be static).
Definition: Profile.cpp:110
virtual size_t GetNumberRows()
GetNumberRows.
Definition: Profile.cpp:124
void Start(const char *name)
Definition: Profile.cpp:657