source: trunk/kProfile/prfreader.cpp.h@ 3524

Last change on this file since 3524 was 3524, checked in by bird, 18 years ago

kProfile Mark II. Some early/old code.

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