1 /* $Id: kPrf2Read.cpp 77 2016-06-22 17:03:55Z bird $ */
2 /** @file
3  * kProfiler Mark 2 - The reader and producer of statistics.
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 *   Header Files                                                               *
33 *******************************************************************************/
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <stdarg.h>
38 #include <k/kDbg.h>
39 
40 
41 /** @def KPRF_OFF2PTR
42  * Internal helper for converting a offset to a pointer.
43  * @internal
44  */
45 #define KPRF_OFF2PTR(TypePrefix, TypeName, off, pHdr) \
46     ( (KPRF_TYPE(TypePrefix, TypeName)) ((off) + (KUPTR)pHdr) )
47 
48 /** @def KPRF_ALIGN
49  * The usual align macro.
50  * @internal
51  */
52 #define KPRF_ALIGN(n, align) ( ((n) + ( (align) - 1)) & ~((align) - 1) )
53 
54 /** @def KPRF_OFFSETOF
55  * My usual extended offsetof macro, except this returns KU32 and mangles the type name.
56  * @internal
57  */
58 #define KPRF_OFFSETOF(kPrfType, Member) ( (KU32)(KUPTR)&((KPRF_TYPE(P,kPrfType))0)->Member )
59 
60 /** @def PRF_SIZEOF
61  * Size of a kPrf type.
62  * @internal
63  */
64 #define KPRF_SIZEOF(kPrfType)           sizeof(KPRF_TYPE(,kPrfType))
65 
66 #ifdef _MSC_VER
67 # define KPRF_FMT_U64   "I64u"
68 # define KPRF_FMT_X64   "I64x"
69 # define KPRF_FMT_I64   "I64d"
70 #else
71 # define KPRF_FMT_X64   "llx"
72 # define KPRF_FMT_U64   "llu"
73 # define KPRF_FMT_I64   "lld"
74 #endif
75 
76 
77 /*
78  * Instantiate the readers.
79  */
80 /* 32-bit */
81 #define KPRF_NAME(Suffix)               KPrf32##Suffix
82 #define KPRF_TYPE(Prefix,Suffix)        Prefix##KPRF32##Suffix
83 #define KPRF_BITS                       32
84 #define KPRF_FMT_UPTR                   "#010x"
85 
86 #include "prfcore.h.h"
87 #include "prfreader.cpp.h"
88 
89 #undef KPRF_FMT_UPTR
90 #undef KPRF_NAME
91 #undef KPRF_TYPE
92 #undef KPRF_BITS
93 
94 /* 64-bit */
95 #define KPRF_NAME(Suffix)               KPrf64##Suffix
96 #define KPRF_TYPE(Prefix,Suffix)        Prefix##KPRF64##Suffix
97 #define KPRF_BITS                       64
98 #ifdef _MSC_VER
99 # define KPRF_FMT_UPTR                  "#018I64x"
100 #else
101 # define KPRF_FMT_UPTR                  "#018llx"
102 #endif
103 
104 #include "prfcore.h.h"
105 #include "prfreader.cpp.h"
106 
107 #undef KPRF_FMT_UPTR
108 #undef KPRF_NAME
109 #undef KPRF_TYPE
110 #undef KPRF_BITS
111 
112 
113 /*******************************************************************************
114 *   Structures and Typedefs                                                    *
115 *******************************************************************************/
116 /**
117  * Header union type.
118  */
119 typedef union KPRFHDR
120 {
121     KPRF32HDR   Hdr32;
122     KPRF64HDR   Hdr64;
123 } KPRFHDR;
124 typedef KPRFHDR *PKPRFHDR;
125 typedef const KPRFHDR *PCKPRFHDR;
126 
127 
128 
129 /**
130  * Read the data set into memory.
131  *
132  * @returns Pointer to the loaded data set. (release using free()).
133  *
134  * @param   pszFilename         The path to the profiler data set.
135  * @param   pcb                 Where to store the size of the data set.
136  * @param   pOut                Where to write errors.
137  */
kPrfLoad(const char * pszFilename,KU32 * pcb,FILE * pOut)138 PKPRFHDR kPrfLoad(const char *pszFilename, KU32 *pcb, FILE *pOut)
139 {
140     FILE *pFile = fopen(pszFilename, "rb");
141     if (!pFile)
142     {
143         fprintf(pOut, "Cannot open '%s' for reading!\n", pszFilename);
144         return NULL;
145     }
146 
147     /*
148      * Read the file into memory.
149      */
150     long cbFile;
151     if (    !fseek(pFile, 0, SEEK_END)
152         &&  (cbFile = ftell(pFile)) >= 0
153         &&  !fseek(pFile, 0, SEEK_SET)
154         )
155     {
156         if (pcb)
157             *pcb = cbFile;
158 
159         void *pvData = malloc(cbFile);
160         if (pvData)
161         {
162             if (fread(pvData, cbFile, 1, pFile))
163             {
164 
165                 fclose(pFile);
166                 return (PKPRFHDR)pvData;
167             }
168             fprintf(pOut, "Failed reading '%s' into memory!\n", pszFilename);
169             free(pvData);
170         }
171         else
172             fprintf(pOut, "Failed to allocate %ld bytes of memory for reading the file '%s' into!\n", cbFile, pszFilename);
173     }
174     else
175         fprintf(pOut, "Failed to determin the size of '%s'!\n", pszFilename);
176 
177     fclose(pFile);
178     return NULL;
179 }
180 
181 
182 /**
183  * Validates the data set
184  *
185  * @returns true if valid.
186  * @returns false if invalid.
187  *
188  * @param   pHdr        Pointer to the data set.
189  * @param   cb          The size of the data set.
190  * @param   pOut        Where to write error messages.
191  */
kPrfIsValidate(PCKPRFHDR pHdr,KU32 cb,FILE * pOut)192 static bool kPrfIsValidate(PCKPRFHDR pHdr, KU32 cb, FILE *pOut)
193 {
194     /*
195      * We ASSUMES that the header is identicial with the exception
196      * of the uBasePtr size. (this is padded out and the upper bits are all zero)
197      */
198 
199     if (    pHdr->Hdr32.u32Magic != KPRF32HDR_MAGIC
200         &&  pHdr->Hdr32.u32Magic != KPRF64HDR_MAGIC)
201     {
202         fprintf(pOut, "Invalid magic %#x\n", pHdr->Hdr32.u32Magic);
203         return false;
204     }
205 
206     if (    pHdr->Hdr32.cFormatBits != 32
207         &&  pHdr->Hdr32.cFormatBits != 64)
208     {
209         fprintf(pOut, "Invalid/Unsupported bit count %u\n", pHdr->Hdr32.cFormatBits);
210         return false;
211     }
212 
213     if (pHdr->Hdr32.cb > cb)
214     {
215         fprintf(pOut, "Data set size mismatch. Header say %#x, input is %#x\n", pHdr->Hdr32.cb, cb);
216         return false;
217     }
218 
219 #define KPRF_VALIDATE_SIZE(MemBaseName, cb32, cb64) do {\
220         if (pHdr->Hdr32.cb##MemBaseName > (pHdr->Hdr32.cFormatBits == 32 ? cb32 : cb64)) \
221         { \
222             fprintf(pOut, "cb" #MemBaseName " was expected to be %#x but is %#x. Probably a format change, rebuild.\n", \
223                     (unsigned)(pHdr->Hdr32.cFormatBits == 32 ? cb32 : cb64), pHdr->Hdr32.cb##MemBaseName); \
224             return false; \
225         }\
226     } while (0)
227 
228     KPRF_VALIDATE_SIZE(Function, sizeof(KPRF32FUNC), sizeof(KPRF64FUNC));
229     KPRF_VALIDATE_SIZE(Thread, sizeof(KPRF32THREAD), sizeof(KPRF64THREAD));
230     KPRF_VALIDATE_SIZE(Stack,
231                        (KU32)&((PKPRF32STACK)0)->aFrames[pHdr->Hdr32.cMaxStackFrames],
232                        (KU32)&((PKPRF64STACK)0)->aFrames[pHdr->Hdr32.cMaxStackFrames]);
233 
234     KUPTR cbHeader = (KUPTR)&pHdr->Hdr32.aiFunctions[pHdr->Hdr32.cFunctions] - (KUPTR)pHdr;
235     if (    cbHeader != (KU32)cbHeader
236         ||  cbHeader >= cb)
237     {
238         fprintf(pOut, "cFunctions (%#x) is too large to fit the lookup table inside the data set.\n",
239                 pHdr->Hdr32.cFunctions);
240         return false;
241     }
242 
243     /* The space assignment is hereby required to be equal to the member order in the header. */
244     KU32 offMin = cbHeader;
245 #define KPRF_VALIDATE_OFF(off, name) do {\
246         if (    off > 0 \
247             &&  off < offMin) \
248         { \
249             fprintf(pOut, #name " (%#x) is overlapping with other element or invalid space assignment order.\n", off); \
250             return false; \
251         }\
252         if (off >= cb) \
253         { \
254             fprintf(pOut, #name " (%#x) is outside the data set (%#x)\n", off, cb); \
255             return false; \
256         }\
257     } while (0)
258 #define KPRF_VALIDATE_MEM(MemBaseName) do {\
259         KPRF_VALIDATE_OFF(pHdr->Hdr32.off##MemBaseName##s, off##MemBaseName##s); \
260         if (    pHdr->Hdr32.off##MemBaseName##s \
261             &&  (   pHdr->Hdr32.off##MemBaseName##s + pHdr->Hdr32.cb##MemBaseName * pHdr->Hdr32.cMax##MemBaseName##s > cb \
262                  || pHdr->Hdr32.off##MemBaseName##s + pHdr->Hdr32.cb##MemBaseName * pHdr->Hdr32.cMax##MemBaseName##s < pHdr->Hdr32.off##MemBaseName##s)\
263            ) \
264         { \
265             fprintf(pOut, #MemBaseName " (%#x) is outside the data set (%#x)\n", \
266                     pHdr->Hdr32.off##MemBaseName##s + pHdr->Hdr32.cb##MemBaseName * pHdr->Hdr32.cMax##MemBaseName##s, cb); \
267             return false; \
268         }\
269         if (pHdr->Hdr32.c##MemBaseName##s > pHdr->Hdr32.cMax##MemBaseName##s) \
270         { \
271             fprintf(pOut, "c" #MemBaseName " (%#x) higher than the max (%#x)\n", \
272                     pHdr->Hdr32.c##MemBaseName##s, pHdr->Hdr32.cMax##MemBaseName##s); \
273             return false; \
274         } \
275         if (pHdr->Hdr32.off##MemBaseName##s) \
276             offMin += pHdr->Hdr32.cb##MemBaseName * pHdr->Hdr32.cMax##MemBaseName##s; \
277     } while (0)
278 
279     KPRF_VALIDATE_MEM(Function);
280     KPRF_VALIDATE_OFF(pHdr->Hdr32.offModSegs, offModSegs);
281     if (pHdr->Hdr32.offModSegs)
282         KPRF_VALIDATE_OFF(pHdr->Hdr32.offModSegs + pHdr->Hdr32.cbMaxModSegs, cbMaxModSegs);
283     if (pHdr->Hdr32.cbModSegs > pHdr->Hdr32.cbMaxModSegs)
284     {
285         fprintf(pOut, "ccbModSegs (%#x) higher than the max (%#x)\n",
286                 pHdr->Hdr32.cbModSegs, pHdr->Hdr32.cbMaxModSegs);
287         return false;
288     }
289     if (pHdr->Hdr32.offModSegs) \
290         offMin += pHdr->Hdr32.cbMaxModSegs; \
291     KPRF_VALIDATE_MEM(Thread);
292     KPRF_VALIDATE_MEM(Stack);
293     KPRF_VALIDATE_OFF(pHdr->Hdr32.offCommandLine, offCommandLine);
294     KPRF_VALIDATE_OFF(pHdr->Hdr32.offCommandLine + pHdr->Hdr32.cchCommandLine, cchCommandLine);
295 
296     /*
297      * Validate the function lookup table
298      */
299     for (KU32 i = 0; i < pHdr->Hdr32.cFunctions; i++)
300         if (pHdr->Hdr32.aiFunctions[i] >= pHdr->Hdr32.cFunctions)
301         {
302             fprintf(pOut, "Function lookup entry %#x is invalid: index %#x, max is %#x\n",
303                     i, pHdr->Hdr32.aiFunctions[i], pHdr->Hdr32.cFunctions);
304             return false;
305         }
306 
307     /*
308      * Validate the functions.
309      */
310     switch (pHdr->Hdr32.cFormatBits)
311     {
312         case 32:
313             return KPrf32IsValid(&pHdr->Hdr32, cb, pOut);
314 
315         case 64:
316             return KPrf64IsValid(&pHdr->Hdr64, cb, pOut);
317     }
318     return false;
319 #undef KPRF_VALIDATE_SIZE
320 #undef KPRF_VALIDATE_MEM
321 #undef KPRF_VALIDATE_OFF
322 }
323 
324 
325 /**
326  * Dumps a kProfiler 2 format file.
327  *
328  * @returns 0 on success.
329  * @returns -1 on failure.
330  *
331  * @param   pszFilename         The path to the profiler data set.
332  * @param   pOut                Where to write the output.
333  */
KPrfDumpFile(const char * pszFilename,FILE * pOut)334 int KPrfDumpFile(const char *pszFilename, FILE *pOut)
335 {
336     /*
337      * Load and validate the data set.
338      */
339     KU32 cb;
340     PKPRFHDR pHdr = kPrfLoad(pszFilename, &cb, pOut);
341     if (!pHdr)
342         return -1;
343     if (!kPrfIsValidate(pHdr, cb, pOut))
344         return -1;
345 
346     /*
347      * Switch to the appropirate dumper routine.
348      */
349     int rc;
350     switch (pHdr->Hdr32.cFormatBits)
351     {
352         case 32:
353             rc = KPrf32Dump(&pHdr->Hdr32, pOut);
354             break;
355 
356         case 64:
357             rc = KPrf64Dump(&pHdr->Hdr64, pOut);
358             break;
359 
360         default:
361             fprintf(stderr, "Unsupported bit count %d\n", pHdr->Hdr32.cFormatBits);
362             rc = -1;
363             break;
364     }
365 
366     return rc;
367 }
368 
369 
370 /**
371  * Creates a HTML report from a kProfiler 2 format file.
372  *
373  * @returns 0 on success.
374  * @returns -1 on failure.
375  *
376  * @param   pszFilename         The path to the profiler data set.
377  * @param   pOut                Where to write the output.
378  */
KPrfHtmlReport(const char * pszFilename,FILE * pOut)379 int KPrfHtmlReport(const char *pszFilename, FILE *pOut)
380 {
381     /*
382      * Load and validate the data set.
383      */
384     KU32 cb;
385     PKPRFHDR pHdr = kPrfLoad(pszFilename, &cb, pOut);
386     if (!pHdr)
387         return -1;
388     if (!kPrfIsValidate(pHdr, cb, pOut))
389         return -1;
390 
391     /*
392      * Switch to the appropirate dumper routine.
393      */
394     int rc;
395     switch (pHdr->Hdr32.cFormatBits)
396     {
397         case 32:
398         {
399             PKPRF32REPORT pReport;
400             rc = KPrf32Analyse(&pHdr->Hdr32, &pReport);
401             if (!rc)
402             {
403                 rc = KPrf32WriteHtmlReport(pReport, pOut);
404                 if (rc)
405                     fprintf(stderr, "Error while writing HTML report for '%s'\n", pszFilename);
406                 KPrf32DeleteReport(pReport);
407             }
408             else
409                 fprintf(stderr, "Analysis of '%s' failed!\n", pszFilename);
410             break;
411         }
412 
413         case 64:
414         {
415             PKPRF64REPORT pReport;
416             rc = KPrf64Analyse(&pHdr->Hdr64, &pReport);
417             if (!rc)
418             {
419                 rc = KPrf64WriteHtmlReport(pReport, pOut);
420                 if (rc)
421                     fprintf(stderr, "Error while writing HTML report for '%s'\n", pszFilename);
422                 KPrf64DeleteReport(pReport);
423             }
424             else
425                 fprintf(stderr, "Analysis of '%s' failed!\n", pszFilename);
426             break;
427         }
428 
429         default:
430             fprintf(stderr, "Unsupported bit count %d\n", pHdr->Hdr32.cFormatBits);
431             rc = -1;
432             break;
433     }
434 
435     return rc;
436 }
437 
438 
439 
440 /**
441  * Prints the usage.
442  */
Usage(void)443 static int Usage(void)
444 {
445     printf("kProfiler MK2 - Reader & Producer of Statistics\n"
446            "usage: kPrf2Read [-r|-d] <file1> [[-r|-d] file2 []]\n"
447            );
448     return 1;
449 }
450 
451 
main(int argc,char ** argv)452 int main(int argc, char **argv)
453 {
454     /*
455      * Parse arguments.
456      */
457     if (argc <= 1)
458         return Usage();
459     enum { OP_DUMP, OP_HTML } enmOp = OP_DUMP;
460     for (int i = 1; i < argc; i++)
461     {
462         if (argv[i][0] == '-')
463         {
464             switch (argv[i][1])
465             {
466                 case 'h':
467                 case 'H':
468                 case '?':
469                 case '-':
470                     return Usage();
471 
472                 case 'd':
473                     enmOp = OP_DUMP;
474                     break;
475 
476                 case 'r':
477                     enmOp = OP_HTML;
478                     break;
479 
480                 default:
481                     printf("Syntax error: Unknown argument '%s'\n", argv[i]);
482                     return 1;
483             }
484         }
485         else
486         {
487             int rc;
488             switch (enmOp)
489             {
490                 case OP_DUMP:
491                     rc = KPrfDumpFile(argv[i], stdout);
492                     break;
493                 case OP_HTML:
494                     rc = KPrfHtmlReport(argv[i], stdout);
495                     break;
496             }
497             if (rc)
498                 return rc;
499         }
500     }
501 
502     return 0;
503 }
504