summaryrefslogtreecommitdiffstats
path: root/src/lib/kStuff/kProfiler2/kPrf2Read.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/kStuff/kProfiler2/kPrf2Read.cpp')
-rw-r--r--src/lib/kStuff/kProfiler2/kPrf2Read.cpp503
1 files changed, 503 insertions, 0 deletions
diff --git a/src/lib/kStuff/kProfiler2/kPrf2Read.cpp b/src/lib/kStuff/kProfiler2/kPrf2Read.cpp
new file mode 100644
index 0000000..5f24e4a
--- /dev/null
+++ b/src/lib/kStuff/kProfiler2/kPrf2Read.cpp
@@ -0,0 +1,503 @@
+/* $Id: kPrf2Read.cpp 77 2016-06-22 17:03:55Z bird $ */
+/** @file
+ * kProfiler Mark 2 - The reader and producer of statistics.
+ */
+
+/*
+ * Copyright (c) 2006-2007 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <k/kDbg.h>
+
+
+/** @def KPRF_OFF2PTR
+ * Internal helper for converting a offset to a pointer.
+ * @internal
+ */
+#define KPRF_OFF2PTR(TypePrefix, TypeName, off, pHdr) \
+ ( (KPRF_TYPE(TypePrefix, TypeName)) ((off) + (KUPTR)pHdr) )
+
+/** @def KPRF_ALIGN
+ * The usual align macro.
+ * @internal
+ */
+#define KPRF_ALIGN(n, align) ( ((n) + ( (align) - 1)) & ~((align) - 1) )
+
+/** @def KPRF_OFFSETOF
+ * My usual extended offsetof macro, except this returns KU32 and mangles the type name.
+ * @internal
+ */
+#define KPRF_OFFSETOF(kPrfType, Member) ( (KU32)(KUPTR)&((KPRF_TYPE(P,kPrfType))0)->Member )
+
+/** @def PRF_SIZEOF
+ * Size of a kPrf type.
+ * @internal
+ */
+#define KPRF_SIZEOF(kPrfType) sizeof(KPRF_TYPE(,kPrfType))
+
+#ifdef _MSC_VER
+# define KPRF_FMT_U64 "I64u"
+# define KPRF_FMT_X64 "I64x"
+# define KPRF_FMT_I64 "I64d"
+#else
+# define KPRF_FMT_X64 "llx"
+# define KPRF_FMT_U64 "llu"
+# define KPRF_FMT_I64 "lld"
+#endif
+
+
+/*
+ * Instantiate the readers.
+ */
+/* 32-bit */
+#define KPRF_NAME(Suffix) KPrf32##Suffix
+#define KPRF_TYPE(Prefix,Suffix) Prefix##KPRF32##Suffix
+#define KPRF_BITS 32
+#define KPRF_FMT_UPTR "#010x"
+
+#include "prfcore.h.h"
+#include "prfreader.cpp.h"
+
+#undef KPRF_FMT_UPTR
+#undef KPRF_NAME
+#undef KPRF_TYPE
+#undef KPRF_BITS
+
+/* 64-bit */
+#define KPRF_NAME(Suffix) KPrf64##Suffix
+#define KPRF_TYPE(Prefix,Suffix) Prefix##KPRF64##Suffix
+#define KPRF_BITS 64
+#ifdef _MSC_VER
+# define KPRF_FMT_UPTR "#018I64x"
+#else
+# define KPRF_FMT_UPTR "#018llx"
+#endif
+
+#include "prfcore.h.h"
+#include "prfreader.cpp.h"
+
+#undef KPRF_FMT_UPTR
+#undef KPRF_NAME
+#undef KPRF_TYPE
+#undef KPRF_BITS
+
+
+/*******************************************************************************
+* Structures and Typedefs *
+*******************************************************************************/
+/**
+ * Header union type.
+ */
+typedef union KPRFHDR
+{
+ KPRF32HDR Hdr32;
+ KPRF64HDR Hdr64;
+} KPRFHDR;
+typedef KPRFHDR *PKPRFHDR;
+typedef const KPRFHDR *PCKPRFHDR;
+
+
+
+/**
+ * Read the data set into memory.
+ *
+ * @returns Pointer to the loaded data set. (release using free()).
+ *
+ * @param pszFilename The path to the profiler data set.
+ * @param pcb Where to store the size of the data set.
+ * @param pOut Where to write errors.
+ */
+PKPRFHDR kPrfLoad(const char *pszFilename, KU32 *pcb, FILE *pOut)
+{
+ FILE *pFile = fopen(pszFilename, "rb");
+ if (!pFile)
+ {
+ fprintf(pOut, "Cannot open '%s' for reading!\n", pszFilename);
+ return NULL;
+ }
+
+ /*
+ * Read the file into memory.
+ */
+ long cbFile;
+ if ( !fseek(pFile, 0, SEEK_END)
+ && (cbFile = ftell(pFile)) >= 0
+ && !fseek(pFile, 0, SEEK_SET)
+ )
+ {
+ if (pcb)
+ *pcb = cbFile;
+
+ void *pvData = malloc(cbFile);
+ if (pvData)
+ {
+ if (fread(pvData, cbFile, 1, pFile))
+ {
+
+ fclose(pFile);
+ return (PKPRFHDR)pvData;
+ }
+ fprintf(pOut, "Failed reading '%s' into memory!\n", pszFilename);
+ free(pvData);
+ }
+ else
+ fprintf(pOut, "Failed to allocate %ld bytes of memory for reading the file '%s' into!\n", cbFile, pszFilename);
+ }
+ else
+ fprintf(pOut, "Failed to determin the size of '%s'!\n", pszFilename);
+
+ fclose(pFile);
+ return NULL;
+}
+
+
+/**
+ * Validates the data set
+ *
+ * @returns true if valid.
+ * @returns false if invalid.
+ *
+ * @param pHdr Pointer to the data set.
+ * @param cb The size of the data set.
+ * @param pOut Where to write error messages.
+ */
+static bool kPrfIsValidate(PCKPRFHDR pHdr, KU32 cb, FILE *pOut)
+{
+ /*
+ * We ASSUMES that the header is identicial with the exception
+ * of the uBasePtr size. (this is padded out and the upper bits are all zero)
+ */
+
+ if ( pHdr->Hdr32.u32Magic != KPRF32HDR_MAGIC
+ && pHdr->Hdr32.u32Magic != KPRF64HDR_MAGIC)
+ {
+ fprintf(pOut, "Invalid magic %#x\n", pHdr->Hdr32.u32Magic);
+ return false;
+ }
+
+ if ( pHdr->Hdr32.cFormatBits != 32
+ && pHdr->Hdr32.cFormatBits != 64)
+ {
+ fprintf(pOut, "Invalid/Unsupported bit count %u\n", pHdr->Hdr32.cFormatBits);
+ return false;
+ }
+
+ if (pHdr->Hdr32.cb > cb)
+ {
+ fprintf(pOut, "Data set size mismatch. Header say %#x, input is %#x\n", pHdr->Hdr32.cb, cb);
+ return false;
+ }
+
+#define KPRF_VALIDATE_SIZE(MemBaseName, cb32, cb64) do {\
+ if (pHdr->Hdr32.cb##MemBaseName > (pHdr->Hdr32.cFormatBits == 32 ? cb32 : cb64)) \
+ { \
+ fprintf(pOut, "cb" #MemBaseName " was expected to be %#x but is %#x. Probably a format change, rebuild.\n", \
+ (unsigned)(pHdr->Hdr32.cFormatBits == 32 ? cb32 : cb64), pHdr->Hdr32.cb##MemBaseName); \
+ return false; \
+ }\
+ } while (0)
+
+ KPRF_VALIDATE_SIZE(Function, sizeof(KPRF32FUNC), sizeof(KPRF64FUNC));
+ KPRF_VALIDATE_SIZE(Thread, sizeof(KPRF32THREAD), sizeof(KPRF64THREAD));
+ KPRF_VALIDATE_SIZE(Stack,
+ (KU32)&((PKPRF32STACK)0)->aFrames[pHdr->Hdr32.cMaxStackFrames],
+ (KU32)&((PKPRF64STACK)0)->aFrames[pHdr->Hdr32.cMaxStackFrames]);
+
+ KUPTR cbHeader = (KUPTR)&pHdr->Hdr32.aiFunctions[pHdr->Hdr32.cFunctions] - (KUPTR)pHdr;
+ if ( cbHeader != (KU32)cbHeader
+ || cbHeader >= cb)
+ {
+ fprintf(pOut, "cFunctions (%#x) is too large to fit the lookup table inside the data set.\n",
+ pHdr->Hdr32.cFunctions);
+ return false;
+ }
+
+ /* The space assignment is hereby required to be equal to the member order in the header. */
+ KU32 offMin = cbHeader;
+#define KPRF_VALIDATE_OFF(off, name) do {\
+ if ( off > 0 \
+ && off < offMin) \
+ { \
+ fprintf(pOut, #name " (%#x) is overlapping with other element or invalid space assignment order.\n", off); \
+ return false; \
+ }\
+ if (off >= cb) \
+ { \
+ fprintf(pOut, #name " (%#x) is outside the data set (%#x)\n", off, cb); \
+ return false; \
+ }\
+ } while (0)
+#define KPRF_VALIDATE_MEM(MemBaseName) do {\
+ KPRF_VALIDATE_OFF(pHdr->Hdr32.off##MemBaseName##s, off##MemBaseName##s); \
+ if ( pHdr->Hdr32.off##MemBaseName##s \
+ && ( pHdr->Hdr32.off##MemBaseName##s + pHdr->Hdr32.cb##MemBaseName * pHdr->Hdr32.cMax##MemBaseName##s > cb \
+ || pHdr->Hdr32.off##MemBaseName##s + pHdr->Hdr32.cb##MemBaseName * pHdr->Hdr32.cMax##MemBaseName##s < pHdr->Hdr32.off##MemBaseName##s)\
+ ) \
+ { \
+ fprintf(pOut, #MemBaseName " (%#x) is outside the data set (%#x)\n", \
+ pHdr->Hdr32.off##MemBaseName##s + pHdr->Hdr32.cb##MemBaseName * pHdr->Hdr32.cMax##MemBaseName##s, cb); \
+ return false; \
+ }\
+ if (pHdr->Hdr32.c##MemBaseName##s > pHdr->Hdr32.cMax##MemBaseName##s) \
+ { \
+ fprintf(pOut, "c" #MemBaseName " (%#x) higher than the max (%#x)\n", \
+ pHdr->Hdr32.c##MemBaseName##s, pHdr->Hdr32.cMax##MemBaseName##s); \
+ return false; \
+ } \
+ if (pHdr->Hdr32.off##MemBaseName##s) \
+ offMin += pHdr->Hdr32.cb##MemBaseName * pHdr->Hdr32.cMax##MemBaseName##s; \
+ } while (0)
+
+ KPRF_VALIDATE_MEM(Function);
+ KPRF_VALIDATE_OFF(pHdr->Hdr32.offModSegs, offModSegs);
+ if (pHdr->Hdr32.offModSegs)
+ KPRF_VALIDATE_OFF(pHdr->Hdr32.offModSegs + pHdr->Hdr32.cbMaxModSegs, cbMaxModSegs);
+ if (pHdr->Hdr32.cbModSegs > pHdr->Hdr32.cbMaxModSegs)
+ {
+ fprintf(pOut, "ccbModSegs (%#x) higher than the max (%#x)\n",
+ pHdr->Hdr32.cbModSegs, pHdr->Hdr32.cbMaxModSegs);
+ return false;
+ }
+ if (pHdr->Hdr32.offModSegs) \
+ offMin += pHdr->Hdr32.cbMaxModSegs; \
+ KPRF_VALIDATE_MEM(Thread);
+ KPRF_VALIDATE_MEM(Stack);
+ KPRF_VALIDATE_OFF(pHdr->Hdr32.offCommandLine, offCommandLine);
+ KPRF_VALIDATE_OFF(pHdr->Hdr32.offCommandLine + pHdr->Hdr32.cchCommandLine, cchCommandLine);
+
+ /*
+ * Validate the function lookup table
+ */
+ for (KU32 i = 0; i < pHdr->Hdr32.cFunctions; i++)
+ if (pHdr->Hdr32.aiFunctions[i] >= pHdr->Hdr32.cFunctions)
+ {
+ fprintf(pOut, "Function lookup entry %#x is invalid: index %#x, max is %#x\n",
+ i, pHdr->Hdr32.aiFunctions[i], pHdr->Hdr32.cFunctions);
+ return false;
+ }
+
+ /*
+ * Validate the functions.
+ */
+ switch (pHdr->Hdr32.cFormatBits)
+ {
+ case 32:
+ return KPrf32IsValid(&pHdr->Hdr32, cb, pOut);
+
+ case 64:
+ return KPrf64IsValid(&pHdr->Hdr64, cb, pOut);
+ }
+ return false;
+#undef KPRF_VALIDATE_SIZE
+#undef KPRF_VALIDATE_MEM
+#undef KPRF_VALIDATE_OFF
+}
+
+
+/**
+ * Dumps a kProfiler 2 format file.
+ *
+ * @returns 0 on success.
+ * @returns -1 on failure.
+ *
+ * @param pszFilename The path to the profiler data set.
+ * @param pOut Where to write the output.
+ */
+int KPrfDumpFile(const char *pszFilename, FILE *pOut)
+{
+ /*
+ * Load and validate the data set.
+ */
+ KU32 cb;
+ PKPRFHDR pHdr = kPrfLoad(pszFilename, &cb, pOut);
+ if (!pHdr)
+ return -1;
+ if (!kPrfIsValidate(pHdr, cb, pOut))
+ return -1;
+
+ /*
+ * Switch to the appropirate dumper routine.
+ */
+ int rc;
+ switch (pHdr->Hdr32.cFormatBits)
+ {
+ case 32:
+ rc = KPrf32Dump(&pHdr->Hdr32, pOut);
+ break;
+
+ case 64:
+ rc = KPrf64Dump(&pHdr->Hdr64, pOut);
+ break;
+
+ default:
+ fprintf(stderr, "Unsupported bit count %d\n", pHdr->Hdr32.cFormatBits);
+ rc = -1;
+ break;
+ }
+
+ return rc;
+}
+
+
+/**
+ * Creates a HTML report from a kProfiler 2 format file.
+ *
+ * @returns 0 on success.
+ * @returns -1 on failure.
+ *
+ * @param pszFilename The path to the profiler data set.
+ * @param pOut Where to write the output.
+ */
+int KPrfHtmlReport(const char *pszFilename, FILE *pOut)
+{
+ /*
+ * Load and validate the data set.
+ */
+ KU32 cb;
+ PKPRFHDR pHdr = kPrfLoad(pszFilename, &cb, pOut);
+ if (!pHdr)
+ return -1;
+ if (!kPrfIsValidate(pHdr, cb, pOut))
+ return -1;
+
+ /*
+ * Switch to the appropirate dumper routine.
+ */
+ int rc;
+ switch (pHdr->Hdr32.cFormatBits)
+ {
+ case 32:
+ {
+ PKPRF32REPORT pReport;
+ rc = KPrf32Analyse(&pHdr->Hdr32, &pReport);
+ if (!rc)
+ {
+ rc = KPrf32WriteHtmlReport(pReport, pOut);
+ if (rc)
+ fprintf(stderr, "Error while writing HTML report for '%s'\n", pszFilename);
+ KPrf32DeleteReport(pReport);
+ }
+ else
+ fprintf(stderr, "Analysis of '%s' failed!\n", pszFilename);
+ break;
+ }
+
+ case 64:
+ {
+ PKPRF64REPORT pReport;
+ rc = KPrf64Analyse(&pHdr->Hdr64, &pReport);
+ if (!rc)
+ {
+ rc = KPrf64WriteHtmlReport(pReport, pOut);
+ if (rc)
+ fprintf(stderr, "Error while writing HTML report for '%s'\n", pszFilename);
+ KPrf64DeleteReport(pReport);
+ }
+ else
+ fprintf(stderr, "Analysis of '%s' failed!\n", pszFilename);
+ break;
+ }
+
+ default:
+ fprintf(stderr, "Unsupported bit count %d\n", pHdr->Hdr32.cFormatBits);
+ rc = -1;
+ break;
+ }
+
+ return rc;
+}
+
+
+
+/**
+ * Prints the usage.
+ */
+static int Usage(void)
+{
+ printf("kProfiler MK2 - Reader & Producer of Statistics\n"
+ "usage: kPrf2Read [-r|-d] <file1> [[-r|-d] file2 []]\n"
+ );
+ return 1;
+}
+
+
+int main(int argc, char **argv)
+{
+ /*
+ * Parse arguments.
+ */
+ if (argc <= 1)
+ return Usage();
+ enum { OP_DUMP, OP_HTML } enmOp = OP_DUMP;
+ for (int i = 1; i < argc; i++)
+ {
+ if (argv[i][0] == '-')
+ {
+ switch (argv[i][1])
+ {
+ case 'h':
+ case 'H':
+ case '?':
+ case '-':
+ return Usage();
+
+ case 'd':
+ enmOp = OP_DUMP;
+ break;
+
+ case 'r':
+ enmOp = OP_HTML;
+ break;
+
+ default:
+ printf("Syntax error: Unknown argument '%s'\n", argv[i]);
+ return 1;
+ }
+ }
+ else
+ {
+ int rc;
+ switch (enmOp)
+ {
+ case OP_DUMP:
+ rc = KPrfDumpFile(argv[i], stdout);
+ break;
+ case OP_HTML:
+ rc = KPrfHtmlReport(argv[i], stdout);
+ break;
+ }
+ if (rc)
+ return rc;
+ }
+ }
+
+ return 0;
+}