VirtualBox

source: kStuff/trunk/kProfiler2/prfreader.cpp.h@ 77

Last change on this file since 77 was 77, checked in by bird, 8 years ago

prof/dbg: uninitialized variable, missing printf args, pedantic formatting types.

  • Property svn:keywords set to Id Revision
File size: 57.0 KB
Line 
1/* $Id: prfreader.cpp.h 77 2016-06-22 17:03:55Z bird $ */
2/** @file
3 * kProfiler Mark 2 - Reader Code Template.
4 */
5
6/*
7 * Copyright (c) 2006-2007 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
8 *
9 * Permission is hereby granted, free of charge, to any person
10 * obtaining a copy of this software and associated documentation
11 * files (the "Software"), to deal in the Software without
12 * restriction, including without limitation the rights to use,
13 * copy, modify, merge, publish, distribute, sublicense, and/or sell
14 * copies of the Software, and to permit persons to whom the
15 * Software is furnished to do so, subject to the following
16 * conditions:
17 *
18 * The above copyright notice and this permission notice shall be
19 * included in all copies or substantial portions of the Software.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
23 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
25 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
26 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
28 * OTHER DEALINGS IN THE SOFTWARE.
29 */
30
31
32/**
33 * Validates the non-header parts of a data-set.
34 *
35 * @returns true if valid.
36 * @returns false if invalid. (written description to pOut)
37 *
38 * @param pHdr Pointer to the data set.
39 * @param cb The size of the data set.
40 * @param pOut Where to write error messages.
41 */
42static bool KPRF_NAME(IsValid)(KPRF_TYPE(PC,HDR) pHdr, KU32 cb, FILE *pOut)
43{
44 KPRF_TYPE(,UPTR) uMaxPtr = ~(KPRF_TYPE(,UPTR))0 - pHdr->uBasePtr;
45
46 /*
47 * Iterate the module segments.
48 */
49 KU32 off = pHdr->offModSegs;
50 while (off < pHdr->offModSegs + pHdr->cbModSegs)
51 {
52 KPRF_TYPE(PC,MODSEG) pCur = KPRF_OFF2PTR(PC,MODSEG, off, pHdr);
53 KU32 cbCur = KPRF_OFFSETOF(MODSEG, szPath[pCur->cchPath + 1]);
54 cbCur = KPRF_ALIGN(cbCur, KPRF_SIZEOF(UPTR));
55 if (cbCur + off > pHdr->offModSegs + pHdr->cbModSegs)
56 {
57 fprintf(pOut, "The module segment record at 0x%x is too long!\n", off);
58 return false;
59 }
60 if (pCur->uBasePtr > uMaxPtr)
61 fprintf(pOut, "warning: The module segment record at 0x%x has a too high base address.\n", off);
62
63 if (strlen(pCur->szPath) != pCur->cchPath)
64 {
65 fprintf(pOut, "The module segment record at 0x%x has an invalid path length 0x%x it the actual length is 0x%x\n",
66 off, pCur->cchPath, strlen(pCur->szPath));
67 return false;
68 }
69
70 /* next */
71 off += cbCur;
72 }
73
74
75 /*
76 * Iterate the functions.
77 */
78 KPRF_TYPE(PC,FUNC) paFuncs = KPRF_OFF2PTR(PC,FUNC, pHdr->offFunctions, pHdr);
79 for (KU32 i = 0; i < pHdr->cFunctions; i++)
80 {
81 KPRF_TYPE(PC,FUNC) pCur = &paFuncs[i];
82 if (pCur->uEntryPtr > uMaxPtr)
83 fprintf(pOut, "warning: Function 0x%x has a too high base address.\n", i);
84 if (pCur->offModSeg)
85 {
86 if ( pCur->offModSeg < pHdr->offModSegs
87 || pCur->offModSeg >= pHdr->offModSegs + pHdr->cbModSegs
88 || pCur->offModSeg != KPRF_ALIGN(pCur->offModSeg, sizeof(pCur->uEntryPtr))
89 )
90 {
91 fprintf(pOut, "Function 0x%x has an invalid offModSeg value (0x%x).\n", i, pCur->offModSeg);
92 return false;
93 }
94 /** @todo more validation here.. */
95 }
96 }
97
98
99 /*
100 * Validate the threads.
101 */
102 KPRF_TYPE(PC,THREAD) paThreads = KPRF_OFF2PTR(PC,THREAD, pHdr->offThreads, pHdr);
103 for (KU32 i = 0; i < pHdr->cThreads; i++)
104 {
105 KPRF_TYPE(PC,THREAD) pCur = &paThreads[i];
106 if (pCur->uStackBasePtr > uMaxPtr)
107 fprintf(pOut, "warning: Thread 0x%x has a too high base address.\n", i);
108 switch (pCur->enmState)
109 {
110 case KPRF_TYPE(,THREADSTATE_ACTIVE):
111 case KPRF_TYPE(,THREADSTATE_SUSPENDED):
112 case KPRF_TYPE(,THREADSTATE_OVERFLOWED):
113 case KPRF_TYPE(,THREADSTATE_TERMINATED):
114 break;
115 default:
116 fprintf(pOut, "Thread 0x%x has an invalid state value (0x%x).\n", i, pCur->enmState);
117 return false;
118 }
119 }
120
121
122 return true;
123}
124
125
126/**
127 * Dumps a file of a particular format.
128 *
129 * @returns 0 on success. (you might want to check the pOut state)
130 * @returns -1 on failure.
131 *
132 * @param pHdr Pointer to the data set.
133 * @param pOut The output file. This is opened for text writing.
134 * @param pReader The reader object.
135 */
136static int KPRF_NAME(Dump)(KPRF_TYPE(PC,HDR) pHdr, FILE *pOut)
137{
138 /*
139 * Any commandline?
140 */
141 if (pHdr->offCommandLine)
142 fprintf(pOut,
143 "Commandline: %s (%d bytes)\n",
144 (char *)KPRF_OFF2PTR(PC,MODSEG, pHdr->offCommandLine, pHdr), /* stupid, stupid, type hacking. */
145 pHdr->cchCommandLine);
146
147 /*
148 * Dump the module segments.
149 */
150 fprintf(pOut,
151 "Module Segments: off=0x%x 0x%x/0x%x (bytes)\n"
152 "----------------\n",
153 pHdr->offModSegs, pHdr->cbModSegs, pHdr->cbMaxModSegs);
154 KU32 off = pHdr->offModSegs;
155 while (off < pHdr->offModSegs + pHdr->cbModSegs)
156 {
157 KPRF_TYPE(PC,MODSEG) pCur = KPRF_OFF2PTR(PC,MODSEG, off, pHdr);
158 KU32 cbCur = KPRF_OFFSETOF(MODSEG, szPath[pCur->cchPath + 1]);
159 cbCur = KPRF_ALIGN(cbCur, KPRF_SIZEOF(UPTR));
160
161 fprintf(pOut,
162 "0x%04x: iSegment=0x%08x uBasePtr=%" KPRF_FMT_UPTR " szPath='%s' (%d bytes)\n",
163 off, pCur->iSegment, pCur->uBasePtr, pCur->szPath, pCur->cchPath);
164
165 /* next */
166 off += cbCur;
167 }
168 fprintf(pOut, "\n");
169
170 /*
171 * Dump the functions.
172 */
173 fprintf(pOut,
174 "Functions: off=0x%x 0x%x/0x%x\n"
175 "----------\n",
176 pHdr->offFunctions, pHdr->cFunctions, pHdr->cMaxFunctions);
177 KPRF_TYPE(PC,FUNC) paFuncs = KPRF_OFF2PTR(PC,FUNC, pHdr->offFunctions, pHdr);
178 for (KU32 i = 0; i < pHdr->cFunctions; i++)
179 {
180 KPRF_TYPE(PC,FUNC) pCur = &paFuncs[i];
181 fprintf(pOut, "0x%04x: uEntryPtr=%" KPRF_FMT_UPTR " cOnStack=0x%" KPRF_FMT_X64 " cCalls=0x%" KPRF_FMT_X64 "\n"
182 " OnStack={0x%" KPRF_FMT_X64 ", 0x%" KPRF_FMT_X64 ", 0x%" KPRF_FMT_X64 "}\n"
183 " OnTopOfStack={0x%" KPRF_FMT_X64 ", 0x%" KPRF_FMT_X64 ", 0x%" KPRF_FMT_X64 "}\n",
184 i, pCur->uEntryPtr, pCur->cOnStack, pCur->cCalls,
185 pCur->OnStack.MinTicks, pCur->OnStack.MaxTicks, pCur->OnStack.SumTicks,
186 pCur->OnTopOfStack.MinTicks, pCur->OnTopOfStack.MaxTicks, pCur->OnTopOfStack.SumTicks);
187 if (pCur->offModSeg)
188 {
189 KPRF_TYPE(PC,MODSEG) pModSeg = KPRF_OFF2PTR(PC,MODSEG, pCur->offModSeg, pHdr);
190 fprintf(pOut, " offModSeg=0x%08x iSegment=0x%02x uBasePtr=%" KPRF_FMT_UPTR " szPath='%s' (%d bytes)\n",
191 pCur->offModSeg, pModSeg->iSegment, pModSeg->uBasePtr, pModSeg->szPath, pModSeg->cchPath);
192
193#if 1
194 PKDBGMOD pMod;
195 int rc = kDbgModuleOpen(&pMod, pModSeg->szPath, NULL /* pLdrMod */);
196 if (!rc)
197 {
198 KDBGSYMBOL Sym;
199 rc = kDbgModuleQuerySymbol(pMod, pModSeg->iSegment, pCur->uEntryPtr - pModSeg->uBasePtr, &Sym);
200 if (!rc)
201 {
202 fprintf(pOut, " %s\n", Sym.szName);
203 }
204 kDbgModuleClose(pMod);
205 }
206#endif
207
208 }
209 }
210 fprintf(pOut, "\n");
211
212 /*
213 * Dump the threads.
214 */
215 fprintf(pOut,
216 "Threads: off=0x%x 0x%x/0x%x (Stacks=0x%x/0x%x cMaxStackFrames=0x%x)\n"
217 "--------\n",
218 pHdr->offThreads, pHdr->cThreads, pHdr->cMaxThreads, pHdr->cStacks, pHdr->cMaxStacks, pHdr->cMaxStackFrames);
219 KPRF_TYPE(PC,THREAD) paThreads = KPRF_OFF2PTR(PC,THREAD, pHdr->offThreads, pHdr);
220 for (KU32 i = 0; i < pHdr->cThreads; i++)
221 {
222 KPRF_TYPE(PC,THREAD) pCur = &paThreads[i];
223 fprintf(pOut,
224 "0x%02x: ThreadId=0x%08" KPRF_FMT_X64 " enmState=%d szName='%s'\n"
225 " uStackBasePtr=%" KPRF_FMT_UPTR " cbMaxStack=%" KPRF_FMT_UPTR "\n"
226 " cCalls=0x%" KPRF_FMT_X64 " cOverflows=0x%" KPRF_FMT_X64 " cStackSwitchRejects=0x%" KPRF_FMT_X64 "\n"
227 " cUnwinds=0x%" KPRF_FMT_X64 " ProfiledTicks=0x%" KPRF_FMT_X64 " OverheadTicks=0x%" KPRF_FMT_X64 "\n",
228 i, pCur->ThreadId, pCur->enmState, pCur->szName,
229 pCur->uStackBasePtr, pCur->cbMaxStack,
230 pCur->cCalls, pCur->cOverflows, pCur->cStackSwitchRejects,
231 pCur->cUnwinds, pCur->ProfiledTicks, pCur->OverheadTicks);
232 }
233
234 return 0;
235}
236
237
238/** Pointer to a report module.
239 * @internal */
240typedef struct KPRF_TYPE(,REPORTMOD) *KPRF_TYPE(P,REPORTMOD);
241/** Pointer to a report module segment.
242 * @internal */
243typedef struct KPRF_TYPE(,REPORTMODSEG) *KPRF_TYPE(P,REPORTMODSEG);
244
245
246/**
247 * A report module segment.
248 *
249 * @internal
250 */
251typedef struct KPRF_TYPE(,REPORTMODSEG)
252{
253 /** AVL node core. The key is the data set offset of the module segment record. */
254 KDBGADDR offSegment;
255 struct KPRF_TYPE(,REPORTMODSEG) *mpLeft; /**< AVL left branch. */
256 struct KPRF_TYPE(,REPORTMODSEG) *mpRight; /**< AVL rigth branch. */
257 /** Pointer to the next segment for the module. */
258 KPRF_TYPE(P,REPORTMODSEG) pNext;
259 /** Pointer to the module segment data in the data set. */
260 KPRF_TYPE(PC,MODSEG) pModSeg;
261 /** Pointer to the module this segment belongs to. */
262 KPRF_TYPE(P,REPORTMOD) pMod;
263 /** The time this segment has spent on the stack.. */
264 KU64 OnStackTicks;
265 /** The time this segment has spent on the top of the stack.. */
266 KU64 OnTopOfStackTicks;
267 /** The number of profiled functions from this segment. */
268 KU32 cFunctions;
269 KU8 mHeight; /**< AVL Subtree height. */
270} KPRF_TYPE(,REPORTMODSEG), *KPRF_TYPE(P,REPORTMODSEG);
271
272
273/**
274 * A report module segment.
275 *
276 * @internal
277 */
278typedef struct KPRF_TYPE(,REPORTMOD)
279{
280 /** The module number. */
281 KU32 iMod;
282 /** Pointer to the next module in the list. */
283 KPRF_TYPE(P,REPORTMOD) pNext;
284 /** Pointer to the list of segments belonging to this module. */
285 KPRF_TYPE(P,REPORTMODSEG) pFirstSeg;
286 /** The debug module handle. */
287 PKDBGMOD pDbgMod;
288 /** The time this segment has spent on the stack.. */
289 KU64 OnStackTicks;
290 /** The time this segment has spent on the top of the stack.. */
291 KU64 OnTopOfStackTicks;
292 /** The number of profiled functions from this segment. */
293 KU32 cFunctions;
294} KPRF_TYPE(,REPORTMOD), *KPRF_TYPE(P,REPORTMOD);
295
296
297/**
298 * A report function.
299 *
300 * @internal
301 */
302typedef struct KPRF_TYPE(,REPORTFUNC)
303{
304 /** Pointer to the function data in the data set. */
305 KPRF_TYPE(PC,FUNC) pFunc;
306 /** Pointer to the module segment this function belongs to. (can be NULL) */
307 KPRF_TYPE(P,REPORTMODSEG) pModSeg;
308 /** Pointer to the function symbol. */
309 PKDBGSYMBOL pSym;
310 /** Pointer to the function line number. */
311 PKDBGLINE pLine;
312} KPRF_TYPE(,REPORTFUNC), *KPRF_TYPE(P,REPORTFUNC);
313
314
315/**
316 * Compares two REPROTFUNC records to determin which has the higher on-stack time.
317 */
318static int KPRF_NAME(FuncCompareOnStack)(const void *pv1, const void *pv2)
319{
320 KPRF_TYPE(PC,FUNC) p1 = (*(KPRF_TYPE(P,REPORTFUNC) *)pv1)->pFunc;
321 KPRF_TYPE(PC,FUNC) p2 = (*(KPRF_TYPE(P,REPORTFUNC) *)pv2)->pFunc;
322 if (p1->OnStack.SumTicks > p2->OnStack.SumTicks)
323 return -1;
324 if (p1->OnStack.SumTicks < p2->OnStack.SumTicks)
325 return 1;
326 if (p1->OnStack.MaxTicks > p2->OnStack.MaxTicks)
327 return -1;
328 if (p1->OnStack.MaxTicks < p2->OnStack.MaxTicks)
329 return 1;
330 if (p1->OnStack.MinTicks > p2->OnStack.MinTicks)
331 return -1;
332 if (p1->OnStack.MinTicks < p2->OnStack.MinTicks)
333 return 1;
334 if (p1 < p2)
335 return -1;
336 return 1;
337}
338
339
340/**
341 * Compares two REPROTFUNC records to determin which has the higher on-stack average time.
342 */
343static int KPRF_NAME(FuncCompareOnStackAvg)(const void *pv1, const void *pv2)
344{
345 KPRF_TYPE(PC,FUNC) p1 = (*(KPRF_TYPE(P,REPORTFUNC) *)pv1)->pFunc;
346 KPRF_TYPE(PC,FUNC) p2 = (*(KPRF_TYPE(P,REPORTFUNC) *)pv2)->pFunc;
347 if (p1->OnStack.SumTicks / p1->cOnStack > p2->OnStack.SumTicks / p2->cOnStack)
348 return -1;
349 if (p1->OnStack.SumTicks / p1->cOnStack < p2->OnStack.SumTicks / p2->cOnStack)
350 return 1;
351 return KPRF_NAME(FuncCompareOnStack)(pv1, pv2);
352}
353
354
355/**
356 * Compares two REPROTFUNC records to determin which has the higher on-stack min time.
357 */
358static int KPRF_NAME(FuncCompareOnStackMin)(const void *pv1, const void *pv2)
359{
360 KPRF_TYPE(PC,FUNC) p1 = (*(KPRF_TYPE(P,REPORTFUNC) *)pv1)->pFunc;
361 KPRF_TYPE(PC,FUNC) p2 = (*(KPRF_TYPE(P,REPORTFUNC) *)pv2)->pFunc;
362 if (p1->OnStack.MinTicks > p2->OnStack.MinTicks)
363 return -1;
364 if (p1->OnStack.MinTicks < p2->OnStack.MinTicks)
365 return 1;
366 return KPRF_NAME(FuncCompareOnStack)(pv1, pv2);
367}
368
369
370/**
371 * Compares two REPROTFUNC records to determin which has the higher on-stack max time.
372 */
373static int KPRF_NAME(FuncCompareOnStackMax)(const void *pv1, const void *pv2)
374{
375 KPRF_TYPE(PC,FUNC) p1 = (*(KPRF_TYPE(P,REPORTFUNC) *)pv1)->pFunc;
376 KPRF_TYPE(PC,FUNC) p2 = (*(KPRF_TYPE(P,REPORTFUNC) *)pv2)->pFunc;
377 if (p1->OnStack.MaxTicks > p2->OnStack.MaxTicks)
378 return -1;
379 if (p1->OnStack.MaxTicks < p2->OnStack.MaxTicks)
380 return 1;
381 return KPRF_NAME(FuncCompareOnStack)(pv1, pv2);
382}
383
384
385/**
386 * Compares two REPROTFUNC records to determin which has the higher on-stack time.
387 */
388static int KPRF_NAME(FuncCompareOnTopOfStack)(const void *pv1, const void *pv2)
389{
390 KPRF_TYPE(PC,FUNC) p1 = (*(KPRF_TYPE(P,REPORTFUNC) *)pv1)->pFunc;
391 KPRF_TYPE(PC,FUNC) p2 = (*(KPRF_TYPE(P,REPORTFUNC) *)pv2)->pFunc;
392 if (p1->OnTopOfStack.SumTicks > p2->OnTopOfStack.SumTicks)
393 return -1;
394 if (p1->OnTopOfStack.SumTicks < p2->OnTopOfStack.SumTicks)
395 return 1;
396 if (p1->OnTopOfStack.MaxTicks > p2->OnTopOfStack.MaxTicks)
397 return -1;
398 if (p1->OnTopOfStack.MaxTicks < p2->OnTopOfStack.MaxTicks)
399 return 1;
400 if (p1->OnTopOfStack.MinTicks > p2->OnTopOfStack.MinTicks)
401 return -1;
402 if (p1->OnTopOfStack.MinTicks < p2->OnTopOfStack.MinTicks)
403 return 1;
404 if (p1 < p2)
405 return -1;
406 return 1;
407}
408
409
410/**
411 * Compares two REPROTFUNC records to determin which has the higher on-stack average time.
412 */
413static int KPRF_NAME(FuncCompareOnTopOfStackAvg)(const void *pv1, const void *pv2)
414{
415 KPRF_TYPE(PC,FUNC) p1 = (*(KPRF_TYPE(P,REPORTFUNC) *)pv1)->pFunc;
416 KPRF_TYPE(PC,FUNC) p2 = (*(KPRF_TYPE(P,REPORTFUNC) *)pv2)->pFunc;
417 if (p1->OnTopOfStack.SumTicks / p1->cOnStack > p2->OnTopOfStack.SumTicks / p2->cOnStack)
418 return -1;
419 if (p1->OnTopOfStack.SumTicks / p1->cOnStack < p2->OnTopOfStack.SumTicks / p2->cOnStack)
420 return 1;
421 return KPRF_NAME(FuncCompareOnTopOfStack)(pv1, pv2);
422}
423
424
425/**
426 * Compares two REPROTFUNC records to determin which has the higher on-stack min time.
427 */
428static int KPRF_NAME(FuncCompareOnTopOfStackMin)(const void *pv1, const void *pv2)
429{
430 KPRF_TYPE(PC,FUNC) p1 = (*(KPRF_TYPE(P,REPORTFUNC) *)pv1)->pFunc;
431 KPRF_TYPE(PC,FUNC) p2 = (*(KPRF_TYPE(P,REPORTFUNC) *)pv2)->pFunc;
432 if (p1->OnTopOfStack.MinTicks > p2->OnTopOfStack.MinTicks)
433 return -1;
434 if (p1->OnTopOfStack.MinTicks < p2->OnTopOfStack.MinTicks)
435 return 1;
436 return KPRF_NAME(FuncCompareOnTopOfStack)(pv1, pv2);
437}
438
439
440/**
441 * Compares two REPROTFUNC records to determin which has the higher on-stack min time.
442 */
443static int KPRF_NAME(FuncCompareOnTopOfStackMax)(const void *pv1, const void *pv2)
444{
445 KPRF_TYPE(PC,FUNC) p1 = (*(KPRF_TYPE(P,REPORTFUNC) *)pv1)->pFunc;
446 KPRF_TYPE(PC,FUNC) p2 = (*(KPRF_TYPE(P,REPORTFUNC) *)pv2)->pFunc;
447 if (p1->OnTopOfStack.MaxTicks > p2->OnTopOfStack.MaxTicks)
448 return -1;
449 if (p1->OnTopOfStack.MaxTicks < p2->OnTopOfStack.MaxTicks)
450 return 1;
451 return KPRF_NAME(FuncCompareOnTopOfStack)(pv1, pv2);
452}
453
454
455/**
456 * Compares two REPROTFUNC records to determin which has the higher call to count.
457 */
458static int KPRF_NAME(FuncCompareCallsTo)(const void *pv1, const void *pv2)
459{
460 KPRF_TYPE(PC,FUNC) p1 = (*(KPRF_TYPE(P,REPORTFUNC) *)pv1)->pFunc;
461 KPRF_TYPE(PC,FUNC) p2 = (*(KPRF_TYPE(P,REPORTFUNC) *)pv2)->pFunc;
462 if (p1->cOnStack > p2->cOnStack)
463 return -1;
464 if (p1->cOnStack < p2->cOnStack)
465 return 1;
466 return KPRF_NAME(FuncCompareOnStack)(pv1, pv2);
467}
468
469
470/**
471 * Compares two REPROTFUNC records to determin which has the higher call from count.
472 */
473static int KPRF_NAME(FuncCompareCallsFrom)(const void *pv1, const void *pv2)
474{
475 KPRF_TYPE(PC,FUNC) p1 = (*(KPRF_TYPE(P,REPORTFUNC) *)pv1)->pFunc;
476 KPRF_TYPE(PC,FUNC) p2 = (*(KPRF_TYPE(P,REPORTFUNC) *)pv2)->pFunc;
477 if (p1->cCalls > p2->cCalls)
478 return -1;
479 if (p1->cCalls < p2->cCalls)
480 return 1;
481 return KPRF_NAME(FuncCompareOnTopOfStack)(pv1, pv2);
482}
483
484
485/**
486 * A report thread.
487 *
488 * @internal
489 */
490typedef struct KPRF_TYPE(,REPORTTHREAD)
491{
492 /** Pointer to the thread data in the data set. */
493 KPRF_TYPE(PC,THREAD) pThread;
494} KPRF_TYPE(,REPORTTHREAD), *KPRF_TYPE(P,REPORTTHREAD);
495
496
497/**
498 * Data-set analysis report.
499 *
500 * This is an internal structure to store temporary data between the
501 * analysis stage and the priting state.
502 *
503 * @internal
504 */
505typedef struct KPRF_TYPE(,REPORT)
506{
507 /** Pointer to the data set. */
508 KPRF_TYPE(PC,HDR) pHdr;
509
510 /** @name Data-set item wrappers.
511 * @{ */
512 /** Pointer to the array of threads. */
513 KPRF_TYPE(P,REPORTTHREAD) paThreads;
514 /** Pointer to the array of functions. */
515 KPRF_TYPE(P,REPORTFUNC) paFunctions;
516 /** Pointer to the head of the module list. */
517 KPRF_TYPE(P,REPORTMOD) pFirstMod;
518 /** The number of modules in the list. */
519 KU32 cMods;
520 /** The module segment tree. (Only kAvl cares about this.) */
521 KPRF_TYPE(P,REPORTMODSEG) pModSegTree;
522 /** The number of module segments in the tree. */
523 KU32 cModSegs;
524 /** @} */
525
526 /** @name Sorting.
527 * @{ */
528 /** Pointer to the array of threads. */
529 KPRF_TYPE(P,REPORTTHREAD) *papSortedThreads;
530 /** Pointer to the array of functions. */
531 KPRF_TYPE(P,REPORTFUNC) *papSortedFunctions;
532 /** @} */
533
534 /** @name Accumulated Thread Data.
535 * @{ */
536 /** Sum of the profiled ticks. */
537 KU64 ProfiledTicks;
538 /** Sum of the overhead ticks. */
539 KU64 OverheadTicks;
540 /** Sum of the sleep ticks. */
541 KU64 SleepTicks;
542 /** Sum of calls performed. */
543 KU64 cCalls;
544 /** @} */
545
546} KPRF_TYPE(,REPORT), *KPRF_TYPE(P,REPORT), **KPRF_TYPE(PP,REPORT);
547
548
549/* Instantiate the AVL tree code. */
550#define KAVL_CHECK_FOR_EQUAL_INSERT
551#define KAVL_MAX_STACK 32
552#define KAVL_STD_KEY_COMP
553#define mKey offSegment
554#define KAVLKEY KDBGADDR
555#define KAVLNODE KPRF_TYPE(,REPORTMODSEG)
556#define mpRoot pModSegTree
557#define KAVLROOT KPRF_TYPE(,REPORT)
558#define KAVL_FN(name) KPRF_NAME(ReportTree ## name)
559#define KAVL_TYPE(prefix,name) KPRF_TYPE(prefix, REPORTMODESEG ## name)
560#define KAVL_INT(name) KPRF_NAME(REPORTMODESEGINT ## name)
561#define KAVL_DECL(type) K_DECL_INLINE(type)
562#include <k/kAvlTmpl/kAvlBase.h>
563#include <k/kAvlTmpl/kAvlDestroy.h>
564#include <k/kAvlTmpl/kAvlGet.h>
565#include <k/kAvlTmpl/kAvlUndef.h>
566
567
568/**
569 * Allocates and initializes a report.
570 *
571 * @returns Pointer to the report on success.
572 * @returns NULL on failure.
573 */
574static KPRF_TYPE(P,REPORT) KPRF_NAME(NewReport)(KPRF_TYPE(PC,HDR) pHdr)
575{
576 /*
577 * Allocate memory for the report.
578 * Everything but the mods and modsegs is allocated in the same block as the report.
579 */
580 KSIZE cb = KPRF_ALIGN(KPRF_SIZEOF(REPORT), 32);
581 KUPTR offThreads = cb;
582 cb += KPRF_ALIGN(KPRF_SIZEOF(REPORTTHREAD) * pHdr->cThreads, 32);
583 KUPTR offFunctions = cb;
584 cb += KPRF_ALIGN(KPRF_SIZEOF(REPORTFUNC) * pHdr->cFunctions, 32);
585 KUPTR offSortedThreads = cb;
586 cb += KPRF_ALIGN(sizeof(KPRF_TYPE(P,REPORTTHREAD)) * pHdr->cThreads, 32);
587 KUPTR offSortedFunctions = cb;
588 cb += KPRF_ALIGN(sizeof(KPRF_TYPE(P,REPORTFUNC)) * pHdr->cFunctions, 32);
589 KPRF_TYPE(P,REPORT) pReport = (KPRF_TYPE(P,REPORT))malloc(cb);
590 if (!pReport)
591 return NULL;
592
593 /*
594 * Initialize it.
595 */
596 pReport->pHdr = pHdr;
597 pReport->paThreads = (KPRF_TYPE(P,REPORTTHREAD))((KU8 *)pReport + offThreads);
598 pReport->paFunctions = (KPRF_TYPE(P,REPORTFUNC))((KU8 *)pReport + offFunctions);
599 pReport->pFirstMod = NULL;
600 pReport->cMods = 0;
601 KPRF_NAME(ReportTreeInit)(pReport);
602 pReport->cModSegs = 0;
603 pReport->papSortedThreads = (KPRF_TYPE(P,REPORTTHREAD) *)((KU8 *)pReport + offSortedThreads);
604 pReport->papSortedFunctions = (KPRF_TYPE(P,REPORTFUNC) *)((KU8 *)pReport + offSortedFunctions);
605 pReport->ProfiledTicks = 0;
606 pReport->OverheadTicks = 0;
607 pReport->SleepTicks = 0;
608 pReport->cCalls = 0;
609
610 return pReport;
611}
612
613
614/**
615 * AVL callback for deleting a module segment node.
616 *
617 * @returns 0
618 * @param pCore The tree node to delete.
619 * @param pvParam User parameter, ignored.
620 */
621static int KPRF_NAME(DeleteModSeg)(KPRF_TYPE(P,REPORTMODSEG) pCore, void *pvParam)
622{
623 free(pCore);
624 return 0;
625}
626
627
628/**
629 * Releases all the resources held be a report.
630 *
631 * @param pReport The report to delete.
632 */
633static void KPRF_NAME(DeleteReport)(KPRF_TYPE(P,REPORT) pReport)
634{
635 /*
636 * The list and AVL.
637 */
638 while (pReport->pFirstMod)
639 {
640 KPRF_TYPE(P,REPORTMOD) pFree = pReport->pFirstMod;
641 pReport->pFirstMod = pFree->pNext;
642 kDbgModuleClose(pFree->pDbgMod);
643 free(pFree);
644 }
645
646 KPRF_NAME(ReportTreeDestroy)(pReport, KPRF_NAME(DeleteModSeg), NULL);
647
648 /*
649 * The function debug info.
650 */
651 KU32 i = pReport->pHdr->cFunctions;
652 while (i-- > 0)
653 {
654 kDbgSymbolFree(pReport->paFunctions[i].pSym);
655 kDbgLineFree(pReport->paFunctions[i].pLine);
656 }
657
658 /*
659 * The report it self.
660 */
661 pReport->pHdr = NULL;
662 free(pReport);
663}
664
665
666/**
667 * Builds the module segment tree and the list of modules.
668 *
669 * @returns 0 on success.
670 * @returns -1 on failure.
671 * @param pReport The report to work on.
672 */
673static int KPRF_NAME(AnalyzeModSegs)(KPRF_TYPE(P,REPORT) pReport)
674{
675 const KU32 offEnd = pReport->pHdr->offModSegs + pReport->pHdr->cbModSegs;
676 KU32 off = pReport->pHdr->offModSegs;
677 while (off < offEnd)
678 {
679 KPRF_TYPE(PC,MODSEG) pCur = KPRF_OFF2PTR(PC,MODSEG, off, pReport->pHdr);
680 KU32 cbCur = KPRF_OFFSETOF(MODSEG, szPath[pCur->cchPath + 1]);
681 cbCur = KPRF_ALIGN(cbCur, KPRF_SIZEOF(UPTR));
682
683 /*
684 * Create a new modseg record.
685 */
686 KPRF_TYPE(P,REPORTMODSEG) pSeg = (KPRF_TYPE(P,REPORTMODSEG))malloc(sizeof(*pSeg));
687 if (!pSeg)
688 return -1;
689
690 pSeg->offSegment = off;
691 pSeg->pModSeg = pCur;
692 pSeg->pMod = NULL; /* below */
693 pSeg->OnStackTicks = 0;
694 pSeg->OnTopOfStackTicks = 0;
695 pSeg->cFunctions = 0;
696
697 if (!KPRF_NAME(ReportTreeInsert)(pReport, pSeg))
698 {
699 free(pSeg);
700 return -1;
701 }
702 pReport->cModSegs++;
703
704 /*
705 * Search for the module record.
706 */
707 KPRF_TYPE(P,REPORTMOD) pMod = pReport->pFirstMod;
708 while ( pMod
709 && ( pMod->pFirstSeg->pModSeg->cchPath != pCur->cchPath
710 || memcmp(pMod->pFirstSeg->pModSeg->szPath, pCur->szPath, pCur->cchPath)))
711 pMod = pMod->pNext;
712 if (pMod)
713 {
714 /** @todo sort segments */
715 pSeg->pMod = pMod;
716 pSeg->pNext = pMod->pFirstSeg;
717 pMod->pFirstSeg = pSeg;
718 }
719 else
720 {
721 KPRF_TYPE(P,REPORTMOD) pMod = (KPRF_TYPE(P,REPORTMOD))malloc(sizeof(*pMod) + pCur->cchPath);
722 if (!pMod)
723 return -1;
724 pSeg->pMod = pMod;
725 pSeg->pNext = NULL;
726 pMod->iMod = pReport->cMods++;
727 pMod->pNext = pReport->pFirstMod;
728 pReport->pFirstMod = pMod;
729 pMod->pFirstSeg = pSeg;
730 pMod->pDbgMod = NULL;
731 pMod->OnStackTicks = 0;
732 pMod->OnTopOfStackTicks = 0;
733 pMod->cFunctions = 0;
734
735 int rc = kDbgModuleOpen(&pMod->pDbgMod, pSeg->pModSeg->szPath, NULL /* kLdrMod */);
736 if (rc)
737 pMod->pDbgMod = NULL;
738 }
739
740 /* next */
741 off += cbCur;
742 }
743
744 return 0;
745}
746
747
748/**
749 * Initializes the function arrays.
750 *
751 * @returns 0 on success.
752 * @returns -1 on failure.
753 * @param pReport The report to work on.
754 */
755static int KPRF_NAME(AnalyseFunctions)(KPRF_TYPE(P,REPORT) pReport)
756{
757 KU32 iFunc = pReport->pHdr->cFunctions;
758 KPRF_TYPE(PC,FUNC) pFunc = KPRF_OFF2PTR(PC,FUNC, pReport->pHdr->offFunctions + iFunc * sizeof(*pFunc), pReport->pHdr);
759 KPRF_TYPE(P,REPORTFUNC) pReportFunc = &pReport->paFunctions[iFunc];
760 while (iFunc-- > 0)
761 {
762 pFunc--;
763 pReportFunc--;
764
765 pReport->papSortedFunctions[iFunc] = pReportFunc;
766 pReportFunc->pFunc = pFunc;
767 pReportFunc->pModSeg = KPRF_NAME(ReportTreeGet)(pReport, pFunc->offModSeg);
768 pReportFunc->pSym = NULL;
769 pReportFunc->pLine = NULL;
770 if (pReportFunc->pModSeg)
771 {
772 /* Collect module segment and module statistics. */
773 KPRF_TYPE(P,REPORTMODSEG) pModSeg = pReportFunc->pModSeg;
774 pModSeg->cFunctions++;
775 pModSeg->OnStackTicks += pFunc->OnStack.SumTicks;
776 pModSeg->OnTopOfStackTicks += pFunc->OnTopOfStack.SumTicks;
777
778 KPRF_TYPE(P,REPORTMOD) pMod = pModSeg->pMod;
779 pMod->cFunctions++;
780 pMod->OnStackTicks += pFunc->OnStack.SumTicks;
781 pMod->OnTopOfStackTicks += pFunc->OnTopOfStack.SumTicks;
782
783 /* Get debug info. */
784 KDBGADDR offSegment = pFunc->uEntryPtr - pModSeg->pModSeg->uBasePtr;
785 int rc = kDbgModuleQuerySymbolA(pMod->pDbgMod, pModSeg->pModSeg->iSegment, offSegment, &pReportFunc->pSym);
786 /** @todo check displacement! */
787 if (rc)
788 pReportFunc->pSym = NULL;
789 rc = kDbgModuleQueryLineA(pMod->pDbgMod, pModSeg->pModSeg->iSegment, offSegment, &pReportFunc->pLine);
790 if (rc)
791 pReportFunc->pLine = NULL;
792 }
793 }
794 return 0;
795}
796
797
798/**
799 * Initializes the thread arrays.
800 *
801 * @returns 0 on success.
802 * @returns -1 on failure.
803 * @param pReport The report to work on.
804 */
805static int KPRF_NAME(AnalyseThreads)(KPRF_TYPE(P,REPORT) pReport)
806{
807 KU32 iThread = pReport->pHdr->cThreads;
808 KPRF_TYPE(PC,THREAD) pThread = KPRF_OFF2PTR(PC,THREAD, pReport->pHdr->offThreads + iThread * sizeof(*pThread), pReport->pHdr);
809 KPRF_TYPE(P,REPORTTHREAD) pReportThread = &pReport->paThreads[iThread];
810 while (iThread-- > 0)
811 {
812 pThread--;
813 pReportThread--;
814
815 pReport->papSortedThreads[iThread] = pReportThread;
816 pReportThread->pThread = pThread;
817
818 /* collect statistics */
819 pReport->ProfiledTicks += pThread->ProfiledTicks;
820 pReport->OverheadTicks += pThread->OverheadTicks;
821 pReport->SleepTicks += pThread->SleepTicks;
822 pReport->cCalls += pThread->cCalls;
823
824 }
825 return 0;
826}
827
828
829/**
830 * Analyses the data set, producing a report.
831 *
832 * @returns 0 on success.
833 * @returns -1 on failure.
834 *
835 * @param pHdr The data set.
836 * @param ppReport Where to store the report.
837 */
838static int KPRF_NAME(Analyse)(KPRF_TYPE(PC,HDR) pHdr, KPRF_TYPE(PP,REPORT) ppReport)
839{
840 *ppReport = NULL;
841
842 /* allocate it */
843 KPRF_TYPE(P,REPORT) pReport = KPRF_NAME(NewReport)(pHdr);
844 if (!pReport)
845 return -1;
846
847 /* read module segments */
848 int rc = KPRF_NAME(AnalyzeModSegs)(pReport);
849 if (!rc)
850 {
851 /* read functions. */
852 rc = KPRF_NAME(AnalyseFunctions)(pReport);
853 if (!rc)
854 {
855 /* read threads */
856 rc = KPRF_NAME(AnalyseThreads)(pReport);
857 if (!rc)
858 {
859 *ppReport = pReport;
860 return 0;
861 }
862 }
863 }
864
865 KPRF_NAME(DeleteReport)(pReport);
866 return rc;
867}
868
869
870/**
871 * Writes row with 32-bit value.
872 * @internal
873 */
874static void KPRF_NAME(HtmlWriteRowU32X32)(FILE *pOut, const char *pszName, KU32 u32, const char *pszUnit)
875{
876 fprintf(pOut,
877 " <tr>\n"
878 " <th>%s</th>\n"
879 " <td colspan=\"6\">%u (0x%x)%s%s</td>\n"
880 " </tr>\n",
881 pszName,
882 u32, u32, pszUnit ? " " : "", pszUnit ? pszUnit : "");
883}
884
885
886/**
887 * Writes row with 32-bit value.
888 * @internal
889 */
890static void KPRF_NAME(HtmlWriteRowU32)(FILE *pOut, const char *pszName, KU32 u32, const char *pszUnit)
891{
892 fprintf(pOut,
893 " <tr>\n"
894 " <th>%s</th>\n"
895 " <td colspan=\"6\">%u%s%s</td>\n"
896 " </tr>\n",
897 pszName,
898 u32, pszUnit ? " " : "", pszUnit ? pszUnit : "");
899}
900
901
902/**
903 * Writes row with 64-bit value.
904 * @internal
905 */
906static void KPRF_NAME(HtmlWriteRowU64)(FILE *pOut, const char *pszName, KU64 u64, const char *pszUnit)
907{
908 fprintf(pOut,
909 " <tr>\n"
910 " <th>%s</th>\n"
911 " <td colspan=\"6\">% " KPRF_FMT_U64 " (0x%" KPRF_FMT_X64 ")%s%s</td>\n"
912 " </tr>\n",
913 pszName,
914 u64, u64, pszUnit ? " " : "", pszUnit ? pszUnit : "");
915}
916
917
918/**
919 * Writes row with 64-bit hex value.
920 * @internal
921 */
922static void KPRF_NAME(HtmlWriteRowX64)(FILE *pOut, const char *pszName, KU64 u64, const char *pszUnit)
923{
924 fprintf(pOut,
925 " <tr>\n"
926 " <th>%s</th>\n"
927 " <td colspan=\"6\">0x%" KPRF_FMT_X64 "%s%s</td>\n"
928 " </tr>\n",
929 pszName,
930 u64, pszUnit ? " " : "", pszUnit ? pszUnit : "");
931}
932
933
934/**
935 * Writes a ticks.
936 */
937static void KPRF_NAME(HtmlWriteParts)(FILE *pOut, KU64 cTicks, KU64 cTotalTicks)
938{
939 /** U+2030 PER MILLE SIGN */
940 static const KU8 s_szPerMilleSignUtf8[4] = { 0xe2, 0x80, 0xb0, 0};
941
942 if (cTicks * 100 / cTotalTicks)
943 {
944 KU32 u = (KU32)((cTicks * 1000) / cTotalTicks);
945 fprintf(pOut, "%u.%01u%%", u / 10, u %10);
946 }
947 else //if (cTicks * 100000 / cTotalTicks)
948 {
949 KU32 u = (KU32)((cTicks * 100000) / cTotalTicks);
950 fprintf(pOut, "%u.%02u%s", u / 100, u % 100, s_szPerMilleSignUtf8);
951 }
952 /*
953 else if (cTicks * 1000000 / cTotalTicks)
954 fprintf(pOut, "%u ppm", (unsigned)((cTicks * 1000000) / cTotalTicks));
955 else
956 fprintf(pOut, "%u ppb", (unsigned)((cTicks * 1000000000) / cTotalTicks));
957 */
958}
959
960
961/**
962 * Writes a ticks.
963 */
964static void KPRF_NAME(HtmlWriteTicks)(FILE *pOut, KU64 cTicks, KU64 cTotalTicks)
965{
966 fprintf(pOut, "%" KPRF_FMT_U64 "", cTicks);
967 if (cTotalTicks)
968 {
969 fprintf(pOut, "</td><td class=\"PartsRow\">");
970 KPRF_NAME(HtmlWriteParts)(pOut, cTicks, cTotalTicks);
971 }
972}
973
974
975/**
976 * Writes row with ticks value.
977 *
978 * @param pOut Where to write.
979 * @aaran pszName The row name.
980 * @param cTicks The tick count.
981 * @param cTotalTicks If non-zero, this is used for cTicks / cTotalTicks.
982 * @internal
983 */
984static void KPRF_NAME(HtmlWriteRowTicks)(FILE *pOut, const char *pszName, KU64 cTicks, KU64 cTotalTicks)
985{
986 fprintf(pOut,
987 " <tr>\n"
988 " <th class=\"TicksRow\">%s</th>\n"
989 " <td class=\"TicksRow\">",
990 pszName);
991 KPRF_NAME(HtmlWriteTicks)(pOut, cTicks, cTotalTicks);
992 fprintf(pOut,
993 "</td><td colspan=\"%d\"/>\n"
994 " </tr>\n",
995 cTotalTicks ? 4 : 5);
996}
997
998
999/**
1000 * Writes row with a time stat value.
1001 *
1002 * @param pOut Where to write.
1003 * @aaran pszName The row name.
1004 * @param cTicks The tick count.
1005 * @param cTotalTicks If non-zero, this is used for cTicks / cTotalTicks.
1006 * @internal
1007 */
1008static void KPRF_NAME(HtmlWriteRowTimeStat)(FILE *pOut, const char *pszName, KPRF_TYPE(PC,TIMESTAT) pTimeStat, KU64 cTotalTicks)
1009{
1010 fprintf(pOut,
1011 " <tr>\n"
1012 " <th class=\"TicksRow\">%s</th>\n"
1013 " <td class=\"TicksRow\">",
1014 pszName);
1015 KPRF_NAME(HtmlWriteTicks)(pOut, pTimeStat->SumTicks, cTotalTicks);
1016 fprintf(pOut, "</td>\n"
1017 " <td class=\"MinMaxTicksRow\">");
1018 KPRF_NAME(HtmlWriteTicks)(pOut, pTimeStat->MinTicks, cTotalTicks);
1019 fprintf(pOut, "</td>\n"
1020 " <td class=\"MinMaxTicksRow\">");
1021 KPRF_NAME(HtmlWriteTicks)(pOut, pTimeStat->MaxTicks, cTotalTicks);
1022 fprintf(pOut, "</td>\n"
1023 " </tr>\n");
1024}
1025
1026
1027/**
1028 * Writes row with calls value.
1029 *
1030 * @param pOut Where to write.
1031 * @aaran pszName The row name.
1032 * @param cCalls The call count.
1033 * @param cTotalCalls This is used for cCalls / cTotalCalls.
1034 * @internal
1035 */
1036static void KPRF_NAME(HtmlWriteRowCalls)(FILE *pOut, const char *pszName, KU64 cCalls, KU64 cTotalCalls)
1037{
1038 fprintf(pOut,
1039 " <tr>\n"
1040 " <th class=\"CallsRow\">%s</th>\n"
1041 " <td class=\"CallsRow\">%" KPRF_FMT_U64"</td><td class=\"PartsRow\">",
1042 pszName, cCalls);
1043 KPRF_NAME(HtmlWriteParts)(pOut, cCalls, cTotalCalls);
1044 fprintf(pOut, "</td><td colspan=4></td>"
1045 " </tr>\n");
1046}
1047
1048
1049/**
1050 * Writes row with pointer value.
1051 * @internal
1052 */
1053static void KPRF_NAME(HtmlWriteRowUPTR)(FILE *pOut, const char *pszName, KPRF_TYPE(,UPTR) uPtr, const char *pszUnit)
1054{
1055 fprintf(pOut,
1056 " <tr>\n"
1057 " <th>%s</th>\n"
1058 " <td colspan=\"6\">%" KPRF_FMT_UPTR "%s%s</td>\n"
1059 " </tr>\n",
1060 pszName,
1061 uPtr, pszUnit ? " " : "", pszUnit ? pszUnit : "");
1062}
1063
1064
1065/**
1066 * Writes row with string value.
1067 * @internal
1068 */
1069static void KPRF_NAME(HtmlWriteRowString)(FILE *pOut, const char *pszName, const char *pszClass, const char *pszFormat, ...)
1070{
1071 fprintf(pOut,
1072 " <tr>\n"
1073 " <th>%s</th>\n"
1074 " <td%s%s%s colspan=\"6\">",
1075 pszName,
1076 pszClass ? " class=\"" : "", pszClass ? pszClass : "", pszClass ? "\"" : "");
1077 va_list va;
1078 va_start(va, pszFormat);
1079 vfprintf(pOut, pszFormat, va);
1080 va_end(va);
1081 fprintf(pOut, "</td>\n"
1082 " </tr>\n");
1083}
1084
1085
1086/**
1087 * The first column
1088 */
1089typedef enum KPRF_TYPE(,FIRSTCOLUMN)
1090{
1091 KPRF_TYPE(,FIRSTCOLUMN_ON_STACK) = 0,
1092 KPRF_TYPE(,FIRSTCOLUMN_ON_TOP_OF_STACK),
1093 KPRF_TYPE(,FIRSTCOLUMN_CALLS_TO),
1094 KPRF_TYPE(,FIRSTCOLUMN_CALLS_FROM),
1095 KPRF_TYPE(,FIRSTCOLUMN_MAX)
1096} KPRF_TYPE(,FIRSTCOLUMN);
1097
1098
1099/**
1100 * Prints the table with the sorted functions.
1101 * The tricky bit is that the sorted column should be to the left of the function name.
1102 */
1103static void KPRF_NAME(HtmlWriteSortedFunctions)(KPRF_TYPE(P,REPORT) pReport, FILE *pOut, const char *pszName,
1104 const char *pszTitle, KPRF_TYPE(,FIRSTCOLUMN) enmFirst)
1105{
1106 fprintf(pOut,
1107 "<h2><a name=\"%s\">%s</a></h2>\n"
1108 "\n",
1109 pszName, pszTitle);
1110
1111 fprintf(pOut,
1112 "<table class=\"FunctionsSorted\">\n"
1113 " <tr>\n"
1114 " <th/>\n");
1115 static const char *s_pszHeaders[KPRF_TYPE(,FIRSTCOLUMN_MAX) * 2] =
1116 {
1117 " <th colspan=8><a href=\"#Functions-TimeOnStack\">Time On Stack</a> (ticks)</th>\n",
1118 " <th colspan=2><a href=\"#Functions-TimeOnStack\">Sum</a></th>\n"
1119 " <th colspan=2><a href=\"#Functions-TimeOnStack-Min\">Min</a></th>\n"
1120 " <th colspan=2><a href=\"#Functions-TimeOnStack-Avg\">Average</a></th>\n"
1121 " <th colspan=2><a href=\"#Functions-TimeOnStack-Max\">Max</a></th>\n",
1122
1123 " <th colspan=8><a href=\"#Functions-TimeOnTopOfStack\">Time On To Top</a> (ticks)</th>\n",
1124 " <th colspan=2><a href=\"#Functions-TimeOnTopOfStack\">Sum</a></th>\n"
1125 " <th colspan=2><a href=\"#Functions-TimeOnTopOfStack-Min\">Min</a></th>\n"
1126 " <th colspan=2><a href=\"#Functions-TimeOnTopOfStack-Avg\">Average</a></th>\n"
1127 " <th colspan=2><a href=\"#Functions-TimeOnTopOfStack-Max\">Max</a></th>\n",
1128
1129 " <th colspan=2><a href=\"#Functions-CallsTo\">Calls To</a></th>\n",
1130 " <th/><th/>\n",
1131
1132 " <th colspan=2><a href=\"#Functions-CallsFrom\">Calls From</a></th>\n",
1133 " <th/><th/>\n",
1134 };
1135
1136 fprintf(pOut, "%s", s_pszHeaders[enmFirst * 2]);
1137 fprintf(pOut, " <th>Function</th>\n");
1138 for (unsigned i = (enmFirst + 1) % KPRF_TYPE(,FIRSTCOLUMN_MAX); i != enmFirst; i = (i + 1) % KPRF_TYPE(,FIRSTCOLUMN_MAX))
1139 fprintf(pOut, "%s", s_pszHeaders[i * 2]);
1140 fprintf(pOut,
1141 " </tr>\n"
1142 " <tr>\n"
1143 " <th/>\n");
1144 fprintf(pOut, "%s", s_pszHeaders[enmFirst * 2 + 1]);
1145 fprintf(pOut, " <th/>\n");
1146 for (unsigned i = (enmFirst + 1) % KPRF_TYPE(,FIRSTCOLUMN_MAX); i != enmFirst; i = (i + 1) % KPRF_TYPE(,FIRSTCOLUMN_MAX))
1147 fprintf(pOut, "%s", s_pszHeaders[i * 2 + 1]);
1148 fprintf(pOut,
1149 " </tr>\n");
1150
1151 for (KU32 iFunc = 0; iFunc < pReport->pHdr->cFunctions; iFunc++)
1152 {
1153 KPRF_TYPE(P,REPORTFUNC) pReportFunc = pReport->papSortedFunctions[iFunc];
1154 KPRF_TYPE(PC,FUNC) pFunc = pReportFunc->pFunc;
1155 fprintf(pOut,
1156 " <tr>\n"
1157 " <td>%u</td>\n",
1158 iFunc);
1159
1160 unsigned i = enmFirst;
1161 do
1162 {
1163 switch (i)
1164 {
1165 case KPRF_TYPE(,FIRSTCOLUMN_ON_STACK):
1166 fprintf(pOut,
1167 " <td class=\"Ticks\">%" KPRF_FMT_U64 "</td><td class=\"Parts\">",
1168 pFunc->OnStack.SumTicks);
1169 KPRF_NAME(HtmlWriteParts)(pOut, pFunc->OnStack.SumTicks, pReport->ProfiledTicks);
1170 fprintf(pOut, "</td>\n"
1171 " <td class=\"MinMaxTicks\">%" KPRF_FMT_U64 "</td><td class=\"Parts\">",
1172 pFunc->OnStack.MinTicks);
1173 KPRF_NAME(HtmlWriteParts)(pOut, pFunc->OnStack.MinTicks, pReport->ProfiledTicks);
1174 fprintf(pOut, "</td>\n"
1175 " <td class=\"MinMaxTicks\">%" KPRF_FMT_U64 "</td><td class=\"Parts\">",
1176 pFunc->OnStack.SumTicks / pFunc->cOnStack);
1177 KPRF_NAME(HtmlWriteParts)(pOut, pFunc->OnStack.MinTicks, pReport->ProfiledTicks);
1178 fprintf(pOut, "</td>\n"
1179 " <td class=\"MinMaxTicks\">%" KPRF_FMT_U64 "</td><td class=\"Parts\">",
1180 pFunc->OnStack.MaxTicks);
1181 KPRF_NAME(HtmlWriteParts)(pOut, pFunc->OnStack.MaxTicks, pReport->ProfiledTicks);
1182 fprintf(pOut, "</td>\n");
1183 break;
1184
1185 case KPRF_TYPE(,FIRSTCOLUMN_ON_TOP_OF_STACK):
1186 fprintf(pOut,
1187 " <td class=\"Ticks\">%" KPRF_FMT_U64 "</td><td class=\"Parts\">",
1188 pFunc->OnTopOfStack.SumTicks);
1189 KPRF_NAME(HtmlWriteParts)(pOut, pFunc->OnTopOfStack.SumTicks, pReport->ProfiledTicks);
1190 fprintf(pOut, "</td>\n"
1191 " <td class=\"MinMaxTicks\">%" KPRF_FMT_U64 "</td><td class=\"Parts\">",
1192 pFunc->OnTopOfStack.MinTicks);
1193 KPRF_NAME(HtmlWriteParts)(pOut, pFunc->OnTopOfStack.MinTicks, pReport->ProfiledTicks);
1194 fprintf(pOut, "</td>\n"
1195 " <td class=\"MinMaxTicks\">%" KPRF_FMT_U64 "</td><td class=\"Parts\">",
1196 pFunc->OnTopOfStack.SumTicks / pFunc->cOnStack);
1197 KPRF_NAME(HtmlWriteParts)(pOut, pFunc->OnTopOfStack.MinTicks, pReport->ProfiledTicks);
1198 fprintf(pOut, "</td>\n"
1199 " <td class=\"MinMaxTicks\">%" KPRF_FMT_U64 "</td><td class=\"Parts\">",
1200 pFunc->OnTopOfStack.MaxTicks);
1201 KPRF_NAME(HtmlWriteParts)(pOut, pFunc->OnTopOfStack.MaxTicks, pReport->ProfiledTicks);
1202 fprintf(pOut, "</td>\n");
1203 break;
1204
1205 case KPRF_TYPE(,FIRSTCOLUMN_CALLS_TO):
1206 fprintf(pOut,
1207 " <td class=\"Calls\">%" KPRF_FMT_U64 "</td><td Class=\"Parts\">",
1208 pFunc->cOnStack);
1209 KPRF_NAME(HtmlWriteParts)(pOut, pFunc->cOnStack, pReport->cCalls);
1210 fprintf(pOut, "</td>\n");
1211 break;
1212
1213 case KPRF_TYPE(,FIRSTCOLUMN_CALLS_FROM):
1214 fprintf(pOut,
1215 " <td class=\"Calls\">%" KPRF_FMT_U64 "</td><td Class=\"Parts\">",
1216 pFunc->cCalls);
1217 KPRF_NAME(HtmlWriteParts)(pOut, pFunc->cCalls, pReport->cCalls);
1218 fprintf(pOut, "</td>\n");
1219 break;
1220
1221 default:
1222 break;
1223 }
1224
1225 /* inject the function column */
1226 if (i == enmFirst)
1227 {
1228 fprintf(pOut,
1229 " <td><a href=\"#Func-%u\">",
1230 (unsigned)(uintptr_t)(pReportFunc - pReport->paFunctions));
1231 if (pReportFunc->pSym)
1232 fprintf(pOut, "%s</a></td>\n", pReportFunc->pSym->szName);
1233 else
1234 fprintf(pOut, "%" KPRF_FMT_UPTR "</a></td>\n", pFunc->uEntryPtr);
1235 }
1236
1237 /* next */
1238 i = (i + 1) % KPRF_TYPE(,FIRSTCOLUMN_MAX);
1239 } while (i != enmFirst);
1240
1241 fprintf(pOut,
1242 " </tr>\n");
1243 }
1244 fprintf(pOut,
1245 "</table>\n"
1246 "\n");
1247
1248}
1249
1250
1251/**
1252 * Writes an HTML report.
1253 *
1254 * @returns 0 on success.
1255 * @returns -1 on failure.
1256 * @param pReport The report to put into HTML.
1257 * @param pOut The file stream to write the HTML to.
1258 */
1259static int KPRF_NAME(WriteHtmlReport)(KPRF_TYPE(P,REPORT) pReport, FILE *pOut)
1260{
1261 KPRF_TYPE(PC,HDR) pHdr = pReport->pHdr;
1262
1263 /*
1264 * Write the standard html.
1265 */
1266 fprintf(pOut,
1267 "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n"
1268 "<html>\n"
1269 "<head>\n"
1270 " <meta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\">\n"
1271 " <title>kProfiler 2 - %s</title>\n"
1272 "</head>\n"
1273 "<style>\n"
1274 "table\n"
1275 "{\n"
1276// " width: 90%%;\n"
1277 " background: #999999;\n"
1278// " margin-top: .6em;\n"
1279// " margin-bottom: .3em;\n"
1280 "}\n"
1281 "th\n"
1282 "{\n"
1283 " padding: 1px 4px;\n"
1284 " background: #cccccc;\n"
1285// " text-align: left;\n"
1286 " font-size: 90%%;\n"
1287 //" width: 30%%;\n"
1288 "}\n"
1289 "td\n"
1290 "{\n"
1291 " padding: 1px 4px;\n"
1292 " background: #ffffff;\n"
1293 " font-size: 90%%;\n"
1294 "}\n"
1295 "td.Ticks\n"
1296 "{\n"
1297 " text-align: right;\n"
1298 "}\n"
1299 "td.TicksRow\n"
1300 "{\n"
1301 " text-align: right;\n"
1302 "}\n"
1303 "td.MinMaxTicks\n"
1304 "{\n"
1305 " text-align: right;\n"
1306 "}\n"
1307 "td.MinMaxTicksRow\n"
1308 "{\n"
1309 " text-align: right;\n"
1310 "}\n"
1311 "td.Parts\n"
1312 "{\n"
1313 " text-align: right;\n"
1314 "}\n"
1315 "td.PartsRow\n"
1316 "{\n"
1317 " text-align: left;\n"
1318 "}\n"
1319 "td.Calls\n"
1320 "{\n"
1321 " text-align: right;\n"
1322 "}\n"
1323 "td.CallsRow\n"
1324 "{\n"
1325 " text-align: right;\n"
1326 "}\n"
1327 "td.BlankRow\n"
1328 "{\n"
1329 " background: #e0e0e0;\n"
1330 "}\n"
1331 "td.Name\n"
1332 "{\n"
1333 " font-weight: bold;\n"
1334 "}\n"
1335 "table.Summary th\n"
1336 "{\n"
1337 " width:200px;\n"
1338 "}\n"
1339 "table.Thread\n"
1340 "{\n"
1341 " min-width:60%%\n"
1342 "}\n"
1343 "table.Thread th\n"
1344 "{\n"
1345 " width:200px;\n"
1346 "}\n"
1347 "table.Functions\n"
1348 "{\n"
1349 " width:60%%;\n"
1350 "}\n"
1351 "table.Functions th\n"
1352 "{\n"
1353 " width:200px;\n"
1354 "}\n"
1355 "table.Modules\n"
1356 "{\n"
1357 " width:60%%;\n"
1358 "}\n"
1359 "table.Modules th\n"
1360 "{\n"
1361 " width:200px;\n"
1362 "}\n"
1363 "table.FunctionsSorted\n"
1364 "{\n"
1365 "}\n"
1366 "</style>\n"
1367 "<body topmargin=\"0\">\n"
1368 ,
1369 pHdr->offCommandLine
1370 ? (const char *)KPRF_OFF2PTR(P,FUNC, pHdr->offCommandLine, pHdr)
1371 : ""
1372 );
1373
1374 /*
1375 * Table of contents.
1376 */
1377 fprintf(pOut,
1378 "<h2>Table of Contents</h2>\n"
1379 "\n"
1380 "<ul>\n"
1381 " <li><a href=\"#Summary\" >1.0 Summary</a></li>\n"
1382 " <li><a href=\"#Functions\">2.0 Functions</a></li>\n"
1383 " <ul>\n"
1384 " <li><a href=\"#Functions-TimeOnStack\" >2.1 Time On Stack</a></li>\n"
1385 " <ul>\n"
1386 " <li><a href=\"#Functions-TimeOnStack-Avg\" >2.2.1 Time On Stack - Average</a></li>\n"
1387 " <li><a href=\"#Functions-TimeOnStack-Min\" >2.2.1 Time On Stack - Min</a></li>\n"
1388 " <li><a href=\"#Functions-TimeOnStack-Max\" >2.2.2 Time On Stack - Max</a></li>\n"
1389 " </ul>\n"
1390 " <li><a href=\"#Functions-TimeOnTopOfStack\">2.3 Time On Top Of Stack</a></li>\n"
1391 " <ul>\n"
1392 " <li><a href=\"#Functions-TimeOnTopOfStack-Avg\">2.3.1 Time On Top Of Stack - Average</a></li>\n"
1393 " <li><a href=\"#Functions-TimeOnTopOfStack-Min\">2.3.2 Time On Top Of Stack - Min</a></li>\n"
1394 " <li><a href=\"#Functions-TimeOnTopOfStack-Max\">2.3.3 Time On Top Of Stack - Max</a></li>\n"
1395 " </ul>\n"
1396 " <li><a href=\"#Functions-CallsTo\" >2.3 Calls To</a></li>\n"
1397 " <li><a href=\"#Functions-CallsFrom\" >2.4 Calls From</a></li>\n"
1398 " <li><a href=\"#Function-Details\" >2.5 Function Details</a></li>\n"
1399 " </ul>\n"
1400 " <li><a href=\"#Threads\" >3.0 Threads</a></li>\n"
1401 " <li><a href=\"#Modules\" >4.0 Modules</a></li>\n"
1402 "</ul>\n"
1403 "\n"
1404 "\n");
1405
1406 /*
1407 * Summary.
1408 */
1409 fprintf(pOut,
1410 "<h2><a name=\"Summary\">1.0 Summary</a></h2>\n"
1411 "\n"
1412 "<p>\n"
1413 "<table class=\"Summary\">\n");
1414 if (pHdr->offCommandLine)
1415 KPRF_NAME(HtmlWriteRowString)(pOut, "Command Line", NULL, "%s", (const char *)KPRF_OFF2PTR(P,FUNC, pHdr->offCommandLine, pHdr));
1416 KPRF_NAME(HtmlWriteRowU32X32)(pOut, "Threads", pHdr->cThreads, NULL);
1417 KPRF_NAME(HtmlWriteRowU32X32)(pOut, "Modules", pReport->cMods, NULL);
1418 KPRF_NAME(HtmlWriteRowU32X32)(pOut, "Functions", pHdr->cFunctions, NULL);
1419 KPRF_NAME(HtmlWriteRowTicks)(pOut, "Profiled", pReport->ProfiledTicks, pReport->ProfiledTicks);
1420 KPRF_NAME(HtmlWriteRowTicks)(pOut, "Sleep", pReport->SleepTicks, pReport->ProfiledTicks);
1421 KPRF_NAME(HtmlWriteRowTicks)(pOut, "Overhead", pReport->OverheadTicks, pReport->ProfiledTicks + pReport->OverheadTicks);
1422 KPRF_NAME(HtmlWriteRowCalls)(pOut, "Recorded Calls", pReport->cCalls, pReport->cCalls);
1423 fprintf(pOut, "<tr><td class=\"BlankRow\" colspan=7>&nbsp;</td></tr>\n");
1424 KPRF_NAME(HtmlWriteRowString)(pOut, "kProfiler Version ", NULL, "Mark 2 Alpha 1");
1425 KPRF_NAME(HtmlWriteRowString)(pOut, "kProfiler Build Time ", NULL, __DATE__ " " __TIME__);
1426 fprintf(pOut,
1427 "</table>\n"
1428 "</p>\n"
1429 "\n"
1430 "\n");
1431
1432 /*
1433 * Functions.
1434 */
1435 fprintf(pOut,
1436 "<h2><a name=\"Functions\">2.0 Functions</a></h2>\n"
1437 "\n");
1438
1439 qsort(&pReport->papSortedFunctions[0], pHdr->cFunctions, sizeof(pReport->papSortedFunctions[0]), KPRF_NAME(FuncCompareOnStack));
1440 KPRF_NAME(HtmlWriteSortedFunctions)(pReport, pOut, "Functions-TimeOnStack", "2.1 Time On Stack", KPRF_TYPE(,FIRSTCOLUMN_ON_STACK));
1441 qsort(&pReport->papSortedFunctions[0], pHdr->cFunctions, sizeof(pReport->papSortedFunctions[0]), KPRF_NAME(FuncCompareOnStackAvg));
1442 KPRF_NAME(HtmlWriteSortedFunctions)(pReport, pOut, "Functions-TimeOnStack-Avg", "2.2.1 Time On Stack - Average", KPRF_TYPE(,FIRSTCOLUMN_ON_STACK));
1443 qsort(&pReport->papSortedFunctions[0], pHdr->cFunctions, sizeof(pReport->papSortedFunctions[0]), KPRF_NAME(FuncCompareOnStackMin));
1444 KPRF_NAME(HtmlWriteSortedFunctions)(pReport, pOut, "Functions-TimeOnStack-Min", "2.2.2 Time On Stack - Min", KPRF_TYPE(,FIRSTCOLUMN_ON_STACK));
1445 qsort(&pReport->papSortedFunctions[0], pHdr->cFunctions, sizeof(pReport->papSortedFunctions[0]), KPRF_NAME(FuncCompareOnStackMax));
1446 KPRF_NAME(HtmlWriteSortedFunctions)(pReport, pOut, "Functions-TimeOnStack-Max", "2.2.3 Time On Stack - Max", KPRF_TYPE(,FIRSTCOLUMN_ON_STACK));
1447
1448 qsort(&pReport->papSortedFunctions[0], pHdr->cFunctions, sizeof(pReport->papSortedFunctions[0]), KPRF_NAME(FuncCompareOnTopOfStack));
1449 KPRF_NAME(HtmlWriteSortedFunctions)(pReport, pOut, "Functions-TimeOnTopOfStack", "2.2 Time On Top Of Stack", KPRF_TYPE(,FIRSTCOLUMN_ON_TOP_OF_STACK));
1450 qsort(&pReport->papSortedFunctions[0], pHdr->cFunctions, sizeof(pReport->papSortedFunctions[0]), KPRF_NAME(FuncCompareOnTopOfStackAvg));
1451 KPRF_NAME(HtmlWriteSortedFunctions)(pReport, pOut, "Functions-TimeOnTopOfStack-Avg","2.2.1 Time On Top Of Stack - Average", KPRF_TYPE(,FIRSTCOLUMN_ON_TOP_OF_STACK));
1452 qsort(&pReport->papSortedFunctions[0], pHdr->cFunctions, sizeof(pReport->papSortedFunctions[0]), KPRF_NAME(FuncCompareOnTopOfStackMin));
1453 KPRF_NAME(HtmlWriteSortedFunctions)(pReport, pOut, "Functions-TimeOnTopOfStack-Min","2.2.2 Time On Top Of Stack - Min", KPRF_TYPE(,FIRSTCOLUMN_ON_TOP_OF_STACK));
1454 qsort(&pReport->papSortedFunctions[0], pHdr->cFunctions, sizeof(pReport->papSortedFunctions[0]), KPRF_NAME(FuncCompareOnTopOfStackMax));
1455 KPRF_NAME(HtmlWriteSortedFunctions)(pReport, pOut, "Functions-TimeOnTopOfStack-Max","2.2.3 Time On Top Of Stack - Max", KPRF_TYPE(,FIRSTCOLUMN_ON_TOP_OF_STACK));
1456
1457 qsort(&pReport->papSortedFunctions[0], pHdr->cFunctions, sizeof(pReport->papSortedFunctions[0]), KPRF_NAME(FuncCompareCallsTo));
1458 KPRF_NAME(HtmlWriteSortedFunctions)(pReport, pOut, "Functions-CallsTo", "2.4 Calls To", KPRF_TYPE(,FIRSTCOLUMN_CALLS_TO));
1459
1460 qsort(&pReport->papSortedFunctions[0], pHdr->cFunctions, sizeof(pReport->papSortedFunctions[0]), KPRF_NAME(FuncCompareCallsFrom));
1461 KPRF_NAME(HtmlWriteSortedFunctions)(pReport, pOut, "Functions-CallsFrom", "2.5 Calls From", KPRF_TYPE(,FIRSTCOLUMN_CALLS_FROM));
1462
1463 fprintf(pOut,
1464 "<h2><a name=\"Function-Details\">2.5 Function Details</a></h2>\n"
1465 "\n"
1466 "<p>\n"
1467 "<table class=\"Functions\">\n");
1468 for (KU32 iFunc = 0; iFunc < pHdr->cFunctions; iFunc++)
1469 {
1470 KPRF_TYPE(P,REPORTFUNC) pReportFunc = &pReport->paFunctions[iFunc];
1471 KPRF_TYPE(PC,FUNC) pFunc = pReportFunc->pFunc;
1472
1473 fprintf(pOut,
1474 "<tr><td class=\"BlankRow\" colspan=7><a name=\"Func-%u\">&nbsp;</a></td></tr>\n",
1475 iFunc);
1476 KPRF_NAME(HtmlWriteRowU32)(pOut, "Function No.", iFunc, NULL);
1477 if (pReportFunc->pSym)
1478 KPRF_NAME(HtmlWriteRowString)(pOut, "Name", "Name", "%s", pReportFunc->pSym->szName);
1479 if (pReportFunc->pLine)
1480 KPRF_NAME(HtmlWriteRowString)(pOut, "Location", NULL, "<a href=\"file:///%s\">%s</a> Line #%d",
1481 pReportFunc->pLine->szFile, pReportFunc->pLine->szFile, pReportFunc->pLine->iLine);
1482 if (pReportFunc->pModSeg)
1483 {
1484 KPRF_NAME(HtmlWriteRowString)(pOut, "Module", NULL, "<a href=\"#Mod-%u\">%s</a>",
1485 pReportFunc->pModSeg->pMod->iMod, pReportFunc->pModSeg->pModSeg->szPath);
1486 KPRF_NAME(HtmlWriteRowString)(pOut, "Segment:Offset", NULL, "%x:%" KPRF_FMT_UPTR,
1487 pReportFunc->pModSeg->pModSeg->iSegment,
1488 pFunc->uEntryPtr - pReportFunc->pModSeg->pModSeg->uBasePtr);
1489 }
1490 KPRF_NAME(HtmlWriteRowUPTR)(pOut, "Address", pFunc->uEntryPtr, NULL);
1491
1492 KPRF_NAME(HtmlWriteRowTimeStat)(pOut, "On Stack", &pFunc->OnStack, pReport->ProfiledTicks);
1493 KPRF_NAME(HtmlWriteRowTimeStat)(pOut, "On Top Of Stack", &pFunc->OnTopOfStack, pReport->ProfiledTicks);
1494 KPRF_NAME(HtmlWriteRowCalls)(pOut, "Calls To", pFunc->cOnStack, pReport->cCalls);
1495 KPRF_NAME(HtmlWriteRowCalls)(pOut, "Calls From", pFunc->cCalls, pReport->cCalls);
1496
1497 fprintf(pOut,
1498 "\n");
1499 }
1500 fprintf(pOut,
1501 "</table>\n"
1502 "</p>\n"
1503 "\n");
1504
1505 /*
1506 * Threads.
1507 */
1508 fprintf(pOut,
1509 "<h2><a name=\"Threads\">3.0 Threads</a></h2>\n"
1510 "\n"
1511 "<p>\n"
1512 "<table class=\"Threads\">\n");
1513
1514 for (KU32 iThread = 0; iThread < pHdr->cThreads; iThread++)
1515 {
1516 KPRF_TYPE(PC,THREAD) pThread = pReport->paThreads[iThread].pThread;
1517
1518 fprintf(pOut,
1519 "<tr><td class=\"BlankRow\" colspan=7><a name=\"Thread-%u\">&nbsp;</a></td></tr>\n",
1520 iThread);
1521 KPRF_NAME(HtmlWriteRowU32)(pOut, "Thread No.", iThread, NULL);
1522 KPRF_NAME(HtmlWriteRowX64)(pOut, "Thread Id", pThread->ThreadId, NULL);
1523 if (pThread->szName[0])
1524 KPRF_NAME(HtmlWriteRowString)(pOut, "Name", "Name", "%s", pThread->szName);
1525 KPRF_NAME(HtmlWriteRowUPTR)(pOut, "Stack Base Address", pThread->uStackBasePtr, NULL);
1526 KPRF_NAME(HtmlWriteRowUPTR)(pOut, "Max Stack Depth", pThread->cbMaxStack, "bytes");
1527 //KPRF_NAME(HtmlWriteRowUPTR)(pOut, "Max Stack Depth", pThread->cMaxFrames, "frames"); /** @todo max stack frames! */
1528 KPRF_NAME(HtmlWriteRowTicks)(pOut, "Profiled", pThread->ProfiledTicks, pReport->ProfiledTicks);
1529 KPRF_NAME(HtmlWriteRowTicks)(pOut, "Sleep", pThread->SleepTicks, pReport->ProfiledTicks);
1530 KPRF_NAME(HtmlWriteRowTicks)(pOut, "Overhead", pThread->OverheadTicks, pReport->ProfiledTicks + pReport->OverheadTicks);
1531 KPRF_NAME(HtmlWriteRowCalls)(pOut, "Recorded Calls", pThread->cCalls, pReport->cCalls);
1532 KPRF_NAME(HtmlWriteRowU64)(pOut, "Unwinds", pThread->cUnwinds, NULL);
1533 KPRF_NAME(HtmlWriteRowU64)(pOut, "Profiler Stack Overflows", pThread->cOverflows, NULL);
1534 KPRF_NAME(HtmlWriteRowU64)(pOut, "Profiler Stack Switch Rejects", pThread->cStackSwitchRejects, NULL);
1535
1536 fprintf(pOut,
1537 "\n");
1538 }
1539 fprintf(pOut,
1540 "</table>\n"
1541 "</p>\n"
1542 "\n");
1543
1544
1545 /*
1546 * Modules.
1547 */
1548 fprintf(pOut,
1549 "<h2><a name=\"Modules\">4.0 Modules</a></h2>\n"
1550 "\n"
1551 "<p>\n"
1552 "<table class=\"Modules\">\n");
1553
1554 KPRF_TYPE(P,REPORTMOD) pMod = pReport->pFirstMod;
1555 KU32 iMod = 0;
1556 while (pMod)
1557 {
1558 fprintf(pOut,
1559 "<a name=\"Mod-%u\">\n"
1560 "<tr><td class=\"BlankRow\" colspan=7><a name=\"Module-%u\">&nbsp;</a></td></tr>\n",
1561 iMod, iMod);
1562 KPRF_NAME(HtmlWriteRowU32)(pOut, "Module No.", iMod, NULL);
1563 KPRF_NAME(HtmlWriteRowString)(pOut, "Name", "Name", "%s", pMod->pFirstSeg->pModSeg->szPath);
1564
1565 for (KPRF_TYPE(P,REPORTMODSEG) pSeg = pMod->pFirstSeg; pSeg; pSeg = pSeg->pNext)
1566 {
1567 char szName[64];
1568 sprintf(szName, "Segment No.%u - Base", pSeg->pModSeg->iSegment);
1569 KPRF_NAME(HtmlWriteRowUPTR)(pOut, szName, pSeg->pModSeg->uBasePtr, NULL);
1570 sprintf(szName, "Segment No.%u - Size", pSeg->pModSeg->iSegment);
1571 KPRF_NAME(HtmlWriteRowUPTR)(pOut, szName,
1572 pSeg->pModSeg->cbSegmentMinusOne + 1 > pSeg->pModSeg->cbSegmentMinusOne
1573 ? pSeg->pModSeg->cbSegmentMinusOne + 1
1574 : pSeg->pModSeg->cbSegmentMinusOne,
1575 NULL);
1576 }
1577
1578 KPRF_NAME(HtmlWriteRowTicks)(pOut, "On Stack", pMod->OnStackTicks, pReport->ProfiledTicks);
1579 KPRF_NAME(HtmlWriteRowTicks)(pOut, "On Top Of Stack", pMod->OnTopOfStackTicks, pReport->ProfiledTicks);
1580 KPRF_NAME(HtmlWriteRowU32)(pOut, "Functions", pMod->cFunctions, NULL);
1581
1582 fprintf(pOut,
1583 "\n");
1584
1585 /* next */
1586 iMod++;
1587 pMod = pMod->pNext;
1588 }
1589 fprintf(pOut,
1590 "</table>\n"
1591 "</p>\n"
1592 "\n");
1593
1594
1595 /*
1596 * The End.
1597 */
1598 fprintf(pOut,
1599 "</body>\n"
1600 "</html>\n");
1601 return 0;
1602}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette