source: trunk/src/odinprof/profiler.cpp@ 10366

Last change on this file since 10366 was 7537, checked in by phaller, 24 years ago

.

File size: 23.4 KB
Line 
1/* $Id: profiler.cpp,v 1.4 2001-12-03 13:13:06 phaller Exp $ */
2/*
3 * Project Odin Software License can be found in LICENSE.TXT
4 * Execution Trace Profiler
5 *
6 * Copyright 2001 Patrick Haller <patrick.haller@innotek.de>
7 *
8 */
9
10
11/****************************************************************************
12 * To Do
13 ****************************************************************************
14
15 - write result in java.prof style
16 - find tool to nicely work on the output files
17 - fix wrong timestamps (use 64-bit arithmetic and recalculate to ms)
18 */
19
20
21/****************************************************************************
22 * includes
23 ****************************************************************************/
24
25#define INCL_DOSERRORS
26#define INCL_DOSPROCESS
27#include <os2.h>
28
29#include <os2sel.h>
30#include <stdlib.h>
31#include <string.h>
32#include <stdio.h>
33#include <math.h>
34#include <direct.h>
35#include <process.h>
36
37#include "symfile.h"
38
39
40/****************************************************************************
41 * local class definitions
42 ****************************************************************************/
43
44
45typedef struct
46{
47 ULONG ulID;
48 void* pObject;
49} HASHTABLEENTRYID, *PHASHTABLEENTRYID;
50
51
52class CProfilerHashtableLookup : public CHashtableLookup
53{
54 public:
55 CProfilerHashtableLookup(int iInitialSize);
56 PHASHTABLEENTRYID addElement(ULONG ulID, void *pObject);
57 void* removeElement(ULONG ulID);
58 void* getElement(ULONG ulID);
59 int getElementMap(PHASHTABLEENTRYID pBuffer);
60
61 protected:
62 void setSize0(int iNewSize);
63};
64
65
66/****************************************************************************
67 * definitions
68 ****************************************************************************/
69
70typedef struct tagThreadProfileEntry
71{
72 unsigned long EIP; /* currently only 32-bit segments are traced */
73 char* pszModule; /* name of the current module */
74 /* name of the current function or (caller;callee pair) */
75 char* pszFunction;
76 unsigned long ulCalls;
77 unsigned long ulTimeMinimum;
78 unsigned long ulTimeMaximum;
79 unsigned long ulTimeTotal;
80} PROFILEENTRY, *PPROFILEENTRY;
81
82
83#define NUM_RECORDS 256
84typedef struct tagThreadProfilerDataBlock
85{
86 unsigned long ulCalldepth; // current call depth
87 unsigned long ret[NUM_RECORDS]; // last saved return address
88 unsigned long callStack[NUM_RECORDS]; // call stack (CS:) EIP records
89 unsigned long enterTimeHi[NUM_RECORDS]; // machine timestamps of function entry
90 unsigned long enterTimeLo[NUM_RECORDS];
91 unsigned long overheadTimeHi[NUM_RECORDS]; // machine timestamps of function entry
92 unsigned long overheadTimeLo[NUM_RECORDS];
93
94 // Hashtable to record the PROFILEENTRY records
95 CProfilerHashtableLookup* pHashtable;
96
97} PROFILERBLOCK, *PPROFILERBLOCK;
98
99
100/****************************************************************************
101 * module local variables
102 ****************************************************************************/
103
104static SymbolFilePool* pSymPool = new SymbolFilePool();
105static CProfilerHashtableLookup* pProfileMap = new CProfilerHashtableLookup(1021);
106static PPROFILERBLOCK* ppTLS = 0; /* thread local storage */
107static BOOL fIsProfilerEnabled = FALSE; /* enable / disable hook*/
108static CHAR szWorkingDirectory[260] = ".";
109
110extern void _System odin_ProfileHook32Bottom();
111extern void _System _ProfileGetTimestamp(PULONG hi, PULONG lo);
112
113
114/****************************************************************************
115 * local class definitions
116 ****************************************************************************/
117
118static inline unsigned long _Optlink hashcodeEIP(int iRing, ULONG eip)
119{
120 return eip % iRing;
121}
122
123
124CProfilerHashtableLookup::CProfilerHashtableLookup(int iInitialSize)
125 : CHashtableLookup(iInitialSize)
126{
127}
128
129
130PHASHTABLEENTRYID CProfilerHashtableLookup::addElement(ULONG ulID, void *pObject)
131{
132 // get slot number
133 unsigned long ulHash = hashcodeEIP(iSize, ulID);
134
135 // create entry
136 PHASHTABLEENTRYID pHTE = (PHASHTABLEENTRYID)malloc(sizeof(HASHTABLEENTRYID));
137 pHTE->ulID = ulID;
138 pHTE->pObject = pObject;
139
140 // check if slot has a list object already
141 if (parrLists[ulHash] == NULL)
142 parrLists[ulHash] = new CLinearList();
143
144 parrLists[ulHash]->addLast(pHTE);
145
146 // count allocated elements
147 iElements++;
148
149 return pHTE;
150}
151
152
153void* CProfilerHashtableLookup::removeElement(ULONG ulID)
154{
155 // get slot number
156 unsigned long ulHash = hashcodeEIP(iSize, ulID);
157
158 // check if slot is occupied
159 if (parrLists[ulHash] == NULL)
160 // signal not found
161 return NULL;
162
163 // search the list
164 PLINEARLISTENTRY pLLE = parrLists[ulHash]->getFirst();
165 if (pLLE == NULL)
166 // signal not found
167 return NULL;
168
169 // iterate over the list
170 while(pLLE)
171 {
172 PHASHTABLEENTRYID pHTE = (PHASHTABLEENTRYID)pLLE->pObject;
173
174 if (pHTE->ulID == ulID)
175 {
176 // save old object pointer
177 void* pTemp = pHTE->pObject;
178 free(pHTE);
179
180 // found the correct entry
181 parrLists[ulHash]->removeElement(pLLE);
182
183 // count allocated elements
184 iElements--;
185
186 // return old object pointer to signal success
187 return pTemp;
188 }
189
190 pLLE = pLLE->pNext;
191 }
192
193 // failed
194 return NULL;
195}
196
197
198void* CProfilerHashtableLookup::getElement(ULONG ulID)
199{
200 // get slot number
201 unsigned long ulHash = hashcodeEIP(iSize, ulID);
202
203 CLinearList *pLL = parrLists[ulHash];
204
205 // check if slot is occupied
206 if (pLL == NULL)
207 // signal not found
208 return NULL;
209
210 // search the list
211 PLINEARLISTENTRY pLLE = pLL->getFirst();
212 if (pLLE == NULL)
213 // signal not found
214 return NULL;
215
216 // iterate over the list
217 while(pLLE)
218 {
219 PHASHTABLEENTRYID pHTE = (PHASHTABLEENTRYID)pLLE->pObject;
220
221 // quickly compare 1st character for equality
222 // before doing the strcmp call
223 if (pHTE->ulID == ulID)
224 {
225 // return result
226 return pHTE->pObject;
227 }
228
229 pLLE = pLLE->pNext;
230 }
231
232 // failed
233 return NULL;
234}
235
236
237int CProfilerHashtableLookup::getElementMap(PHASHTABLEENTRYID pBuffer)
238{
239 int iIndex = 0;
240
241 // iterate over all registered entries and dump them to the buffer
242 // giving the caller direct access to the hashtable internals.
243 for (int i = 0;
244 i < iSize;
245 i++)
246 {
247 // check if slot was occupied
248 if (parrLists[i] != NULL)
249 {
250 // walk along any entry in that linear list
251 PLINEARLISTENTRY pLLE = parrLists[i]->getFirst();
252
253 while (pLLE)
254 {
255 PHASHTABLEENTRYID pHTE = (PHASHTABLEENTRYID)pLLE->pObject;
256 memcpy(&pBuffer[iIndex], pHTE, sizeof( HASHTABLEENTRYID ) );
257 iIndex++;
258
259 pLLE = parrLists[i]->getNext(pLLE);
260 }
261 }
262 }
263
264 // return number of elements copied
265 return iIndex;
266}
267
268
269void CProfilerHashtableLookup::setSize0(int iNewSize)
270{
271 // check if rehashing is necessary at all
272 if (iSize == iNewSize)
273 return;
274
275 // save old array, allocate new array
276 CLinearList** parrNew = new CLinearList* [iNewSize];
277 memset(parrNew,
278 0,
279 sizeof(CLinearList*) * iNewSize);
280
281 // convert all the slot lists
282 for (int i = 0;
283 i < iSize;
284 i++)
285 {
286 // check if slot was occupied
287 if (parrLists[i] != NULL)
288 {
289 // iterate over the slot
290 PLINEARLISTENTRY pLLE = parrLists[i]->getFirst();
291 PHASHTABLEENTRYID pHTE;
292 unsigned long ulHash;
293
294 while(pLLE)
295 {
296 // calculate new hashcode for the entry
297 pHTE = (PHASHTABLEENTRYID)pLLE->pObject;
298 ulHash = hashcodeEIP(iNewSize, pHTE->ulID);
299
300 // reinsert the pHTE into new slot
301 if (parrNew[ulHash] == NULL)
302 parrNew[ulHash] = new CLinearList();
303
304 parrNew[ulHash]->addLast( (void*)pHTE );
305
306 pLLE=pLLE->pNext;
307 }
308
309 // kill the slot
310 delete parrLists[i];
311 parrLists[i] = NULL;
312 }
313 }
314
315 // swap the tables
316 iSize = iNewSize;
317 delete [] parrLists;
318
319 parrLists = parrNew;
320}
321
322
323/****************************************************************************
324 * implementation
325 ****************************************************************************/
326
327static PPROFILERBLOCK inline getProfilerBlock(void)
328{
329 if ( ( (ULONG)ppTLS != 0) && ( (ULONG)ppTLS != 0xffffffff) )
330 return *ppTLS;
331
332 // is TLS already available?
333 if (ppTLS == NULL)
334 {
335 // allocate thread local storage
336 USHORT sel = RestoreOS2FS();
337 APIRET rc = DosAllocThreadLocalMemory(1, (PULONG*)&ppTLS);
338 SetFS(sel);
339
340 if (rc != NO_ERROR)
341 ppTLS = (PPROFILERBLOCK*)0xffffffff; // mark as invalid
342 }
343
344 if ((ULONG)ppTLS == 0xffffffff)
345 // profiling is not available
346 return NULL;
347
348 // allocate data structure
349 {
350 USHORT sel = RestoreOS2FS();
351
352 // Note: must be DosAllocMem because the CRT would automatically close
353 // all heap objects on termination
354 // *ppTLS = (PPROFILERBLOCK)malloc( sizeof(PROFILERBLOCK) );
355 int iSize = 4096 * (1 + sizeof(PROFILERBLOCK) / 4096);
356 APIRET rc = DosAllocMem((PPVOID)ppTLS, iSize, PAG_COMMIT | PAG_READ | PAG_WRITE);
357 SetFS(sel);
358
359 if (rc == NO_ERROR)
360 {
361 memset(*ppTLS, 0, sizeof(PROFILERBLOCK) );
362
363 PPROFILERBLOCK pPB = (PPROFILERBLOCK)*ppTLS;
364
365 // experiment: thread-local profile
366 // pPB->pHashtable = new CProfilerHashtableLookup(1021);
367 pPB->pHashtable = pProfileMap;
368 }
369 }
370
371 return (PPROFILERBLOCK)*ppTLS;
372}
373
374
375
376/***********************************************************************
377* Name : APIRET QueryModuleFromEIP
378* Funktion : Determine which module resides at 0:EIP
379* Parameter :
380* Variablen :
381* Ergebnis : Rckgabewert ans Betriebssystem
382* Bemerkung :
383*
384* Autor : Patrick Haller [Samstag, 28.10.1995 14.55.13]
385***********************************************************************/
386
387APIRET APIENTRY DosQueryModFromEIP (PULONG pulModule,
388 PULONG pulObject,
389 ULONG ulBufferLength,
390 PSZ pszBuffer,
391 PULONG pulOffset,
392 ULONG ulEIP);
393
394static void i_ProfileQuerySymbol(ULONG eip_function,
395 char **ppszModule,
396 char **ppszFunction)
397{
398 APIRET rc; /* API returncode */
399 ULONG ulModule; /* module number */
400 ULONG ulObject; /* object number within the module */
401 CHAR szModule[260]; /* buffer for the module name */
402 ULONG ulOffset; /* offset within the object within the module */
403 ULONG ulSymbolOffset;
404
405 rc = DosQueryModFromEIP(&ulModule,
406 &ulObject,
407 sizeof(szModule),
408 szModule,
409 &ulOffset,
410 eip_function);
411 if (rc == NO_ERROR)
412 {
413 CHAR szFunction[128];
414 *ppszModule = strdup(szModule);
415
416 // @@@PH replace by symbol file lookup
417 if (FALSE == pSymPool->getSymbolName(szModule,
418 ulObject,
419 ulOffset,
420 szFunction,
421 sizeof(szFunction),
422 &ulSymbolOffset) )
423 {
424 sprintf(szFunction,
425 "func_#%08xh",
426 eip_function);
427 }
428 *ppszFunction = strdup(szFunction);
429 }
430 else
431 {
432 // neither module name nor function could be determined, so
433 // we can just provide these values here.
434 CHAR szFunction[40];
435 *ppszModule = "<unknown module>";
436 sprintf(szFunction,
437 "func_#%08xh",
438 eip_function);
439 *ppszFunction = strdup(szFunction);
440 }
441}
442
443
444
445//
446// @@@PH
447// Note: prevent compiler from generating hooks in these functions here!
448// -> is there any pragma to prevent this?
449// -> alternative: place it in the makefile accordingly w/ "/Gh-"
450
451ULONG _System C_ProfileHook32Enter(ULONG eip_function, ULONG ret, ULONG time_hi, ULONG time_lo)
452{
453 if (!fIsProfilerEnabled)
454 // tell low level procedure not to replace the return address
455 // so bottom half will never be called
456 return ret;
457
458 USHORT sel = RestoreOS2FS();
459 // DosEnterCritSec();
460
461
462 // "call _ProfileHook32" has 5 bytes, adjust here
463 eip_function -= 5;
464
465 // 1 - get profiler per-thread-data-block
466 PPROFILERBLOCK pPB = getProfilerBlock();
467 if (NULL == pPB)
468 // tell low level procedure not to replace the return address
469 // so bottom half will never be called
470 return ret;
471
472 // 2 - save return address and call stack entry
473 int i = pPB->ulCalldepth;
474 pPB->ret[i] = ret;
475 pPB->callStack[i] = eip_function;
476
477 // 3 - check if the database has an entry with the current EIP
478 // @@@PH need to synchronize access!
479 PPROFILEENTRY pPE = (PPROFILEENTRY) pPB->pHashtable->getElement(eip_function);
480
481 // 3.1 - if not, create new entry
482 if (NULL == pPE)
483 {
484 pPE = (PPROFILEENTRY)malloc( sizeof(PROFILEENTRY) );
485
486 i_ProfileQuerySymbol(eip_function,
487 &pPE->pszModule,
488 &pPE->pszFunction);
489
490 pPE->EIP = eip_function;
491 // pPE->pszModule = NULL; // @@@PH
492 // pPE->pszFunction = NULL; // @@@PH
493 pPE->ulCalls = 0;
494 pPE->ulTimeMaximum = 0;
495 pPE->ulTimeMinimum = 0xffffffff;
496 pPE->ulTimeTotal = 0;
497
498 // add to the hashtable
499 // @@@PH need to synchronize access!
500 pPB->pHashtable->addElement(eip_function, pPE);
501 }
502
503 // update available statistical data
504 pPE->ulCalls++;
505
506 // register call and skip to the next empty slot
507 pPB->ulCalldepth++;
508
509 // DosExitCritSec();
510 SetFS(sel);
511
512 // save overhead "timestamp"
513 pPB->overheadTimeHi[i] = time_hi;
514 pPB->overheadTimeLo[i] = time_lo;
515
516 // get "compensated" timestamp
517 // -> keep the time between entering the function and
518 // exiting it as low as possible, yet the error will definately
519 // accumulate for the "higher" callers
520 _ProfileGetTimestamp(&pPB->enterTimeHi[i],
521 &pPB->enterTimeLo[i]);
522
523 // tell low level procedure to replace the return address
524 return (ULONG)&odin_ProfileHook32Bottom;
525}
526
527
528ULONG _System C_ProfileHook32Exit(ULONG time_hi, ULONG time_lo)
529{
530 // 1 - get profiler per-thread-data-block
531 PPROFILERBLOCK pPB = getProfilerBlock();
532 if (NULL == pPB)
533 // Oops, we're now in deep trouble! The return address will not be
534 // available, so we're definately gonna crash!
535 return NULL;
536
537 USHORT sel = RestoreOS2FS();
538 // DosEnterCritSec();
539
540 // register call exit
541 pPB->ulCalldepth--;
542
543 // 3.2 - add gathered statistical values to the entry
544 int i = pPB->ulCalldepth;
545 ULONG eip_function = pPB->callStack[i];
546 PPROFILEENTRY pPE = (PPROFILEENTRY) pPB->pHashtable->getElement(eip_function);
547
548 // get another overhead timestamp
549 ULONG ulOverheadHi;
550 ULONG ulOverheadLo;
551 _ProfileGetTimestamp(&ulOverheadHi,
552 &ulOverheadLo);
553
554 // If we're the lowest method on the call stack, the timing is accurate.
555 // If we came back from a subsequent call, we have to compensate the
556 // acumulated time of the lower-level calls and pass it up to the
557 // previous caller.
558
559 // lowest caller?
560 if (pPB->overheadTimeLo[i] != 0)
561 {
562 // No -> got to compensate
563 }
564
565 // calculate and save time difference spent in profiler
566
567 if (pPE)
568 {
569 ULONG ulDiffHi = time_hi - pPB->enterTimeHi[i];
570 ULONG ulDiffLo = time_lo - pPB->enterTimeLo[i];
571
572 // test for wrap around
573 if (ulDiffHi)
574 ulDiffLo = ~ulDiffLo;
575
576 // timestamps are in "cpu cycles"
577 // ULONG ulTime = (ulDiffHi >> 4) | (ulDiffLo << 28);
578 ULONG ulTime = ulDiffLo;
579
580 if (pPE->ulTimeMinimum > ulTime)
581 pPE->ulTimeMinimum = ulTime;
582
583 if (pPE->ulTimeMaximum < ulTime)
584 pPE->ulTimeMaximum = ulTime;
585
586 pPE->ulTimeTotal += ulTime;
587 }
588
589 // DosExitCritSec();
590 SetFS(sel);
591
592 // 2 - return saved return address
593 return pPB->ret[pPB->ulCalldepth];
594}
595
596
597// query if the profiler is enabled
598BOOL _System ProfilerIsEnabled(void)
599{
600 return fIsProfilerEnabled;
601}
602
603
604// enable / disable the profiler hook
605void _System ProfilerEnable(BOOL fState)
606{
607 fIsProfilerEnabled = fState;
608}
609
610
611// initialize the profiler (i. e. working directory)
612void _System ProfilerInitialize(void)
613{
614 USHORT sel = RestoreOS2FS();
615
616 // determine where to write the profile data to
617 // (get the current working directory)
618 getcwd(szWorkingDirectory, sizeof(szWorkingDirectory) );
619
620 // @@@PH
621 // try to determine the measurement overhead
622 // (difficult due to non-deterministic behaviour
623 // of the hashtable lookups and dynamic symbol loading, etc.)
624 // We'd have to split the hook function into three parts:
625 // - lookup and prepare the call
626 // - enter call
627 // - exit call
628 // This however, still leaves us with wrong timing for the parent functions.
629
630 SetFS(sel);
631}
632
633
634// terminate the profiler
635void _System ProfilerTerminate(void)
636{
637 USHORT sel = RestoreOS2FS();
638
639 // disable the profiler
640 ProfilerEnable(FALSE);
641
642 SetFS(sel);
643}
644
645
646int _Optlink sortHashtableEntries0(const void *arg1,const void *arg2)
647{
648 PHASHTABLEENTRYID pHTE1 = (PHASHTABLEENTRYID)arg1;
649 PHASHTABLEENTRYID pHTE2 = (PHASHTABLEENTRYID)arg2;
650
651 PPROFILEENTRY p1 = (PPROFILEENTRY)pHTE1->pObject;
652 PPROFILEENTRY p2 = (PPROFILEENTRY)pHTE2->pObject;
653
654 return strcmp(p1->pszFunction, p2->pszFunction);
655}
656
657int _Optlink sortHashtableEntries1(const void *arg1,const void *arg2)
658{
659 PHASHTABLEENTRYID pHTE1 = (PHASHTABLEENTRYID)arg1;
660 PHASHTABLEENTRYID pHTE2 = (PHASHTABLEENTRYID)arg2;
661
662 PPROFILEENTRY p1 = (PPROFILEENTRY)pHTE1->pObject;
663 PPROFILEENTRY p2 = (PPROFILEENTRY)pHTE2->pObject;
664
665 return p1->ulTimeTotal - p2->ulTimeTotal;
666}
667
668int _Optlink sortHashtableEntries2(const void *arg1,const void *arg2)
669{
670 PHASHTABLEENTRYID pHTE1 = (PHASHTABLEENTRYID)arg1;
671 PHASHTABLEENTRYID pHTE2 = (PHASHTABLEENTRYID)arg2;
672
673 PPROFILEENTRY p1 = (PPROFILEENTRY)pHTE1->pObject;
674 PPROFILEENTRY p2 = (PPROFILEENTRY)pHTE2->pObject;
675
676 return p1->ulCalls - p2->ulCalls;
677}
678
679int _Optlink sortHashtableEntries3(const void *arg1,const void *arg2)
680{
681 PHASHTABLEENTRYID pHTE1 = (PHASHTABLEENTRYID)arg1;
682 PHASHTABLEENTRYID pHTE2 = (PHASHTABLEENTRYID)arg2;
683
684 PPROFILEENTRY p1 = (PPROFILEENTRY)pHTE1->pObject;
685 PPROFILEENTRY p2 = (PPROFILEENTRY)pHTE2->pObject;
686
687 unsigned long int iAvg1 = p1->ulTimeTotal / p1->ulCalls;
688 unsigned long int iAvg2 = p2->ulTimeTotal / p2->ulCalls;
689
690 return iAvg1 - iAvg2;
691}
692
693int _Optlink sortHashtableEntries4(const void *arg1,const void *arg2)
694{
695 PHASHTABLEENTRYID pHTE1 = (PHASHTABLEENTRYID)arg1;
696 PHASHTABLEENTRYID pHTE2 = (PHASHTABLEENTRYID)arg2;
697
698 PPROFILEENTRY p1 = (PPROFILEENTRY)pHTE1->pObject;
699 PPROFILEENTRY p2 = (PPROFILEENTRY)pHTE2->pObject;
700
701 return p1->EIP - p2->EIP;
702}
703
704
705// dump the collected profile to the specified file
706void _Optlink Profiler_DumpProfile(FILE *file)
707{
708 fprintf(file,
709 "ODIN Performance Analysis\n");
710
711 BOOL flagLock = ProfilerIsEnabled(); // lock recording
712 ProfilerEnable(FALSE);
713
714 int iEntries = pProfileMap->getNumberOfElements();
715
716 // get a list of all entries of the hashtable
717 PHASHTABLEENTRYID arrEntries = (PHASHTABLEENTRYID)malloc( iEntries * sizeof(HASHTABLEENTRYID) );
718 iEntries = pProfileMap->getElementMap(arrEntries);
719
720 fprintf(file,
721 "%d entries available.\n\n",
722 iEntries);
723
724 // sort the list by function name
725 qsort(arrEntries,
726 iEntries,
727 sizeof( HASHTABLEENTRYID ),
728 sortHashtableEntries0);
729
730 // write to file
731 fprintf(file,
732 "Sorted by function name\n"
733 "Ticks ---- Called --- Average -- Minimum -- Maximum -- Function -----------\n");
734 for(int i = 0;
735 i < iEntries;
736 i++)
737 {
738 PPROFILEENTRY p = (PPROFILEENTRY)arrEntries[i].pObject;
739 fprintf(file,
740 "%10d %10d %10d %10d %10d %s(%s)\n",
741 p->ulTimeTotal,
742 p->ulCalls,
743 p->ulTimeTotal / p->ulCalls,
744 p->ulTimeMinimum,
745 p->ulTimeMaximum,
746 p->pszFunction,
747 p->pszModule);
748 }
749
750
751 // sort the list by ulTimeTotal
752 qsort(arrEntries,
753 iEntries,
754 sizeof( HASHTABLEENTRYID ),
755 sortHashtableEntries1);
756
757 // write to file
758 fprintf(file,
759 "\nSorted by total call time\n"
760 "Ticks ---- Called --- Average -- Function ---------------------------------\n");
761 for(i = 0;
762 i < iEntries;
763 i++)
764 {
765 PPROFILEENTRY p = (PPROFILEENTRY)arrEntries[i].pObject;
766 fprintf(file,
767 "%10d %10d %10d %s(%s)\n",
768 p->ulTimeTotal,
769 p->ulCalls,
770 p->ulTimeTotal / p->ulCalls,
771 p->pszFunction,
772 p->pszModule);
773 }
774
775
776 // sort the list by ulCalls
777 qsort(arrEntries,
778 iEntries,
779 sizeof( HASHTABLEENTRYID ),
780 sortHashtableEntries2);
781
782 // write to file
783 fprintf(file,
784 "\nSorted by total calls\n"
785 "Called --- Ticks ---- Average -- Function ---------------------------------\n");
786 for(i = 0;
787 i < iEntries;
788 i++)
789 {
790 PPROFILEENTRY p = (PPROFILEENTRY)arrEntries[i].pObject;
791 fprintf(file,
792 "%10d %10d %10d %s(%s)\n",
793 p->ulCalls,
794 p->ulTimeTotal,
795 p->ulTimeTotal / p->ulCalls,
796 p->pszFunction,
797 p->pszModule);
798 }
799
800
801 // sort the list by average call time
802 qsort(arrEntries,
803 iEntries,
804 sizeof( HASHTABLEENTRYID ),
805 sortHashtableEntries3);
806
807 // write to file
808 fprintf(file,
809 "\nSorted by average call time\n"
810 "Average -- Calls ---- Ticks ---- Function ---------------------------------\n");
811 for(i = 0;
812 i < iEntries;
813 i++)
814 {
815 PPROFILEENTRY p = (PPROFILEENTRY)arrEntries[i].pObject;
816 fprintf(file,
817 "%10d %10d %10d %s(%s)\n",
818 p->ulTimeTotal / p->ulCalls,
819 p->ulCalls,
820 p->ulTimeTotal,
821 p->pszFunction,
822 p->pszModule);
823 }
824
825
826 // sort the list by address
827 qsort(arrEntries,
828 iEntries,
829 sizeof( HASHTABLEENTRYID ),
830 sortHashtableEntries4);
831
832
833 // write to file
834 fprintf(file,
835 "\nFunctions / symbols sorted by address\n"
836 "Address ----- Function / Symbol -------------------------------------------\n");
837 for(i = 0;
838 i < iEntries;
839 i++)
840 {
841 PPROFILEENTRY p = (PPROFILEENTRY)arrEntries[i].pObject;
842 fprintf(file,
843 "#%08xh %-9s %s\n",
844 p->EIP,
845 p->pszModule,
846 p->pszFunction);
847 }
848
849 // at last print the sym map
850 pSymPool->printSYMs(file);
851
852 ProfilerEnable(flagLock);
853}
854
855
856void _System ProfilerWrite()
857{
858 FILE* _privateLogFile;
859 char szFilename[260];
860 USHORT sel = RestoreOS2FS();
861 int pid = _getpid();
862
863 sprintf(szFilename, "%s\\%d.prof", szWorkingDirectory, pid);
864 _privateLogFile = fopen(szFilename, "w");
865
866 if(_privateLogFile == NULL)
867 {
868 DosBeep(500,500);
869 }
870 else
871 {
872 // dump the gathered data
873 Profiler_DumpProfile(_privateLogFile);
874
875 if(_privateLogFile)
876 {
877 fclose(_privateLogFile);
878 _privateLogFile = NULL;
879 }
880 }
881
882 SetFS(sel);
883}
884
Note: See TracBrowser for help on using the repository browser.