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

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

Added ODIN profiler employing the VAC308 generated profiler hooks

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