1 // $Id: dynatracer.cpp,v 1.10 2011/03/07 06:08:47 bobgian Exp $
2 
3 /*
4  * Copyright 2009-2010 Bob Giansiracusa, Mary Kuhner, Jon Yamato, and Joseph Felsenstein
5  *
6  * This software is distributed free of charge for non-commercial use
7  * and is copyrighted.  Of course, we do not guarantee that the software
8  * works, and are not responsible for any damage you may cause or have.
9  */
10 
11 // The name of this file should be changed to "dynameter.cpp", but CVS makes that *so* difficult ...
12 
13 //------------------------------------------------------------------------------------
14 
15 // If using the Dynameter, must define next symbol before including "local_build.h".
16 // And set its value to start at zero in first user and increase sequentially in each later using file.
17 #define DYNAMETER_FILE_INDEX 0
18 // Must include next file before testing LAMARC_QA_DYNAMETER and before including "dynatracer.h".
19 #include "local_build.h"
20 #include "dynatracer.h"
21 
22 //------------------------------------------------------------------------------------
23 
24 // If LAMARC_QA_DYNAMETER is now defined, compile the rest of the file.  But if not, skip to the end.
25 // This file is included in the build, but when LAMARC_QA_DYNAMETER is not defined, the rest is skipped
26 // (except for the definition of rdtsc() at the very end).
27 #ifdef LAMARC_QA_DYNAMETER
28 
29 //------------------------------------------------------------------------------------
30 
31 #include <iostream>
32 #include <iomanip>
33 #include "timex.h"                      // for GetTime()
34 
35 using namespace std;
36 
37 //------------------------------------------------------------------------------------
38 // Experimental overloaded definitions for new and delete.
39 
40 #ifdef LAMARC_QA_MEMORYTRACE            // Under construction - not running in current distribution.
41 
operator new(size_t size)42 void * operator new (size_t size)
43 {
44     void * ptr = malloc(size);
45 #ifdef LAMARC_QA_MEMORYPRINT
46     printf("operator new: %lu bytes at %p\n", size, ptr);
47 #endif // LAMARC_QA_MEMORYPRINT
48     if(!ptr)
49         printf("operator new: OUT OF MEMORY\n");
50     return ptr;
51 }
52 
53 //------------------------------------------------------------------------------------
54 
operator new[](size_t size)55 void * operator new [] (size_t size)
56 {
57     void * ptr = malloc(size);
58 #ifdef LAMARC_QA_MEMORYPRINT
59     printf("operator new []: %lu bytes at %p\n", size, ptr);
60 #endif // LAMARC_QA_MEMORYPRINT
61     if(!ptr)
62     {
63         printf("operator new []: OUT OF MEMORY\n");
64     }
65     return ptr;
66 }
67 
68 //------------------------------------------------------------------------------------
69 
operator delete(void * ptr)70 void operator delete (void * ptr)
71 {
72 #ifdef LAMARC_QA_MEMORYPRINT
73     printf("operator delete: %p\n", ptr);
74 #endif // LAMARC_QA_MEMORYPRINT
75     if(ptr)
76         free(ptr);
77     else
78         printf("operator delete: NULL POINTER\n");
79 }
80 
81 //------------------------------------------------------------------------------------
82 
operator delete[](void * ptr)83 void operator delete [] (void * ptr)
84 {
85 #ifdef LAMARC_QA_MEMORYPRINT
86     printf("operator delete []: %p\n", ptr);
87 #endif // LAMARC_QA_MEMORYPRINT
88     if(ptr)
89         free(ptr);
90     else
91         printf("operator delete []: NULL POINTER\n");
92 }
93 
94 //------------------------------------------------------------------------------------
95 
operator delete(void * ptr,size_t size)96 void operator delete (void * ptr, size_t size)
97 {
98 #ifdef LAMARC_QA_MEMORYPRINT
99     printf("operator delete: %lu bytes at %p\n", size, ptr);
100 #endif // LAMARC_QA_MEMORYPRINT
101     if(ptr)
102         free(ptr);
103     else
104         printf("operator delete: NULL POINTER\n");
105 }
106 
107 //------------------------------------------------------------------------------------
108 
operator delete[](void * ptr,size_t size)109 void operator delete [] (void * ptr, size_t size)
110 {
111 #ifdef LAMARC_QA_MEMORYPRINT
112     printf("operator delete []: %lu bytes at %p\n", size, ptr);
113 #endif // LAMARC_QA_MEMORYPRINT
114     if(ptr)
115         free(ptr);
116     else
117         printf("operator delete []: NULL POINTER\n");
118 }
119 
120 //------------------------------------------------------------------------------------
121 
operator new(size_t size,char * file,unsigned int line)122 void * operator new (size_t size, char * file, unsigned int line)
123 {
124     void * ptr = malloc(size);
125     printf("operator NEW: %lu bytes at %p, File: %s, Line: %u\n", size, ptr, file, line);
126     if(!ptr)
127         printf("operator NEW: OUT OF MEMORY\n");
128     return ptr;
129 }
130 
131 //------------------------------------------------------------------------------------
132 
operator new[](size_t size,char * file,unsigned int line)133 void * operator new [] (size_t size, char * file, unsigned int line)
134 {
135     void * ptr = malloc(size);
136     printf("operator NEW []: %lu bytes at %p, File: %s, Line: %u\n", size, ptr, file, line);
137     if(!ptr)
138         printf("operator NEW []: OUT OF MEMORY\n");
139     return ptr;
140 }
141 
142 #endif // LAMARC_QA_MEMORYTRACE - End of experimental code - not running in current distribution.
143 
144 //------------------------------------------------------------------------------------
145 // Constructor and expansion of StartDynameter().
146 
LocalDynameter(const char * const srcFunctionName,const char * const srcFileName,const unsigned int srcLineNumber,const unsigned int globalDynameterIdx)147 LocalDynameter::LocalDynameter(const char * const srcFunctionName, // Supplied by __PRETTY_FUNCTION__ macro
148                                const char * const srcFileName,     // Supplied by __FILE__ macro
149                                const unsigned int srcLineNumber,   // Supplied by __LINE__ macro
150                                // Next argument supplied by macro expansion as: DYNACOUNTER_START + __COUNTER__ .
151                                // DYNACOUNTER_START must be defined at the beginning of each file that uses it
152                                // to the starting range of indices for that file to use.  Then __COUNTER__ increments
153                                // automatically through the range.  Count the number of calls in each file so the next
154                                // file to use tracing/metering can initialize its range to start with the next value
155                                // (<last-in-current-file> + 1).
156                                const unsigned int globalDynameterIdx)
157     :
158     m_fcnEntryTime(rdtsc()),            // Clock count at initial function entry.
159     m_srcFunctionName(srcFunctionName), // Function name decorated with class and argument signature.
160     m_srcFileName(srcFileName),         // Source file name.
161 #if (LAMARC_QA_DYNAMETER >= 3u)
162     m_tracePrintoutOK(true),            // Controls suppression of trace printout (elision).
163 #endif // (LAMARC_QA_DYNAMETER >= 3u)
164     m_calleeRuntime(0u),                // Accumulated runtime of callees.
165     m_globalDynameterPtr(NULL),         // Pointer to GlobalDynameter object in GlobalDynameterArray.
166     m_srcLineNumber(srcLineNumber)      // Source line number.
167 {
168     // Clocks since last clock tracing/metering sample before current call.
169     const unsigned long long int sinceLastBeforeCall = m_fcnEntryTime - s_lastClock;
170 
171     if(s_currentMeterPtr)
172     {
173         // If call to this function was made from another - ie, not the top-level invocation -
174         // set my "parent Meter pointer" to that parent's LocalDynameter object and bump trace level.
175         m_parentMeterPtr = s_currentMeterPtr;
176 #if (LAMARC_QA_DYNAMETER >= 3u)
177         m_tracePrintoutOK = s_currentMeterPtr->m_tracePrintoutOK;
178         m_traceLevel = s_currentMeterPtr->m_traceLevel + 1u;
179 #endif // (LAMARC_QA_DYNAMETER >= 3u)
180     }
181     else
182     {
183         // If this is first call - ie, at toplevel - set a few variables.
184         m_parentMeterPtr = NULL;
185 
186 #if (LAMARC_QA_DYNAMETER >= 3u)
187         m_traceLevel = 0u;
188         cout << "Trace Count Limit (max num calls to same function): ";
189         cin >> s_traceCountLimit;
190         cout << endl;
191 #endif // (LAMARC_QA_DYNAMETER >= 3u)
192 
193         // Set slots of GlobalDynameterArray to NULL here, in case implicit initialization doesn't.
194         for(unsigned int fcn_idx = 0u; fcn_idx < DYNAMETER_ARRAYSIZE; ++fcn_idx)
195         {
196             s_GlobalDynameterArray[fcn_idx] = NULL;
197         }
198 
199         // Print opening trace messages no matter the range to be traced dynamically.
200         s_traceOut.open("TracePrint.out", ios::out);
201         s_traceOut << scientific << setprecision(6);
202 
203         s_traceOut <<     "Tracing output started:    " << PrintTime(GetTime(), "%c")
204                    << "\n\nDynameter Version:         " << LAMARC_QA_DYNAMETER
205                    <<   "\nCPU_TICKS_PER_SECOND:      " << CPU_TICKS_PER_SECOND
206 #if (LAMARC_QA_DYNAMETER >= 3u)
207                    <<   "\nTrace max funcall limit:   " << s_traceCountLimit
208 #endif // (LAMARC_QA_DYNAMETER >= 3u)
209                    << "\n\nSize of int:               " << sizeof(int)
210                    <<   "\nSize of long int:          " << sizeof(long int)
211                    <<   "\nSize of long long int:     " << sizeof(long long int)
212                    <<   "\nSize of float:             " << sizeof(float)
213                    <<   "\nSize of double:            " << sizeof(double)
214                    <<   "\nSize of long double:       " << sizeof(long double)
215                    <<   "\nSize of int pointer:       " << sizeof(int *)
216                    <<   "\nSize of void pointer:      " << sizeof(void *) << "\n\n";
217     }
218 
219     // And set the "global" current Meter Object pointer to ME (ie, I'M the function currently running).
220     s_currentMeterPtr = this;
221 
222     DebugAssert(globalDynameterIdx < DYNAMETER_ARRAYSIZE, "GlobalDynameterArray index overflow");
223     m_globalDynameterPtr = s_GlobalDynameterArray[globalDynameterIdx];
224 
225     if(m_globalDynameterPtr)
226     {
227         // If already allocated (second or later call), update these data members.
228         if(sinceLastBeforeCall > m_globalDynameterPtr->m_maxSinceLastBeforeCall)
229             m_globalDynameterPtr->m_maxSinceLastBeforeCall = sinceLastBeforeCall;
230         ++m_globalDynameterPtr->m_selfNumberOfCalls;
231     }
232     else
233     {
234         // If GlobalDynameter object has not yet been allocated and entered into GlobalDynameterArray, do so now.
235         // Note that these objects may be entered into array in non-sequential array index order,
236         // which is why we must keep track of total number as well as NULL/non-NULLness of each entry.
237         // Increment global count of GlobalDynameter objects allocated and use value as fcn's order-of-first-call.
238         m_globalDynameterPtr = new GlobalDynameter(srcFunctionName, srcFileName, ++s_GlobalDynameterCount,
239                                                    0u, 0u, sinceLastBeforeCall);
240         s_GlobalDynameterArray[globalDynameterIdx] = m_globalDynameterPtr;
241     }
242 
243     // Increment the total number of traced calls so far - used to print total count at end and
244     // (more importantly) to restrict printing of dynamic tracing/metering information to subset of all traced calls.
245     ++s_totalNumberOfCalls;
246 
247 #if (LAMARC_QA_DYNAMETER >= 3u)
248     if(m_tracePrintoutOK && (m_globalDynameterPtr->m_selfNumberOfCalls <= s_traceCountLimit))
249     {
250         if(s_ellipsisCounter > 0u)
251             print_ellipsis(s_traceOut);
252 
253         print_indentation(s_traceOut);
254         s_traceOut << ">> " << srcFunctionName << " in " << m_srcFileName << ", Line " << srcLineNumber << '\n';
255         print_indentation(s_traceOut);
256 
257         s_traceOut << "|  "
258                    << (static_cast<double>(m_fcnEntryTime - s_startClock) / CPU_TICKS_PER_SECOND)
259                    << " now.  "
260                    << (static_cast<double>(sinceLastBeforeCall) / CPU_TICKS_PER_SECOND)
261                    << " before.  "
262                    << m_globalDynameterPtr->m_selfNumberOfCalls
263                    << " self, "
264                    << s_totalNumberOfCalls
265                    << " total.\n";
266 
267         print_indentation(s_traceOut);
268         s_traceOut << "|\n";
269     }
270     else
271     {
272         ++s_ellipsisCounter;
273         ++s_printoutsElided;
274         m_tracePrintoutOK = false;
275     }
276 #endif // (LAMARC_QA_DYNAMETER >= 3u)
277 
278     s_lastClock = m_fcnEntryTime;
279     if(sinceLastBeforeCall > s_maxSinceLastBeforeCall)
280         s_maxSinceLastBeforeCall = sinceLastBeforeCall;
281 }
282 
283 //------------------------------------------------------------------------------------
284 // Destructor.  Automatically provides functionality of trace-printing on leaving function.
285 
~LocalDynameter()286 LocalDynameter::~LocalDynameter()
287 {
288     // Set on each call (ie, updated to current time).
289     const unsigned long long int nowClock = rdtsc();
290     const unsigned long long int myTotalRuntime = nowClock - m_fcnEntryTime;
291     const unsigned long long int mySelfRuntime = myTotalRuntime - m_calleeRuntime;
292 
293 #if (LAMARC_QA_DYNAMETER >= 3u)
294     if(m_tracePrintoutOK && (m_globalDynameterPtr->m_selfNumberOfCalls <= s_traceCountLimit))
295     {
296         if(s_ellipsisCounter > 0u)
297         {
298             // Trace printout elided last before this call probably was indented one level deeper.
299             ++m_traceLevel;
300             print_ellipsis(s_traceOut);
301             --m_traceLevel;
302         }
303 
304         print_indentation(s_traceOut);
305         // Member variables used since all values printed here must be stored; destructor takes no arguments.
306         s_traceOut << "<< " << m_srcFunctionName << " in " << m_srcFileName << ", Line " << m_srcLineNumber << '\n';
307         print_indentation(s_traceOut);
308 
309         s_traceOut << "   "
310                    << (static_cast<double>(nowClock - s_startClock) / CPU_TICKS_PER_SECOND)
311                    << " now.  "
312                    << (static_cast<double>(nowClock - s_lastClock) / CPU_TICKS_PER_SECOND)
313                    << " before.  "
314                    << (static_cast<double>(m_calleeRuntime) / CPU_TICKS_PER_SECOND)
315                    << " callees.  "
316                    << (static_cast<double>(mySelfRuntime) / CPU_TICKS_PER_SECOND)
317                    << " self.  "
318                    << (static_cast<double>(myTotalRuntime) / CPU_TICKS_PER_SECOND)
319                    << " total.  "
320                    << m_globalDynameterPtr->m_selfNumberOfCalls
321                    << " self, "
322                    << s_totalNumberOfCalls
323                    << " total.\n";
324 
325         print_indentation(s_traceOut);
326         s_traceOut << '\n';
327     }
328     else
329     {
330         ++s_ellipsisCounter;
331         ++s_printoutsElided;
332     }
333 #endif // (LAMARC_QA_DYNAMETER >= 3u)
334 
335     s_lastClock = nowClock;
336 
337     // Insert the information accumulated in this dynamic object into the permanent structure holding global data.
338     // The GlobalDynameter object is assumed to exist (m_globalDynameterPtr is non-NULL) because otherwise 'new' would
339     // have thrown a 'bad_alloc' exception.
340     m_globalDynameterPtr->m_calleeRuntime += m_calleeRuntime;
341     m_globalDynameterPtr->m_selfRuntime += mySelfRuntime;
342 
343     // Pop the metering tool stack: set current object pointer to my parent.
344     s_currentMeterPtr = m_parentMeterPtr;
345 
346     if(m_parentMeterPtr)
347     {
348         // If parent is non-NULL, increment his kid's runtime by my own.
349         m_parentMeterPtr->m_calleeRuntime += myTotalRuntime;
350     }
351     else
352     {
353         // If we've popped back to top-level, ie, the toplevel destructor, write trailer and close file.
354         // Print closing trace messages no matter the range to be traced dynamically.
355         // First write the summary data stored in the GlobalDynameterArray objects.
356         s_traceOut << "\nTrace information on "
357                    << s_GlobalDynameterCount
358                    << " functions traced of "
359                    << DYNAMETER_ARRAYSIZE
360                    << " function slots available.\n";
361 
362         // "Squish" all populated entries down to contiguous set at "low" end of array.
363         // All functions will have slots reserved at compile time for themselves, but
364         // functions not called during this run will contain empty slots in this array.
365         unsigned int target = 0u, source = 0u;
366         while(source < DYNAMETER_ARRAYSIZE)
367         {
368             if(s_GlobalDynameterArray[source]) // Source populated.
369             {
370                 if(s_GlobalDynameterArray[target]) // Source populated; Target populated.
371                 {
372                     if(source == target) // If tracking same slot, bump both indices.
373                         ++source;        // Otherwise bump only target.
374                     ++target;
375                 }
376                 else                    // Source populated; Target empty.
377                 {
378                     s_GlobalDynameterArray[target] = s_GlobalDynameterArray[source]; // Swap source into target.
379                     s_GlobalDynameterArray[source] = NULL;       // "Swap" means "clear source slot".
380                     ++target;                                    // This pair now OK; bump both indices.
381                     ++source;
382                 }
383             }
384             else                        // Source empty; target may be empty or populated.
385             {
386                 ++source;               // Bump only source (look for populated slot).
387             }
388             DebugAssert(target <= source, "Bug in GlobalDynameterArray squisher");
389         }
390         DebugAssert((source == DYNAMETER_ARRAYSIZE) && (target == s_GlobalDynameterCount),
391                     "Bug in GlobalDynameterArray compactor");
392 
393         TablePrinter("Traced functions unordered:",
394                      "FcnsUnsorted.txt");
395 
396         sort(s_GlobalDynameterArray, s_GlobalDynameterArray + s_GlobalDynameterCount, NumSelfCalls);
397         TablePrinter("Traced functions ordered by number of calls:",
398                      "FcnsNumCalls.txt");
399 
400         sort(s_GlobalDynameterArray, s_GlobalDynameterArray + s_GlobalDynameterCount, MaxSinceLastBefore);
401         TablePrinter("Traced functions ordered by max time since last before call:",
402                      "FcnsMaxTimeBefore.txt");
403 
404         sort(s_GlobalDynameterArray, s_GlobalDynameterArray + s_GlobalDynameterCount, CalleeRuntimePerCall);
405         TablePrinter("Traced functions ordered by callee runtime per call:",
406                      "FcnsCalleeTimePerCall.txt");
407 
408         sort(s_GlobalDynameterArray, s_GlobalDynameterArray + s_GlobalDynameterCount, CalleeRuntimeAggregate);
409         TablePrinter("Traced functions ordered by callee runtime, aggregate:",
410                      "FcnsCalleeTimeAggregate.txt");
411 
412         sort(s_GlobalDynameterArray, s_GlobalDynameterArray + s_GlobalDynameterCount, SelfRuntimePerCall);
413         TablePrinter("Traced functions ordered by self runtime (excluding callees) per call:",
414                      "FcnsSelfTimePerCall.txt");
415 
416         sort(s_GlobalDynameterArray, s_GlobalDynameterArray + s_GlobalDynameterCount, SelfRuntimeAggregate);
417         TablePrinter("Traced functions ordered by self runtime (excluding callees), aggregate:",
418                      "FcnsSelfTimeAggregate.txt");
419 
420         sort(s_GlobalDynameterArray, s_GlobalDynameterArray + s_GlobalDynameterCount, TotalRuntimePerCall);
421         TablePrinter("Traced functions ordered by total runtime (self plus callees) per call:",
422                      "FcnsTotalTimePerCall.txt");
423 
424         sort(s_GlobalDynameterArray, s_GlobalDynameterArray + s_GlobalDynameterCount, TotalRuntimeAggregate);
425         TablePrinter("Traced functions ordered by total runtime (self plus callees), aggregate:",
426                      "FcnsTotalTimeAggregate.txt");
427 
428         s_traceOut <<   "\nTracing output done:       " << PrintTime(GetTime(), "%c")
429                    <<   "\nTotal number of calls:     " << s_totalNumberOfCalls
430 #if (LAMARC_QA_DYNAMETER >= 3u)
431                    <<   "\nTrace max funcall limit:   " << s_traceCountLimit
432                    <<   "\nTrace print calls elided:  " << s_printoutsElided
433 #endif // (LAMARC_QA_DYNAMETER >= 3u)
434                    << "\n\nDynameter Version:         " << LAMARC_QA_DYNAMETER
435                    <<   "\nCPU_TICKS_PER_SECOND:      " << CPU_TICKS_PER_SECOND
436                    << "\n\nSize of int:               " << sizeof(int)
437                    <<   "\nSize of long int:          " << sizeof(long int)
438                    <<   "\nSize of long long int:     " << sizeof(long long int)
439                    <<   "\nSize of float:             " << sizeof(float)
440                    <<   "\nSize of double:            " << sizeof(double)
441                    <<   "\nSize of long double:       " << sizeof(long double)
442                    <<   "\nSize of int pointer:       " << sizeof(int *)
443                    <<   "\nSize of void pointer:      " << sizeof(void *)
444                    << "\n\nMax since last trace:      "
445                    << (static_cast<double>(LocalDynameter::s_maxSinceLastBeforeCall) / CPU_TICKS_PER_SECOND)
446                    << " sec (over all function calls).\n";
447 
448         s_traceOut.close();
449     }
450 }
451 
452 //------------------------------------------------------------------------------------
453 // Private utility member function.
454 
455 #if (LAMARC_QA_DYNAMETER >= 3u)
456 
print_indentation(ofstream & stream)457 void LocalDynameter::print_indentation(ofstream & stream)
458 {
459     stream << ' ';
460     for(unsigned int trace_idx = 0u; trace_idx < m_traceLevel; ++trace_idx)
461         stream << "| ";
462 }
463 
464 #endif // (LAMARC_QA_DYNAMETER >= 3u)
465 
466 //------------------------------------------------------------------------------------
467 // Private utility member function.
468 
469 #if (LAMARC_QA_DYNAMETER >= 3u)
470 
print_ellipsis(ofstream & stream)471 void LocalDynameter::print_ellipsis(ofstream & stream)
472 {
473     print_indentation(stream);
474     stream << "... " << s_ellipsisCounter << " trace printouts elided.\n";
475     print_indentation(stream);
476     stream << '\n';
477     s_ellipsisCounter = 0u;
478 }
479 
480 #endif // (LAMARC_QA_DYNAMETER >= 3u)
481 
482 //------------------------------------------------------------------------------------
483 // Private utility member function.
484 
TablePrinter(const string heading,const string filename)485 void LocalDynameter::TablePrinter(const string heading, const string filename)
486 {
487     ofstream os;
488     os.open(filename.c_str(), ios::out);
489     os << scientific << setprecision(6);
490     os << heading << '\n';
491 
492     for(unsigned int fcn_idx = 0u; fcn_idx < s_GlobalDynameterCount; ++fcn_idx)
493     {
494         GlobalDynameter * pGlobalDynameter = s_GlobalDynameterArray[fcn_idx];
495         DebugAssert(pGlobalDynameter, "Bug in GlobalDynameterArray display");
496 
497         const unsigned long long int selfNumCalls = pGlobalDynameter->m_selfNumberOfCalls;
498         const unsigned long long int calleeRuntime = pGlobalDynameter->m_calleeRuntime;
499         const unsigned long long int selfRuntime = pGlobalDynameter->m_selfRuntime;
500         const unsigned long long int maxSinceLastBeforeCall = pGlobalDynameter->m_maxSinceLastBeforeCall;
501 
502         os << '\n' << fcn_idx
503            << ":\t" << pGlobalDynameter->m_srcFunctionName
504            << " in file " << pGlobalDynameter->m_srcFileName
505            << "\n\t" << selfNumCalls << " calls (order "
506            << pGlobalDynameter->m_ordinalCount << ")  "
507            << (static_cast<double>(maxSinceLastBeforeCall) / CPU_TICKS_PER_SECOND)
508            << " before.  "
509            << (static_cast<double>(calleeRuntime / selfNumCalls) / CPU_TICKS_PER_SECOND)
510            << '/'
511            << (static_cast<double>(calleeRuntime) / CPU_TICKS_PER_SECOND)
512            << " callees.  "
513            << (static_cast<double>(selfRuntime / selfNumCalls) / CPU_TICKS_PER_SECOND)
514            << '/'
515            << (static_cast<double>(selfRuntime) / CPU_TICKS_PER_SECOND)
516            << " self.  "
517            << (static_cast<double>((selfRuntime + calleeRuntime) / selfNumCalls) / CPU_TICKS_PER_SECOND)
518            << '/'
519            << (static_cast<double>(selfRuntime + calleeRuntime) / CPU_TICKS_PER_SECOND)
520            << " total.\n";
521     }
522 
523     os.close();
524 }
525 
526 //------------------------------------------------------------------------------------
527 // Constructor for GlobalDynameter called by LocalDynameter constructor to store per-function results.
528 
GlobalDynameter(const char * const srcFunctionName,const char * const srcFileName,const unsigned int ordinalCount,const unsigned long long int calleeRuntime,const unsigned long long int selfRuntime,const unsigned long long int maxSinceLastBeforeCall)529 GlobalDynameter::GlobalDynameter(const char * const srcFunctionName,
530                                  const char * const srcFileName,
531                                  const unsigned int ordinalCount,
532                                  const unsigned long long int calleeRuntime,
533                                  const unsigned long long int selfRuntime,
534                                  const unsigned long long int maxSinceLastBeforeCall)
535     :
536     m_srcFunctionName(srcFunctionName),               // Function name decorated with class and argument signature.
537     m_srcFileName(srcFileName),                       // Source file name.
538     m_ordinalCount(ordinalCount),                     // Ordinal count of this function's call of all traced.
539     m_calleeRuntime(calleeRuntime),                   // Accumulated runtime of callees.
540     m_selfRuntime(selfRuntime),                       // Accumulated runtime of this function (not including callees).
541     m_maxSinceLastBeforeCall(maxSinceLastBeforeCall), // Max time since last timing measurement (CPU cycles).
542     m_selfNumberOfCalls(1u)                           // Count of number of times this function called.
543 { }
544 
545 //------------------------------------------------------------------------------------
546 // Sorts by number of self calls (decreasing order).
547 
NumSelfCalls(const GlobalDynameter * const x_ptr,const GlobalDynameter * const y_ptr)548 bool NumSelfCalls(const GlobalDynameter * const x_ptr, const GlobalDynameter * const y_ptr)
549 {
550     return x_ptr->m_selfNumberOfCalls > y_ptr->m_selfNumberOfCalls;
551 }
552 
553 //------------------------------------------------------------------------------------
554 // Sorts by max time since last trace probe before current call (decreasing order).
555 
MaxSinceLastBefore(const GlobalDynameter * const x_ptr,const GlobalDynameter * const y_ptr)556 bool MaxSinceLastBefore(const GlobalDynameter * const x_ptr, const GlobalDynameter * const y_ptr)
557 {
558     return x_ptr->m_maxSinceLastBeforeCall > y_ptr->m_maxSinceLastBeforeCall;
559 }
560 
561 //------------------------------------------------------------------------------------
562 // Sorts by callee runtime per call (total over all callees) (decreasing order).
563 
CalleeRuntimePerCall(const GlobalDynameter * const x_ptr,const GlobalDynameter * const y_ptr)564 bool CalleeRuntimePerCall(const GlobalDynameter * const x_ptr, const GlobalDynameter * const y_ptr)
565 {
566     return (x_ptr->m_calleeRuntime / x_ptr->m_selfNumberOfCalls)
567         > (y_ptr->m_calleeRuntime / y_ptr->m_selfNumberOfCalls);
568 }
569 
570 //------------------------------------------------------------------------------------
571 // Sorts by callee runtime aggregate (summed over all calls) (decreasing order).
572 
CalleeRuntimeAggregate(const GlobalDynameter * const x_ptr,const GlobalDynameter * const y_ptr)573 bool CalleeRuntimeAggregate(const GlobalDynameter * const x_ptr, const GlobalDynameter * const y_ptr)
574 {
575     return x_ptr->m_calleeRuntime > y_ptr->m_calleeRuntime;
576 }
577 
578 //------------------------------------------------------------------------------------
579 // Sorts by self runtime per call (excluding all callees) (decreasing order).
580 
SelfRuntimePerCall(const GlobalDynameter * const x_ptr,const GlobalDynameter * const y_ptr)581 bool SelfRuntimePerCall(const GlobalDynameter * const x_ptr, const GlobalDynameter * const y_ptr)
582 {
583     return (x_ptr->m_selfRuntime / x_ptr->m_selfNumberOfCalls) > (y_ptr->m_selfRuntime / y_ptr->m_selfNumberOfCalls);
584 }
585 
586 //------------------------------------------------------------------------------------
587 // Sorts by self runtime aggregate (excluding callees, summed over all calls) (decreasing order).
588 
SelfRuntimeAggregate(const GlobalDynameter * const x_ptr,const GlobalDynameter * const y_ptr)589 bool SelfRuntimeAggregate(const GlobalDynameter * const x_ptr, const GlobalDynameter * const y_ptr)
590 {
591     return x_ptr->m_selfRuntime > y_ptr->m_selfRuntime;
592 }
593 
594 //------------------------------------------------------------------------------------
595 // Sorts by total runtime per call (sum of self plus callee runtime) (decreasing order).
596 
TotalRuntimePerCall(const GlobalDynameter * const x_ptr,const GlobalDynameter * const y_ptr)597 bool TotalRuntimePerCall(const GlobalDynameter * const x_ptr, const GlobalDynameter * const y_ptr)
598 {
599     return
600         ((x_ptr->m_selfRuntime + x_ptr->m_calleeRuntime) / x_ptr->m_selfNumberOfCalls)
601         >
602         ((y_ptr->m_selfRuntime + y_ptr->m_calleeRuntime) / y_ptr->m_selfNumberOfCalls);
603 }
604 
605 //------------------------------------------------------------------------------------
606 // Sorts by total runtime aggregate (self plus callee, summed over all calls) (decreasing order).
607 
TotalRuntimeAggregate(const GlobalDynameter * const x_ptr,const GlobalDynameter * const y_ptr)608 bool TotalRuntimeAggregate(const GlobalDynameter * const x_ptr, const GlobalDynameter * const y_ptr)
609 {
610     return (x_ptr->m_selfRuntime + x_ptr->m_calleeRuntime) > (y_ptr->m_selfRuntime + y_ptr->m_calleeRuntime);
611 }
612 
613 //------------------------------------------------------------------------------------
614 
615 #endif // LAMARC_QA_DYNAMETER
616 
617 //------------------------------------------------------------------------------------
618 // CPU cycle counter used by tracing/metering and other timing functions.
619 
620 #if defined(LAMARC_QA_DYNAMETER) || defined(LAMARC_QA_TIMINGTEST)
621 
rdtsc()622 unsigned long long int rdtsc()
623 {
624     unsigned int a, d;
625     __asm__ volatile("rdtsc" : "=a" (a), "=d" (d));
626     return (static_cast<unsigned long long int>(a)) | ((static_cast<unsigned long long int>(d)) << 32);
627 }
628 
629 #endif // defined(LAMARC_QA_DYNAMETER) || defined(LAMARC_QA_TIMINGTEST)
630 
631 //____________________________________________________________________________________
632