Index: trunk/kProfile/DBGInternal.h
===================================================================
--- trunk/kProfile/DBGInternal.h	(revision 3524)
+++ trunk/kProfile/DBGInternal.h	(revision 3524)
@@ -0,0 +1,114 @@
+/* $Id: $ */
+/** @file
+ *
+ * kProfile Mark 2 - Debug Info Reader, Internal Header.
+ *
+ * Copyright (c) 2006 knut st. osmundsen <bird-src-spam@anduin.net.de>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with This program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef __internal_DBGInternal_h__
+#define __internal_DBGInternal_h__
+
+#include "dbg.h"
+
+__BEGIN_DECLS
+
+
+/**
+ * The debug module method table.
+ */
+typedef struct RTDBGMODOPS
+{
+    /** The name of the reader. */
+    const char *pszName;
+
+    /**
+     * Closes the module.
+     *
+     * This should free all resources associated with the module
+     * except the pMod which is freed by the caller.
+     *
+     * @returns IPRT status code.
+     * @param   pMod        The module.
+     */
+    DECLCALLBACKMEMBER(int, pfnClose)(PRTDBGMOD pMod);
+
+    /**
+     * Gets a symbol by segment:offset.
+     * This will be approximated to the nearest symbol if there is no exact match.
+     *
+     * @returns IPRT status code.
+     * @param   pMod        The module.
+     * @param   iSegment    The segment this offset is relative to.
+     *                      The -1 segment is special, it means that the addres is relative to
+     *                      the image base. The image base is where the first bit of the image
+     *                      is mapped during load.
+     * @param   off         The offset into the segment.
+     * @param   pSym        Where to store the symbol details.
+     */
+    DECLCALLBACKMEMBER(int, pfnQuerySymbol)(PRTDBGMOD pMod, int32_t iSegment, RTUINTPTR off, PRTDBGSYMBOL pSym);
+
+    /**
+     * Gets a line number entry by segment:offset.
+     * This will be approximated to the nearest line number there is no exact match.
+     *
+     * @returns IPRT status code.
+     * @param   pMod        The module.
+     * @param   iSegment    The segment this offset is relative to.
+     *                      The -1 segment is special, it means that the addres is relative to
+     *                      the image base. The image base is where the first bit of the image
+     *                      is mapped during load.
+     * @param   off         The offset into the segment.
+     * @param   pLine       Where to store the line number details.
+     */
+    DECLCALLBACKMEMBER(int, pfnQueryLine)(PRTDBGMOD pMod, int32_t iSegment, RTUINTPTR uOffset, PRTDBGLINE pLine);
+
+} RTDBGMODOPS;
+/** Pointer to a module method table. */
+typedef RTDBGMODOPS *PRTDBGMODOPS;
+/** Pointer to a const module method table. */
+typedef const RTDBGMODOPS *PCRTDBGMODOPS;
+
+
+/**
+ * Internal representation of a debug module.
+ */
+typedef struct RTDBGMOD
+{
+    /** Magic value (RTDBGMOD_MAGIC). */
+    uint32_t        u32Magic;
+    /** The handle to the module. (If closed, this is NIL_RTFILE.) */
+    RTFILE          File;
+    /** Pointer to the method table. */
+    PCRTDBGMODOPS   pOps;
+} RTDBGMOD;
+
+
+/** The magic value for the debug module structure. (Some english writer) */
+#define RTDBGMOD_MAGIC      0x00000000
+/** The magic value of a dead module structure. */
+#define RTDBGMOD_MAGIC_DEAD 0x00000000
+
+
+int rtDbgModPEOpen(RTFILE File, RTFOFF offHdr, const char *pszModulePath, PRTDBGMOD *ppDbgMod);
+
+
+__END_DECLS
+#endif
+
Index: trunk/kProfile/Makefile
===================================================================
--- trunk/kProfile/Makefile	(revision 3524)
+++ trunk/kProfile/Makefile	(revision 3524)
@@ -0,0 +1,167 @@
+# $Id: $
+## @file
+#
+# kProfiler Mark 2
+#
+# Copyright (c) 2006 knut st. osmundsen <bird-src-spam@anduin.net.de>
+#
+#
+# This file is part of kLIBC.
+#
+# kLIBC is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# kLIBC is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with kLIBC; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+#
+#
+
+
+DEPTH = ../../../..
+include $(PATH_KBUILD)/header.kmk
+
+#LIBRARIES   = kPrf2GC kPrf2R0
+DLLS        = kPrf2
+PROGRAMS    = kPrf2Read tst
+
+
+#
+# Our template.
+#
+TEMPLATE_kPrf2 = kProfiler Template
+TEMPLATE_kPrf2_TOOL             = GCC3
+TEMPLATE_kPrf2_TOOL.os2         = GCC3OMF
+TEMPLATE_kPrf2_TOOL.win32       = VCC70
+TEMPLATE_kPrf2_ASTOOL           = NASM
+
+TEMPLATE_kPrf2_DEFS.freebsd     = __FREEBSD__
+TEMPLATE_kPrf2_DEFS.l4          = __L4__
+TEMPLATE_kPrf2_DEFS.linux       = __LINUX__
+TEMPLATE_kPrf2_DEFS.os2         = __OS2__
+TEMPLATE_kPrf2_DEFS.win32       = __WIN32__
+TEMPLATE_kPrf2_DEFS.win64       = __WIN64__
+
+TEMPLATE_kPrf2_CXXFLAGS.freebsd = -g
+TEMPLATE_kPrf2_CXXFLAGS.l4      = -g
+TEMPLATE_kPrf2_CXXFLAGS.linux   = -g
+TEMPLATE_kPrf2_CXXFLAGS.os2     = -g
+TEMPLATE_kPrf2_CXXFLAGS.win32   = -Zi -Zl -MD -W3 -GF -GR-
+ifneq ($(BUILD_TYPE),debug)
+TEMPLATE_kPrf2_CXXFLAGS.freebsd+= -O3
+TEMPLATE_kPrf2_CXXFLAGS.l4     += -O3
+TEMPLATE_kPrf2_CXXFLAGS.linux  += -O3
+TEMPLATE_kPrf2_CXXFLAGS.os2    += -O3
+TEMPLATE_kPrf2_CXXFLAGS.win32  += -O2xtg -Oi -Ob2
+endif
+TEMPLATE_kPrf2_CXXFLAGS.win64   = $(TEMPLATE_kPrf2_CXXFLAGS.win32)
+
+TEMPLATE_kPrf2_ASFLAGS.freebsd  = -f elf32 -g
+TEMPLATE_kPrf2_ASFLAGS.l4       = -f elf32 -g
+TEMPLATE_kPrf2_ASFLAGS.linux    = -f elf32 -g
+TEMPLATE_kPrf2_ASFLAGS.os2      = -f omf
+TEMPLATE_kPrf2_ASFLAGS.win32    = -f win32 -g
+
+TEMPLATE_kPrf2_INCS.win32  	    = \
+	$(PATH_DEV)/x86.win32/vcc70/include \
+	$(PATH_DEV)/x86.win32/sdk200209/include
+
+TEMPLATE_kPrf2_LDFLAGS.freebsd  = -g
+TEMPLATE_kPrf2_LDFLAGS.l4       = -g
+TEMPLATE_kPrf2_LDFLAGS.linux    = -g
+TEMPLATE_kPrf2_LDFLAGS.os2      = -g
+TEMPLATE_kPrf2_LDFLAGS.win32    = /DEBUG
+TEMPLATE_kPrf2_LDFLAGS.win64    = /DEBUG
+
+TEMPLATE_kPrf2_LIBS.freebsd     =
+TEMPLATE_kPrf2_LIBS.l4          =
+TEMPLATE_kPrf2_LIBS.linux       =
+TEMPLATE_kPrf2_LIBS.os2         =
+TEMPLATE_kPrf2_LIBS.win32       = \
+	$(PATH_TOOL_VCC70_LIB)/msvcrt.lib \
+	$(PATH_TOOL_VCC70_LIB)/msvcprt.lib \
+	$(PATH_TOOL_VCC70_LIB)/oldnames.lib \
+	$(PATH_DEV)/x86.win32/sdk200209/lib/Kernel32.Lib \
+	$(PATH_DEV)/x86.win32/sdk200209/lib/psapi.Lib
+
+
+#
+# kPrf2 - The profiler module.
+#
+kPrf2_TEMPLATE      = kPrf2
+kPrf2_DEFS.x86      = KPRF_BITS=32
+kPrf2_DEFS.amd64    = KPRF_BITS=64
+
+kPrf2_SOURCES       = \
+	kProfileR3.cpp
+#	kProfileGC.cpp
+#	kProfileR0.cpp
+
+kPrf2_SOURCES.win32 = \
+	dllmain-win32.cpp \
+	kPrf2-win32.def \
+	prfx86msc.asm
+prfx86msc.asm_DEFS.win32 = \
+	KPRF_ENTER=_KPrfEnter \
+	KPRF_LEAVE=_KPrfLeave
+
+
+
+#
+# kPrf2Read - The read & producer of statistics.
+#
+kPrf2Read_TEMPLATE = kPrf2
+kPrf2Read_INCS = \
+	$(PATH_CURRENT)/../include \
+	$(PATH_ROOT)/include
+kPrf2Read_DEFS = IN_RING3 IN_RT_R3
+kPrf2Read_SOURCES  = \
+	kPrf2Read.cpp \
+	dbgmodule.cpp \
+	dbgline.cpp \
+	dbgsymbol.cpp
+ifeq ($(filter-out win32 win64,$(BUILD_TARGET)),)
+kPrf2Read_SOURCES += dbgModPE-win32.cpp
+else
+kPrf2Read_SOURCES += dbgModPE-generic.cpp
+endif
+kPrf2Read_LIBS  = \
+	$(LIB_RUNTIME)
+kPrf2Read_LIBS.win32 = \
+	$(PATH_DEV)/x86.win32/sdk200209/lib/uuid.Lib \
+	$(PATH_DEV)/x86.win32/sdk200209/lib/Version.Lib
+
+
+
+tst_TOOL = VCC70
+tst_CFLAGS = -GH -Gh -Zi -Zl -GR- -GX- -GF- -MD -W3 -wd4244
+tst_SOURCES = tst.c
+tst.c_CFLAGS = -Od
+tst_LDFLAGS = /DEBUG
+tst_LIBS = \
+	$(PATH_TOOL_VCC70_LIB)/msvcrt.lib \
+	$(PATH_TOOL_VCC70_LIB)/msvcprt.lib \
+	$(PATH_TOOL_VCC70_LIB)/oldnames.lib \
+	$(PATH_DEV)/x86.win32/sdk200209/lib/Kernel32.Lib \
+	$(PATH_kPrf2)/kPrf2.lib
+
+# Generate the rules
+include $(PATH_KBUILD)/footer.kmk
+
+
+#
+# Aliases for .cpp.h files so we can more easily do syntax checking from the editor.
+#
+CORE := $(wildcard *core*.cpp.h *core*.h.h)
+$(CORE:.h=.o) $(CORE:.h=.obj) : kProfileR3.o
+
+READ := $(wildcard *read*.cpp.h *read*.h.h)
+$(READ:.h=.o) $(READ:.h=.obj) : kPrf2Read.o
+
Index: trunk/kProfile/dbg.h
===================================================================
--- trunk/kProfile/dbg.h	(revision 3524)
+++ trunk/kProfile/dbg.h	(revision 3524)
@@ -0,0 +1,262 @@
+/* $Id: $ */
+/** @file
+ *
+ * kProfile Mark 2 - Debug Info Reader.
+ *
+ * Copyright (c) 2006 knut st. osmundsen <bird-src-spam@anduin.net.de>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with This program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+
+#ifndef __iprt_dbg_h__
+#define __iprt_dbg_h__
+
+#include <iprt/types.h>
+
+__BEGIN_DECLS
+
+/** @defgroup grp_rt_dbg    Debug Info Reader
+ * @addtogroup grp_rt
+ * @{
+ */
+
+/** The max filename path length used by the debug reader. */
+#define RTDBG_PATH_MAX      260
+
+/** The max symbol name length used by the debug reader. */
+#define RTDBG_SYMBOL_MAX    384
+
+
+/** @name   Special Segments
+ * @{ */
+/** Relative Virtual Address.
+ * The specified offset is relative to the image base. The image base is the lowest memory
+ * address used by the image when loaded with the address assignments indicated in the image. */
+#define RTDBGSEG_RVA (-1)
+/** Absolute segment. The offset isn't relative to anything. */
+#define RTDBGSEG_ABS (-2)
+/** @} */
+
+
+/**
+ * Line number details.
+ */
+typedef struct RTDBGLINE
+{
+    /** The relative virtual address. */
+    RTUINTPTR   RVA;
+    /** The offset into the segment. */
+    RTUINTPTR   offSegment;
+    /** The segment number. */
+    int32_t     iSegment;
+    /** The Line number. */
+    uint32_t    iLine;
+    /** The length of the filename. */
+    uint16_t    cchFile;
+    /** The name of the file this line number relates to. */
+    char        szFile[RTDBG_PATH_MAX];
+} RTDBGLINE;
+/** Pointer to line number details. */
+typedef RTDBGLINE *PRTDBGLINE;
+/** Pointer to const line number details. */
+typedef const RTDBGLINE *PCRTDBGLINE;
+/** Pointer to a pointer to line number details. */
+typedef PRTDBGLINE *PPRTDBGLINE;
+
+/**
+ * Duplicates a line number.
+ *
+ * To save heap space, the returned line number will not own more heap space
+ * than it strictly need to. So, it's not possible to append stuff to the symbol
+ * or anything of that kind.
+ *
+ * @returns Pointer to the duplicate.
+ *          This must be freed using RTDbgSymbolFree().
+ * @param   pLine       The line number to be duplicated.
+ */
+RTDECL(PRTDBGLINE) RTDbgLineDup(PCRTDBGLINE pLine);
+
+/**
+ * Frees a line number obtained from the RTDbg API.
+ *
+ * @returns VINF_SUCCESS on success.
+ * @returns VERR_INVALID_POINTER if a NULL pointer or an !VALID_PTR() is passed in.
+ *
+ * @param   pLine       The line number to be freed.
+ */
+RTDECL(int) RTDbgLineFree(PRTDBGLINE pLine);
+
+
+/** @name Symbol Flags.
+ * @{ */
+/** The symbol is weak. */
+#define RTDBGSYM_FLAGS_WEAK     BIT(0)
+/** The symbol is absolute.
+ * (This also indicated by the segment number.) */
+#define RTDBGSYM_FLAGS_ABS      BIT(1)
+/** The symbol is exported. */
+#define RTDBGSYM_FLAGS_EXPORTED BIT(2)
+/** The symbol is a function/method/procedure/whatever-executable-code. */
+#define RTDBGSYM_FLAGS_CODE     BIT(3)
+/** The symbol is some kind of data. */
+#define RTDBGSYM_FLAGS_DATA     BIT(4)
+/** @} */
+
+/**
+ * Symbol details.
+ */
+typedef struct RTDBGSYMBOL
+{
+    /** The relative virtual address. */
+    RTUINTPTR   RVA;
+    /** The symbol size.
+     * This is not a reliable field, it could be a bad guess. Ignore if zero. */
+    RTUINTPTR   cb;
+    /** The offset into the segment. */
+    RTUINTPTR   offSegment;
+    /** The segment number. */
+    int32_t     iSegment;
+    /** The symbol flags. */
+    uint32_t    fFlags;
+/** @todo type info? */
+    /** The length of the symbol name. */
+    uint16_t    cchName;
+    /** The symbol name. */
+    char        szName[RTDBG_SYMBOL_MAX];
+} RTDBGSYMBOL;
+/** Pointer to symbol details. */
+typedef RTDBGSYMBOL *PRTDBGSYMBOL;
+/** Pointer to const symbol details. */
+typedef const RTDBGSYMBOL *PCRTDBGSYMBOL;
+/** Pointer to a pointer to symbol details. */
+typedef PRTDBGSYMBOL *PPRTDBGSYMBOL;
+
+
+/**
+ * Duplicates a symbol.
+ *
+ * To save heap space, the returned symbol will not own more heap space than
+ * it strictly need to. So, it's not possible to append stuff to the symbol
+ * or anything of that kind.
+ *
+ * @returns Pointer to the duplicate.
+ *          This must be freed using RTDbgSymbolFree().
+ * @param   pSymbol     The symbol to be freed.
+ */
+RTDECL(PRTDBGSYMBOL) RTDbgSymbolDup(PCRTDBGSYMBOL pSymbol);
+
+/**
+ * Frees a symbol obtained from the RTDbg API.
+ *
+ * @returns VINF_SUCCESS on success.
+ * @returns VERR_INVALID_POINTER if a NULL pointer or an !VALID_PTR() is passed in.
+ *
+ * @param   pSymbol     The symbol to be freed.
+ */
+RTDECL(int) RTDbgSymbolFree(PRTDBGSYMBOL pSymbol);
+
+
+
+/** A debug module handle. */
+typedef struct RTDBGMOD *PRTDBGMOD;
+
+/**
+ * Opens the debug info for a specified executable module.
+ *
+ * @returns IPRT status code.
+ * @param   pszModulePath       The path to the executable module.
+ * @param   ppDbgMod            Where to store the debug module handle.
+ */
+RTDECL(int) RTDbgModuleOpen(const char *pszModulePath, PRTDBGMOD *ppDbgMod);
+
+/**
+ * Closes the module.
+ *
+ * @returns IPRT status code.
+ * @param   pMod        The module handle.
+ */
+RTDECL(int) RTDbgModuleClose(PRTDBGMOD pMod);
+
+/**
+ * Gets a symbol by segment:offset.
+ * This will be approximated to the nearest symbol if there is no exact match.
+ *
+ * @returns IPRT status code.
+ * @param   pMod        The module.
+ * @param   iSegment    The segment this offset is relative to.
+ *                      The -1 segment is special, it means that the addres is relative to
+ *                      the image base. The image base is where the first bit of the image
+ *                      is mapped during load.
+ * @param   off         The offset into the segment.
+ * @param   pSym        Where to store the symbol details.
+ */
+RTDECL(int) RTDbgModuleQuerySymbol(PRTDBGMOD pMod, int32_t iSegment, RTUINTPTR off, PRTDBGSYMBOL pSym);
+
+/**
+ * Gets & allocates a symbol by segment:offset.
+ * This will be approximated to the nearest symbol if there is no exact match.
+ *
+ * @returns IPRT status code.
+ * @param   pMod        The module.
+ * @param   iSegment    The segment this offset is relative to.
+ *                      The -1 segment is special, it means that the addres is relative to
+ *                      the image base. The image base is where the first bit of the image
+ *                      is mapped during load.
+ * @param   off         The offset into the segment.
+ * @param   ppSym       Where to store the pointer to the symbol info.
+ *                      Free the returned symbol using RTDbgSymbolFree().
+ */
+RTDECL(int) RTDbgModuleQuerySymbolA(PRTDBGMOD pMod, int32_t iSegment, RTUINTPTR off, PPRTDBGSYMBOL ppSym);
+
+/**
+ * Gets a line number entry by segment:offset.
+ * This will be approximated to the nearest line number there is no exact match.
+ *
+ * @returns IPRT status code.
+ * @param   pMod        The module.
+ * @param   iSegment    The segment this offset is relative to.
+ *                      The -1 segment is special, it means that the addres is relative to
+ *                      the image base. The image base is where the first bit of the image
+ *                      is mapped during load.
+ * @param   off         The offset into the segment.
+ * @param   pLine       Where to store the line number details.
+ */
+RTDECL(int) RTDbgModuleQueryLine(PRTDBGMOD pMod, int32_t iSegment, RTUINTPTR off, PRTDBGLINE pLine);
+
+/**
+ * Gets & allocates a line number entry by segment:offset.
+ * This will be approximated to the nearest line number there is no exact match.
+ *
+ * @returns IPRT status code.
+ * @param   pMod        The module.
+ * @param   iSegment    The segment this offset is relative to.
+ *                      The -1 segment is special, it means that the addres is relative to
+ *                      the image base. The image base is where the first bit of the image
+ *                      is mapped during load.
+ * @param   off         The offset into the segment.
+ * @param   ppLine      Where to store the pointer to the line number info.
+ *                      Free the returned line number using RTDbgLineFree().
+ */
+RTDECL(int) RTDbgModuleQueryLineA(PRTDBGMOD pMod, int32_t iSegment, RTUINTPTR off, PPRTDBGLINE ppLine);
+
+
+__END_DECLS
+
+/** @} */
+
+#endif
Index: trunk/kProfile/dbgModPE-generic.cpp
===================================================================
--- trunk/kProfile/dbgModPE-generic.cpp	(revision 3524)
+++ trunk/kProfile/dbgModPE-generic.cpp	(revision 3524)
@@ -0,0 +1,386 @@
+/* $Id: $ */
+/** @file
+ *
+ * kProfile Mark 2 - Debug Info Reader, DbgHelp Base Reader.
+ *
+ * Copyright (c) 2006 knut st. osmundsen <bird-src-spam@anduin.net.de>
+ *
+ *
+ * This file is part of kLIBC.
+ *
+ * kLIBC is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * kLIBC is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with kLIBC; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+
+/*******************************************************************************
+*   Header Files                                                               *
+*******************************************************************************/
+#include <iprt/file.h>
+#include <iprt/string.h>
+#include <iprt/alloc.h>
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#include <iprt/err.h>
+#include <iprt/thread.h>
+#include <iprt/alloca.h>
+#include "DBGInternal.h"
+#include "internal/ldrPE.h"
+
+
+
+/*******************************************************************************
+*   Structures and Typedefs                                                    *
+*******************************************************************************/
+/**
+ * A dbghelp based PE debug reader.
+ */
+typedef struct RTDBGMODPE
+{
+    /** The common module core. */
+    RTDBGMOD    Core;
+    /** The image size. */
+    uint32_t    cbImage;
+    /** The number of sections. (We've added the implicit header section.) */
+    int32_t     cSections;
+    /** The section headers (variable size). The first section is the
+     * implicit header section.*/
+    IMAGE_SECTION_HEADER    aSections[1];
+} RTDBGMODPE, *PRTDBGMODPE;
+
+
+/**
+ * Calcs the RVA for a segment:offset address.
+ *
+ * @returns IPRT status code.
+ *
+ * @param   pModPe      The PE debug module instance.
+ * @param   iSegment    The segment number. Special segments are dealt with as well.
+ * @param   off         The segment offset.
+ * @param   puRVA       Where to store the RVA on success.
+ */
+static int rtDbgModPeSegOffToRVA(PRTDBGMODPE pModPe, int32_t iSegment, RTUINTPTR off, uint32_t *puRVA)
+{
+    if (iSegment >= 0)
+    {
+        AssertMsgReturn(iSegment < pModPe->cSections, ("iSegment=%RX32 cSections=%RX32\n", iSegment, pModPe->cSections),
+                        VERR_DBGMOD_INVALID_ADDRESS);
+        AssertMsgReturn(off < pModPe->aSections[iSegment].Misc.VirtualSize,
+                        ("off=%RTptr VirtualSize=%RX32\n", off, pModPe->aSections[iSegment].Misc.VirtualSize),
+                        VERR_DBGMOD_INVALID_ADDRESS);
+        *puRVA = pModPe->aSections[iSegment].VirtualAddress + off;
+        return VINF_SUCCESS;
+    }
+
+    if (iSegment == RTDBGSEG_RVA)
+    {
+        AssertMsgReturn(off < pModPe->cbImage, ("off=%RTptr, cbImage=%RX32\n", off, pModPe->cbImage),
+                        VERR_DBGMOD_INVALID_ADDRESS);
+        *puRVA = off;
+        return VINF_SUCCESS;
+    }
+    AssertMsgFailedReturn(("iSegment=%RI32\n", iSegment), VERR_DBGMOD_INVALID_ADDRESS);
+}
+
+
+/**
+ * Calcs the segment:offset address for a RVA.
+ *
+ * @returns IPRT status code.
+ *
+ * @param   pModPe      The PE debug module instance.
+ * @param   uRVA        The RVA.
+ * @param   piSegment   Where to store the segment number.
+ * @param   poff        Where to store the segment offset.
+ */
+static int rtDbgModPeRVAToSegOff(PRTDBGMODPE pModPe, uint32_t uRVA, int32_t *piSegment, RTUINTPTR *poff)
+{
+    AssertMsgReturn(uRVA < pModPe->cbImage, ("uRVA=%RX32, cbImage=%RX32\n", uRVA, pModPe->cbImage),
+                    VERR_DBGMOD_INVALID_ADDRESS);
+    for (int32_t iSegment = 0; iSegment < pModPe->cSections; iSegment++)
+    {
+        /** @todo should probably be less strict about address in the alignment gaps. */
+        uint32_t off = uRVA - pModPe->aSections[iSegment].VirtualAddress;
+        if (off < pModPe->aSections[iSegment].Misc.VirtualSize)
+        {
+            *poff = off;
+            *piSegment = iSegment;
+            return VINF_SUCCESS;
+        }
+    }
+    AssertMsgFailedReturn(("uRVA=%RX32\n", uRVA), VERR_DBGMOD_INVALID_ADDRESS);
+}
+
+
+/**
+ * @copydoc RTDBGMODOPS::pfnQueryLine
+ */
+static DECLCALLBACK(int) rtDbgModPEQueryLine(PRTDBGMOD pMod, int32_t iSegment, RTUINTPTR off, PRTDBGLINE pLine)
+{
+    PRTDBGMODPE pModPe = (PRTDBGMODPE)pMod;
+
+    /*
+     * Translate the address to an RVA.
+     */
+    uint32_t uRVA;
+    int rc = rtDbgModPeSegOffToRVA(pModPe, iSegment, off, &uRVA);
+    if (RT_SUCCESS(rc))
+    {
+#if 0
+        DWORD64 off;
+        IMAGEHLP_LINE64 Line;
+        Line.SizeOfStruct = sizeof(Line);
+        if (g_pfnSymGetLineFromAddr64(pModPe->hSymInst, pModPe->ImageBase + uRVA, &off, &Line))
+        {
+            pLine->RVA = (RTUINTPTR)(Line.Address - pModPe->ImageBase);
+            rc = rtDbgModPeRVAToSegOff(pModPe, pLine->RVA, &pLine->iSegment, &pLine->offSegment);
+            pLine->iLine = Line.LineNumber;
+            pLine->cchFile = strlen(Line.FileName);
+            if (pLine->cchFile >= sizeof(pLine->szFile))
+                pLine->cchFile = sizeof(pLine->szFile) - 1;
+            memcpy(pLine->szFile, Line.FileName, pLine->cchFile + 1);
+        }
+        else
+        {
+            DWORD Err = GetLastError();
+            rc = RTErrConvertFromWin32(Err);
+        }
+#endif
+        rc = VERR_NOT_IMPLEMENTED;
+    }
+    return rc;
+}
+
+
+/**
+ * @copydoc RTDBGMODOPS::pfnQuerySymbol
+ */
+static DECLCALLBACK(int) rtDbgModPEQuerySymbol(PRTDBGMOD pMod, int32_t iSegment, RTUINTPTR off, PRTDBGSYMBOL pSym)
+{
+    PRTDBGMODPE pModPe = (PRTDBGMODPE)pMod;
+
+    /*
+     * Translate the address to an RVA.
+     */
+    uint32_t uRVA;
+    int rc = rtDbgModPeSegOffToRVA(pModPe, iSegment, off, &uRVA);
+    if (RT_SUCCESS(rc))
+    {
+#if 0
+        DWORD64 off;
+        union
+        {
+            SYMBOL_INFO Sym;
+            char        achBuffer[sizeof(SYMBOL_INFO) + RTDBG_SYMBOL_MAX];
+        } Buf;
+        Buf.Sym.SizeOfStruct = sizeof(SYMBOL_INFO);
+        Buf.Sym.MaxNameLen = RTDBG_SYMBOL_MAX;
+        if (g_pfnSymFromAddr(pModPe->hSymInst, pModPe->ImageBase + uRVA, &off, &Buf.Sym))
+        {
+            pSym->cb     = Buf.Sym.Size;
+            pSym->fFlags = 0;
+            if (Buf.Sym.Flags & SYMFLAG_FUNCTION)
+                pSym->fFlags |= RTDBGSYM_FLAGS_CODE;
+            else if (Buf.Sym.Flags & SYMFLAG_CONSTANT)
+                pSym->fFlags |= RTDBGSYM_FLAGS_ABS; /** @todo SYMFLAG_CONSTANT must be tested - documentation is too brief to say what is really meant here.*/
+            else
+                pSym->fFlags |= RTDBGSYM_FLAGS_DATA;
+            if (Buf.Sym.Flags & SYMFLAG_EXPORT)
+                pSym->fFlags |= RTDBGSYM_FLAGS_EXPORTED;
+            if ((Buf.Sym.Flags & (SYMFLAG_VALUEPRESENT | SYMFLAG_CONSTANT)) == (SYMFLAG_VALUEPRESENT | SYMFLAG_CONSTANT))
+            {
+                pSym->iSegment   = RTDBGSEG_ABS;
+                pSym->offSegment = (RTUINTPTR)Buf.Sym.Value;
+                pSym->RVA        = (RTUINTPTR)Buf.Sym.Value;
+            }
+            else
+            {
+                pSym->RVA        = (RTUINTPTR)(Buf.Sym.Address - pModPe->ImageBase);
+                rc = rtDbgModPeRVAToSegOff(pModPe, pSym->RVA, &pSym->iSegment, &pSym->offSegment);
+            }
+            pSym->cchName = (uint16_t)Buf.Sym.NameLen;
+            if (pSym->cchName >= sizeof(pSym->szName))
+                pSym->cchName = sizeof(pSym->szName) - 1;
+            memcpy(pSym->szName, Buf.Sym.Name, Buf.Sym.NameLen);
+            pSym->szName[Buf.Sym.NameLen] = '\0';
+        }
+        else
+        {
+            DWORD Err = GetLastError();
+            rc = RTErrConvertFromWin32(Err);
+        }
+#endif
+        rc = VERR_NOT_IMPLEMENTED;
+    }
+    return rc;
+}
+
+
+/**
+ * @copydoc RTDBGMODOPS::pfnClose
+ */
+static DECLCALLBACK(int) rtDbgModPEClose(PRTDBGMOD pMod)
+{
+    PRTDBGMODPE pModPe = (PRTDBGMODPE)pMod;
+
+    //if (g_pfnSymCleanup(pModPe->hSymInst))
+    //    return VINF_SUCCESS;
+    //
+    //DWORD Err = GetLastError();
+    //int rc = RTErrConvertFromWin32(Err);
+    //AssertMsgFailed(("SymInitialize failed: Err=%d rc=%Rrc\n", Err, rc));
+    //return rc;
+    return VERR_NOT_IMPLEMENTED;
+}
+
+
+/**
+ * Methods for a PE module.
+ */
+static const RTDBGMODOPS g_rtDbgModPEOps =
+{
+    "PE (dbghelp)",
+    rtDbgModPEClose,
+    rtDbgModPEQuerySymbol,
+    rtDbgModPEQueryLine
+};
+
+
+/**
+ * Opens the debug info for a PE image using the windows dbghelp library.
+ *
+ * @returns IPRT status code.
+ *
+ * @param   File                The handle to the module.
+ * @param   offHdr              The offset of the PE header.
+ * @param   pszModulePath       The path to the module.
+ * @param   ppDbgMod            Where to store the module handle.
+ *
+ */
+int rtDbgModPEOpen(RTFILE File, RTFOFF offHdr, const char *pszModulePath, PRTDBGMOD *ppDbgMod)
+{
+    /*
+     * We need to read the section headers and get the image size.
+     */
+    IMAGE_FILE_HEADER FHdr;
+    int rc = RTFileReadAt(File, offHdr + OFFSETOF(IMAGE_NT_HEADERS32, FileHeader), &FHdr, sizeof(FHdr), NULL);
+    AssertRCReturn(rc, rc);
+
+    uint32_t cbImage;
+    if (FHdr.SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER32))
+        rc = RTFileReadAt(File, offHdr + OFFSETOF(IMAGE_NT_HEADERS32, OptionalHeader.SizeOfImage),
+                          &cbImage, sizeof(cbImage), NULL);
+    else if (FHdr.SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER64))
+        rc = RTFileReadAt(File, offHdr + OFFSETOF(IMAGE_NT_HEADERS64, OptionalHeader.SizeOfImage),
+                          &cbImage, sizeof(cbImage), NULL);
+    else
+        AssertFailedReturn(VERR_BAD_EXE_FORMAT);
+    AssertRCReturn(rc, rc);
+
+    /*
+     * Allocate the module and read/construct the section headers.
+     */
+    PRTDBGMODPE pModPe = (PRTDBGMODPE)RTMemAlloc(OFFSETOF(RTDBGMODPE, aSections[FHdr.NumberOfSections + 2]));
+    AssertReturn(pModPe, VERR_NO_MEMORY);
+    pModPe->Core.u32Magic   = RTDBGMOD_MAGIC;
+    pModPe->Core.pOps       = &g_rtDbgModPEOps;
+    pModPe->Core.File       = File;
+    pModPe->cbImage         = cbImage;
+    pModPe->cSections       = 1 + FHdr.NumberOfSections;
+    rc = RTFileReadAt(File, offHdr + OFFSETOF(IMAGE_NT_HEADERS32, OptionalHeader) + FHdr.SizeOfOptionalHeader,
+                      &pModPe->aSections[1], sizeof(pModPe->aSections[0]) * FHdr.NumberOfSections, NULL);
+    if (RT_SUCCESS(rc))
+    {
+        PIMAGE_SECTION_HEADER pSH = &pModPe->aSections[0];
+        memcpy(pSH->Name, "headers", sizeof(pSH->Name));
+        pSH->Misc.VirtualSize       = pModPe->aSections[1].VirtualAddress;
+        pSH->VirtualAddress         = 0;
+        pSH->SizeOfRawData          = pSH->Misc.VirtualSize;
+        pSH->PointerToRawData       = 0;
+        pSH->PointerToRelocations   = 0;
+        pSH->PointerToLinenumbers   = 0;
+        pSH->NumberOfRelocations    = 0;
+        pSH->NumberOfLinenumbers    = 0;
+        pSH->Characteristics        = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_MEM_READ;
+
+        uint32_t uTheEnd = pModPe->aSections[FHdr.NumberOfSections].VirtualAddress
+                         + pModPe->aSections[FHdr.NumberOfSections].Misc.VirtualSize;
+        if (uTheEnd < cbImage)
+        {
+            pSH = &pModPe->aSections[pModPe->cSections++];
+            memcpy(pSH->Name, "tail\0\0\0", sizeof(pSH->Name));
+            pSH->Misc.VirtualSize       = cbImage - uTheEnd;
+            pSH->VirtualAddress         = uTheEnd;
+            pSH->SizeOfRawData          = pSH->Misc.VirtualSize;
+            pSH->PointerToRawData       = 0;
+            pSH->PointerToRelocations   = 0;
+            pSH->PointerToLinenumbers   = 0;
+            pSH->NumberOfRelocations    = 0;
+            pSH->NumberOfLinenumbers    = 0;
+            pSH->Characteristics        = IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_ALIGN_1BYTES | IMAGE_SCN_MEM_READ;
+        }
+
+#if 0
+        /*
+         * Find a new dbghelp handle.
+         *
+         * We assume 4GB of handles outlast most debugging sessions, or in anyways that
+         * when we start reusing handles they are no longer in use. :-)
+         */
+        static volatile uint32_t s_u32LastHandle = 1;
+        HANDLE hSymInst = (HANDLE)ASMAtomicIncU32(&s_u32LastHandle);
+        while (     hSymInst == INVALID_HANDLE_VALUE
+               ||   hSymInst == (HANDLE)0
+               ||   hSymInst == GetCurrentProcess())
+            hSymInst = (HANDLE)ASMAtomicIncU32(&s_u32LastHandle);
+
+        /*
+         * Initialize dbghelp and try open the specified module.
+         */
+        if (g_pfnSymInitialize(hSymInst, NULL, FALSE))
+        {
+            g_pfnSymSetOptions(SYMOPT_LOAD_LINES | SYMOPT_AUTO_PUBLICS | SYMOPT_ALLOW_ABSOLUTE_SYMBOLS);
+
+            RTFileSeek(File, 0, RTFILE_SEEK_BEGIN, NULL); /* don't know if this is required or not... */
+            DWORD64 ImageBase = g_pfnSymLoadModule64(hSymInst, (HANDLE)File, pszModulePath, NULL, 0x00400000, 0);
+            if (ImageBase)
+            {
+                pModPe->hSymInst    = hSymInst;
+                pModPe->ImageBase   = ImageBase;
+                *ppDbgMod = &pModPe->Core;
+                return rc;
+            }
+
+            DWORD Err = GetLastError();
+            rc = RTErrConvertFromWin32(Err);
+            AssertMsgFailed(("SymLoadModule64 failed: Err=%d rc=%Rrc\n", Err, rc));
+            g_pfnSymCleanup(hSymInst);
+        }
+        else
+        {
+            DWORD Err = GetLastError();
+            rc = RTErrConvertFromWin32(Err);
+            AssertMsgFailed(("SymInitialize failed: Err=%d rc=%Rrc\n", Err, rc));
+        }
+#endif
+        rc = VERR_NOT_IMPLEMENTED;
+    }
+    else
+        AssertRC(rc);
+
+    RTMemFree(pModPe);
+    return rc;
+}
+
Index: trunk/kProfile/dbgModPE-win32.cpp
===================================================================
--- trunk/kProfile/dbgModPE-win32.cpp	(revision 3524)
+++ trunk/kProfile/dbgModPE-win32.cpp	(revision 3524)
@@ -0,0 +1,675 @@
+/* $Id: $ */
+/** @file
+ *
+ * kProfile Mark 2 - Debug Info Reader, DbgHelp Base Reader.
+ *
+ * Copyright (c) 2006 knut st. osmundsen <bird-src-spam@anduin.net.de>
+ *
+ *
+ * This file is part of kLIBC.
+ *
+ * kLIBC is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * kLIBC is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with kLIBC; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+
+/*******************************************************************************
+*   Header Files                                                               *
+*******************************************************************************/
+#include <Windows.h>
+#define _IMAGEHLP64
+#include <DbgHelp.h>
+
+#include <iprt/file.h>
+#include <iprt/string.h>
+#include <iprt/alloc.h>
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#include <iprt/err.h>
+#include <iprt/thread.h>
+#include <iprt/alloca.h>
+#include "DBGInternal.h"
+
+
+//#include "internal/ldrPE.h"
+
+/*******************************************************************************
+*   Global Variables                                                           *
+*******************************************************************************/
+/** The dbghelp.dll module handle. */
+static HMODULE g_hDbgHelp = NULL;
+/** Pointer to the dbhelp.dll SymInitialize function. */
+static BOOL (WINAPI *g_pfnSymInitialize)(IN HANDLE,IN LPSTR,IN BOOL);
+/** Pointer to the dbhelp.dll SymCleanup function. */
+static BOOL (WINAPI *g_pfnSymCleanup)(IN HANDLE);
+/** Pointer to the dbhelp.dll SymSetOptions function. */
+static DWORD (WINAPI *g_pfnSymSetOptions)(IN DWORD);
+/** Pointer to the dbhelp.dll SymLoadModule64 function. */
+static DWORD64 (WINAPI *g_pfnSymLoadModule64)(IN HANDLE, IN HANDLE, IN PCSTR, IN PCSTR ModuleName, IN DWORD64, IN DWORD);
+/** Pointer to the dbhelp.dll SymFromAddr function. */
+static DWORD (WINAPI *g_pfnSymFromAddr)(IN HANDLE, IN DWORD64, OUT PDWORD64, OUT PSYMBOL_INFO);
+/** Pointer to the dbhelp.dll SymGetLineFromAddr64 function. */
+static DWORD (WINAPI *g_pfnSymGetLineFromAddr64)(IN HANDLE, IN DWORD64, OUT PDWORD64, OUT PIMAGEHLP_LINE64);
+
+
+
+/*******************************************************************************
+*   Structures and Typedefs                                                    *
+*******************************************************************************/
+/**
+ * A dbghelp based PE debug reader.
+ */
+typedef struct RTDBGMODPE
+{
+    /** The common module core. */
+    RTDBGMOD    Core;
+    /** The image base. */
+    DWORD64     ImageBase;
+    /** The "process" handle we present dbghelp. */
+    HANDLE      hSymInst;
+    /** The image size. */
+    uint32_t    cbImage;
+    /** The number of sections. (We've added the implicit header section.) */
+    int32_t     cSections;
+    /** The section headers (variable size). The first section is the
+     * implicit header section.*/
+    IMAGE_SECTION_HEADER    aSections[1];
+} RTDBGMODPE, *PRTDBGMODPE;
+
+
+/**
+ * Calcs the RVA for a segment:offset address.
+ *
+ * @returns IPRT status code.
+ *
+ * @param   pModPe      The PE debug module instance.
+ * @param   iSegment    The segment number. Special segments are dealt with as well.
+ * @param   off         The segment offset.
+ * @param   puRVA       Where to store the RVA on success.
+ */
+static int rtDbgModPeSegOffToRVA(PRTDBGMODPE pModPe, int32_t iSegment, RTUINTPTR off, uint32_t *puRVA)
+{
+    if (iSegment >= 0)
+    {
+        AssertMsgReturn(iSegment < pModPe->cSections, ("iSegment=%RX32 cSections=%RX32\n", iSegment, pModPe->cSections),
+                        VERR_DBGMOD_INVALID_ADDRESS);
+        AssertMsgReturn(off < pModPe->aSections[iSegment].Misc.VirtualSize,
+                        ("off=%RTptr VirtualSize=%RX32\n", off, pModPe->aSections[iSegment].Misc.VirtualSize),
+                        VERR_DBGMOD_INVALID_ADDRESS);
+        *puRVA = pModPe->aSections[iSegment].VirtualAddress + off;
+        return VINF_SUCCESS;
+    }
+
+    if (iSegment == RTDBGSEG_RVA)
+    {
+        AssertMsgReturn(off < pModPe->cbImage, ("off=%RTptr, cbImage=%RX32\n", off, pModPe->cbImage),
+                        VERR_DBGMOD_INVALID_ADDRESS);
+        *puRVA = off;
+        return VINF_SUCCESS;
+    }
+    AssertMsgFailedReturn(("iSegment=%RI32\n", iSegment), VERR_DBGMOD_INVALID_ADDRESS);
+}
+
+
+/**
+ * Calcs the segment:offset address for a RVA.
+ *
+ * @returns IPRT status code.
+ *
+ * @param   pModPe      The PE debug module instance.
+ * @param   uRVA        The RVA.
+ * @param   piSegment   Where to store the segment number.
+ * @param   poff        Where to store the segment offset.
+ */
+static int rtDbgModPeRVAToSegOff(PRTDBGMODPE pModPe, uint32_t uRVA, int32_t *piSegment, RTUINTPTR *poff)
+{
+    AssertMsgReturn(uRVA < pModPe->cbImage, ("uRVA=%RX32, cbImage=%RX32\n", uRVA, pModPe->cbImage),
+                    VERR_DBGMOD_INVALID_ADDRESS);
+    for (int32_t iSegment = 0; iSegment < pModPe->cSections; iSegment++)
+    {
+        /** @todo should probably be less strict about address in the alignment gaps. */
+        uint32_t off = uRVA - pModPe->aSections[iSegment].VirtualAddress;
+        if (off < pModPe->aSections[iSegment].Misc.VirtualSize)
+        {
+            *poff = off;
+            *piSegment = iSegment;
+            return VINF_SUCCESS;
+        }
+    }
+    AssertMsgFailedReturn(("uRVA=%RX32\n", uRVA), VERR_DBGMOD_INVALID_ADDRESS);
+}
+
+
+/**
+ * @copydoc RTDBGMODOPS::pfnQueryLine
+ */
+static DECLCALLBACK(int) rtDbgModPEQueryLine(PRTDBGMOD pMod, int32_t iSegment, RTUINTPTR off, PRTDBGLINE pLine)
+{
+    PRTDBGMODPE pModPe = (PRTDBGMODPE)pMod;
+
+    /*
+     * Translate the address to an RVA.
+     */
+    uint32_t uRVA;
+    int rc = rtDbgModPeSegOffToRVA(pModPe, iSegment, off, &uRVA);
+    if (RT_SUCCESS(rc))
+    {
+        DWORD64 off;
+        IMAGEHLP_LINE64 Line;
+        Line.SizeOfStruct = sizeof(Line);
+        if (g_pfnSymGetLineFromAddr64(pModPe->hSymInst, pModPe->ImageBase + uRVA, &off, &Line))
+        {
+            pLine->RVA = (RTUINTPTR)(Line.Address - pModPe->ImageBase);
+            rc = rtDbgModPeRVAToSegOff(pModPe, pLine->RVA, &pLine->iSegment, &pLine->offSegment);
+            pLine->iLine = Line.LineNumber;
+            pLine->cchFile = strlen(Line.FileName);
+            if (pLine->cchFile >= sizeof(pLine->szFile))
+                pLine->cchFile = sizeof(pLine->szFile) - 1;
+            memcpy(pLine->szFile, Line.FileName, pLine->cchFile + 1);
+        }
+        else
+        {
+            DWORD Err = GetLastError();
+            rc = RTErrConvertFromWin32(Err);
+        }
+    }
+    return rc;
+}
+
+
+/**
+ * @copydoc RTDBGMODOPS::pfnQuerySymbol
+ */
+static DECLCALLBACK(int) rtDbgModPEQuerySymbol(PRTDBGMOD pMod, int32_t iSegment, RTUINTPTR off, PRTDBGSYMBOL pSym)
+{
+    PRTDBGMODPE pModPe = (PRTDBGMODPE)pMod;
+
+    /*
+     * Translate the address to an RVA.
+     */
+    uint32_t uRVA;
+    int rc = rtDbgModPeSegOffToRVA(pModPe, iSegment, off, &uRVA);
+    if (RT_SUCCESS(rc))
+    {
+        DWORD64 off;
+        union
+        {
+            SYMBOL_INFO Sym;
+            char        achBuffer[sizeof(SYMBOL_INFO) + RTDBG_SYMBOL_MAX];
+        } Buf;
+        Buf.Sym.SizeOfStruct = sizeof(SYMBOL_INFO);
+        Buf.Sym.MaxNameLen = RTDBG_SYMBOL_MAX;
+        if (g_pfnSymFromAddr(pModPe->hSymInst, pModPe->ImageBase + uRVA, &off, &Buf.Sym))
+        {
+            pSym->cb     = Buf.Sym.Size;
+            pSym->fFlags = 0;
+            if (Buf.Sym.Flags & SYMFLAG_FUNCTION)
+                pSym->fFlags |= RTDBGSYM_FLAGS_CODE;
+            else if (Buf.Sym.Flags & SYMFLAG_CONSTANT)
+                pSym->fFlags |= RTDBGSYM_FLAGS_ABS; /** @todo SYMFLAG_CONSTANT must be tested - documentation is too brief to say what is really meant here.*/
+            else
+                pSym->fFlags |= RTDBGSYM_FLAGS_DATA;
+            if (Buf.Sym.Flags & SYMFLAG_EXPORT)
+                pSym->fFlags |= RTDBGSYM_FLAGS_EXPORTED;
+            if ((Buf.Sym.Flags & (SYMFLAG_VALUEPRESENT | SYMFLAG_CONSTANT)) == (SYMFLAG_VALUEPRESENT | SYMFLAG_CONSTANT))
+            {
+                pSym->iSegment   = RTDBGSEG_ABS;
+                pSym->offSegment = (RTUINTPTR)Buf.Sym.Value;
+                pSym->RVA        = (RTUINTPTR)Buf.Sym.Value;
+            }
+            else
+            {
+                pSym->RVA        = (RTUINTPTR)(Buf.Sym.Address - pModPe->ImageBase);
+                rc = rtDbgModPeRVAToSegOff(pModPe, pSym->RVA, &pSym->iSegment, &pSym->offSegment);
+            }
+            pSym->cchName = (uint16_t)Buf.Sym.NameLen;
+            if (pSym->cchName >= sizeof(pSym->szName))
+                pSym->cchName = sizeof(pSym->szName) - 1;
+            memcpy(pSym->szName, Buf.Sym.Name, Buf.Sym.NameLen);
+            pSym->szName[Buf.Sym.NameLen] = '\0';
+        }
+        else
+        {
+            DWORD Err = GetLastError();
+            rc = RTErrConvertFromWin32(Err);
+        }
+    }
+    return rc;
+}
+
+
+/**
+ * @copydoc RTDBGMODOPS::pfnClose
+ */
+static DECLCALLBACK(int) rtDbgModPEClose(PRTDBGMOD pMod)
+{
+    PRTDBGMODPE pModPe = (PRTDBGMODPE)pMod;
+
+    if (g_pfnSymCleanup(pModPe->hSymInst))
+        return VINF_SUCCESS;
+
+    DWORD Err = GetLastError();
+    int rc = RTErrConvertFromWin32(Err);
+    AssertMsgFailed(("SymInitialize failed: Err=%d rc=%Rrc\n", Err, rc));
+    return rc;
+}
+
+
+/**
+ * Methods for a PE module.
+ */
+static const RTDBGMODOPS g_rtDbgModPEOps =
+{
+    "PE (dbghelp)",
+    rtDbgModPEClose,
+    rtDbgModPEQuerySymbol,
+    rtDbgModPEQueryLine
+};
+
+
+/**
+ * Checks if the specified dbghelp.dll is usable.
+ *
+ * @returns IPRT status code.
+ *
+ * @param   pszPath     the path to the dbghelp.dll.
+ */
+static int rtDbgModPETryDbgHelp(const char *pszPath, uint32_t *pu32FileVersionMS, uint32_t *pu32FileVersionLS)
+{
+    int rc;
+    DWORD dwHandle = 0;
+    DWORD cb = GetFileVersionInfoSize(pszPath, &dwHandle);
+    if (cb > 0)
+    {
+        void *pvBuf = alloca(cb);
+        if (GetFileVersionInfo(pszPath, dwHandle, cb, pvBuf))
+        {
+            UINT cbValue = 0;
+            VS_FIXEDFILEINFO *pFileInfo;
+            if (VerQueryValue(pvBuf, "\\", (void **)&pFileInfo, &cbValue))
+            {
+                if (    *pu32FileVersionMS < pFileInfo->dwFileVersionMS
+                    ||  (   *pu32FileVersionMS == pFileInfo->dwFileVersionMS
+                         && *pu32FileVersionLS  > pFileInfo->dwFileVersionLS))
+                {
+                    *pu32FileVersionMS = pFileInfo->dwFileVersionMS;
+                    *pu32FileVersionLS = pFileInfo->dwFileVersionLS;
+                }
+                if (pFileInfo->dwFileVersionMS >= 0x60004)
+                    rc = VINF_SUCCESS;
+                else
+                    rc = VERR_VERSION_MISMATCH;
+            }
+            else
+                rc = VERR_GENERAL_FAILURE;
+        }
+        else
+            rc = RTErrConvertFromWin32(GetLastError());
+    }
+    else
+        rc = RTErrConvertFromWin32(GetLastError());
+    return rc;
+}
+
+
+/**
+ * Find the dbghelp.dll
+ */
+static int rtDbgModPEFindDbgHelp(char *pszPath, size_t cchPath)
+{
+    /*
+     * Try the current directory.
+     */
+    uint32_t FileVersionMS = 0;
+    uint32_t FileVersionLS = 0;
+    int rc = VERR_GENERAL_FAILURE;
+    static char s_szDbgHelp[] = "\\dbghelp.dll";
+    if (GetCurrentDirectory(cchPath - sizeof(s_szDbgHelp) + 1, pszPath))
+    {
+        strcat(pszPath, s_szDbgHelp);
+        int rc2 = rtDbgModPETryDbgHelp(pszPath, &FileVersionMS, &FileVersionLS);
+        if (RT_SUCCESS(rc2))
+            return rc2;
+        if (rc != VERR_VERSION_MISMATCH)
+            rc = rc2;
+    }
+
+    /*
+     * Try the application directory.
+     */
+    if (GetModuleFileName(NULL, pszPath, cchPath - sizeof(s_szDbgHelp) + 1))
+    {
+        strcat(strrchr(pszPath, '\\'), s_szDbgHelp);
+        int rc2 = rtDbgModPETryDbgHelp(pszPath, &FileVersionMS, &FileVersionLS);
+        if (RT_SUCCESS(rc))
+            return rc2;
+        if (rc != VERR_VERSION_MISMATCH)
+            rc = rc2;
+    }
+
+    /*
+     * Try the windows directory.
+     */
+    if (GetSystemDirectory(pszPath, cchPath - sizeof(s_szDbgHelp) + 1))
+    {
+        strcat(pszPath, s_szDbgHelp);
+        int rc2 = rtDbgModPETryDbgHelp(pszPath, &FileVersionMS, &FileVersionLS);
+        if (RT_SUCCESS(rc2))
+            return rc2;
+        if (rc != VERR_VERSION_MISMATCH)
+            rc = rc2;
+    }
+
+    /*
+     * Try the windows directory.
+     */
+    if (GetWindowsDirectory(pszPath, cchPath - sizeof(s_szDbgHelp) + 1))
+    {
+        strcat(pszPath, s_szDbgHelp);
+        int rc2 = rtDbgModPETryDbgHelp(pszPath, &FileVersionMS, &FileVersionLS);
+        if (RT_SUCCESS(rc2))
+            return rc2;
+        if (rc != VERR_VERSION_MISMATCH)
+            rc = rc2;
+    }
+
+    /*
+     * Try the path.
+     */
+    /** @todo find the actual path specs, I'm probably not doing this 100% correctly here. */
+    DWORD cb = GetEnvironmentVariable("PATH", NULL, 0) + 64;
+    char *pszSearchPath = (char *) alloca(cb);
+    if (GetEnvironmentVariable("PATH", pszSearchPath, cb) < cb)
+    {
+        char *psz = pszSearchPath;
+        while (*psz)
+        {
+            /* find the end of the path. */
+            char *pszEnd = strchr(psz, ';');
+            if (!pszEnd)
+                pszEnd = strchr(psz, '\0');
+            if (pszEnd != psz)
+            {
+                /* construct filename and try it out */
+                memcpy(pszPath, psz, pszEnd - psz);
+                memcpy(&pszPath[pszEnd - psz], s_szDbgHelp, sizeof(s_szDbgHelp));
+                int rc2 = rtDbgModPETryDbgHelp(pszPath, &FileVersionMS, &FileVersionLS);
+                if (RT_SUCCESS(rc2))
+                    return rc2;
+                if (rc != VERR_VERSION_MISMATCH)
+                    rc = rc2;
+            }
+
+            /* next path */
+            if (!*pszEnd)
+                break;
+            psz = pszEnd + 1;
+        }
+    }
+
+    if (rc == VERR_VERSION_MISMATCH)
+        AssertMsgFailed(("dbghelp.dll found, but it was ancient! The highest file version found was 0x%08x'%08x.\n"
+                         "This program require a file version of at least 0x00060004'00000000. Please download\n"
+                         "the latest windbg and use the dbghelp.dll from that package. Just put it somewhere in\n"
+                         "the PATH and we'll find it.\n", FileVersionMS, FileVersionLS));
+    else
+        AssertMsgFailed(("dbghelp.dll was not found! Download the latest windbg and use the dbghelp.dll\n"
+                         "from that package - just put it somewhere in the PATH and we'll find it.\n"));
+    return rc;
+}
+
+
+/**
+ * Loads the dbghelp.dll, check that it's the right version, and
+ * resolves all the symbols we need.
+ *
+ * @returns IPRT status code.
+ */
+static int rtDbgModPELoadDbgHelp(void)
+{
+    if (g_hDbgHelp)
+        return VINF_SUCCESS;
+
+    /* primitive locking - make some useful API for this kind of spinning! */
+    static volatile uint32_t s_u32Lock = 0;
+    while (!ASMAtomicCmpXchgU32(&s_u32Lock, 1, 0))
+        while (s_u32Lock)
+            RTThreadYield();
+    if (g_hDbgHelp)
+    {
+        ASMAtomicXchgU32(&s_u32Lock, 0);
+        return VINF_SUCCESS;
+    }
+
+    /*
+     * Load it - try current dir first.
+     */
+    char szPath[260];
+    int rc = rtDbgModPEFindDbgHelp(szPath, sizeof(szPath));
+    if (RT_FAILURE(rc))
+    {
+        ASMAtomicXchgU32(&s_u32Lock, 0);
+        return rc;
+    }
+
+    HMODULE hmod = LoadLibraryEx(szPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
+    if (!hmod)
+    {
+        DWORD Err = GetLastError();
+        int rc = RTErrConvertFromWin32(Err);
+        AssertMsgFailedReturn(("Failed to load '%s', Err=%d rc=%Rrc\n", szPath, Err, rc), rc);
+    }
+
+    /*
+     * Check the API version (too).
+     */
+    LPAPI_VERSION (WINAPI *pfnImagehlpApiVersion)(VOID);
+    FARPROC *ppfn = (FARPROC *)&pfnImagehlpApiVersion;
+    *ppfn = GetProcAddress(hmod, "ImagehlpApiVersion");
+    if (*ppfn)
+    {
+        LPAPI_VERSION pVersion = pfnImagehlpApiVersion();
+        if (    pVersion
+            &&  (   pVersion->MajorVersion > 4
+                 || (pVersion->MajorVersion == 4 && pVersion->MinorVersion > 0)
+                 || (pVersion->MajorVersion == 4 && pVersion->MinorVersion == 0 && pVersion->Revision >= 5)
+                )
+           )
+        {
+            /*
+             * Resolve the entrypoints we need.
+             */
+            static const struct
+            {
+                const char *pszName;
+                FARPROC *ppfn;
+            }   s_aFunctions[] =
+            {
+                { "SymInitialize",      (FARPROC *)&g_pfnSymInitialize },
+                { "SymCleanup",         (FARPROC *)&g_pfnSymCleanup },
+                { "SymSetOptions",      (FARPROC *)&g_pfnSymSetOptions },
+                { "SymLoadModule64",    (FARPROC *)&g_pfnSymLoadModule64 },
+                { "SymFromAddr",        (FARPROC *)&g_pfnSymFromAddr },
+                { "SymFromAddr",        (FARPROC *)&g_pfnSymFromAddr },
+                { "SymGetLineFromAddr64", (FARPROC *)&g_pfnSymGetLineFromAddr64 },
+            };
+            for (unsigned i = 0; i < ELEMENTS(s_aFunctions); i++)
+            {
+                FARPROC pfn = GetProcAddress(hmod, s_aFunctions[i].pszName);
+                if (!pfn)
+                {
+                    DWORD Err = GetLastError();
+                    rc = RTErrConvertFromWin32(Err);
+                    AssertMsgFailed(("Failed to resolve %s in dbghelp, Err=%d rc=%Rrc\n",
+                                     s_aFunctions[i].pszName, Err, rc));
+                    break;
+                }
+                *s_aFunctions[i].ppfn = pfn;
+            }
+            if (RT_SUCCESS(rc))
+            {
+                ASMAtomicXchgSize(&g_hDbgHelp, hmod);
+                ASMAtomicXchgU32(&s_u32Lock, 0);
+                return VINF_SUCCESS;
+            }
+        }
+        else
+        {
+            rc = VERR_VERSION_MISMATCH;
+            AssertMsgFailed(("ImagehlpApiVersion -> %p and MajorVersion=%d.\n", pVersion, pVersion ? pVersion->MajorVersion : 0));
+        }
+    }
+    else
+    {
+        DWORD Err = GetLastError();
+        rc = RTErrConvertFromWin32(Err);
+        AssertMsgFailed(("Failed to resolve ImagehlpApiVersionEx in dbghelp, Err=%d rc=%Rrc\n", Err, rc));
+    }
+    FreeLibrary(hmod);
+    ASMAtomicXchgU32(&s_u32Lock, 0);
+    return rc;
+}
+
+
+/**
+ * Opens the debug info for a PE image using the windows dbghelp library.
+ *
+ * @returns IPRT status code.
+ *
+ * @param   File                The handle to the module.
+ * @param   offHdr              The offset of the PE header.
+ * @param   pszModulePath       The path to the module.
+ * @param   ppDbgMod            Where to store the module handle.
+ *
+ */
+int rtDbgModPEOpen(RTFILE File, RTFOFF offHdr, const char *pszModulePath, PRTDBGMOD *ppDbgMod)
+{
+    /*
+     * We need to read the section headers and get the image size.
+     */
+    IMAGE_FILE_HEADER FHdr;
+    int rc = RTFileReadAt(File, offHdr + OFFSETOF(IMAGE_NT_HEADERS32, FileHeader), &FHdr, sizeof(FHdr), NULL);
+    AssertRCReturn(rc, rc);
+
+    uint32_t cbImage;
+    if (FHdr.SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER32))
+        rc = RTFileReadAt(File, offHdr + OFFSETOF(IMAGE_NT_HEADERS32, OptionalHeader.SizeOfImage),
+                          &cbImage, sizeof(cbImage), NULL);
+    else if (FHdr.SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER64))
+        rc = RTFileReadAt(File, offHdr + OFFSETOF(IMAGE_NT_HEADERS64, OptionalHeader.SizeOfImage),
+                          &cbImage, sizeof(cbImage), NULL);
+    else
+        AssertFailedReturn(VERR_BAD_EXE_FORMAT);
+    AssertRCReturn(rc, rc);
+
+    /*
+     * Load dbghelp.dll.
+     */
+    rc = rtDbgModPELoadDbgHelp();
+    if (RT_FAILURE(rc))
+        return rc;
+
+    /*
+     * Allocate the module and read/construct the section headers.
+     */
+    PRTDBGMODPE pModPe = (PRTDBGMODPE)RTMemAlloc(OFFSETOF(RTDBGMODPE, aSections[FHdr.NumberOfSections + 2]));
+    AssertReturn(pModPe, VERR_NO_MEMORY);
+    pModPe->Core.u32Magic   = RTDBGMOD_MAGIC;
+    pModPe->Core.pOps       = &g_rtDbgModPEOps;
+    pModPe->Core.File       = File;
+    pModPe->cbImage         = cbImage;
+    pModPe->cSections       = 1 + FHdr.NumberOfSections;
+    rc = RTFileReadAt(File, offHdr + OFFSETOF(IMAGE_NT_HEADERS32, OptionalHeader) + FHdr.SizeOfOptionalHeader,
+                      &pModPe->aSections[1], sizeof(pModPe->aSections[0]) * FHdr.NumberOfSections, NULL);
+    if (RT_SUCCESS(rc))
+    {
+        PIMAGE_SECTION_HEADER pSH = &pModPe->aSections[0];
+        memcpy(pSH->Name, "headers", sizeof(pSH->Name));
+        pSH->Misc.VirtualSize       = pModPe->aSections[1].VirtualAddress;
+        pSH->VirtualAddress         = 0;
+        pSH->SizeOfRawData          = pSH->Misc.VirtualSize;
+        pSH->PointerToRawData       = 0;
+        pSH->PointerToRelocations   = 0;
+        pSH->PointerToLinenumbers   = 0;
+        pSH->NumberOfRelocations    = 0;
+        pSH->NumberOfLinenumbers    = 0;
+        pSH->Characteristics        = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_MEM_READ;
+
+        uint32_t uTheEnd = pModPe->aSections[FHdr.NumberOfSections].VirtualAddress
+                         + pModPe->aSections[FHdr.NumberOfSections].Misc.VirtualSize;
+        if (uTheEnd < cbImage)
+        {
+            pSH = &pModPe->aSections[pModPe->cSections++];
+            memcpy(pSH->Name, "tail\0\0\0", sizeof(pSH->Name));
+            pSH->Misc.VirtualSize       = cbImage - uTheEnd;
+            pSH->VirtualAddress         = uTheEnd;
+            pSH->SizeOfRawData          = pSH->Misc.VirtualSize;
+            pSH->PointerToRawData       = 0;
+            pSH->PointerToRelocations   = 0;
+            pSH->PointerToLinenumbers   = 0;
+            pSH->NumberOfRelocations    = 0;
+            pSH->NumberOfLinenumbers    = 0;
+            pSH->Characteristics        = IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_ALIGN_1BYTES | IMAGE_SCN_MEM_READ;
+        }
+
+        /*
+         * Find a new dbghelp handle.
+         *
+         * We assume 4GB of handles outlast most debugging sessions, or in anyways that
+         * when we start reusing handles they are no longer in use. :-)
+         */
+        static volatile uint32_t s_u32LastHandle = 1;
+        HANDLE hSymInst = (HANDLE)ASMAtomicIncU32(&s_u32LastHandle);
+        while (     hSymInst == INVALID_HANDLE_VALUE
+               ||   hSymInst == (HANDLE)0
+               ||   hSymInst == GetCurrentProcess())
+            hSymInst = (HANDLE)ASMAtomicIncU32(&s_u32LastHandle);
+
+        /*
+         * Initialize dbghelp and try open the specified module.
+         */
+        if (g_pfnSymInitialize(hSymInst, NULL, FALSE))
+        {
+            g_pfnSymSetOptions(SYMOPT_LOAD_LINES | SYMOPT_AUTO_PUBLICS | SYMOPT_ALLOW_ABSOLUTE_SYMBOLS);
+
+            RTFileSeek(File, 0, RTFILE_SEEK_BEGIN, NULL); /* don't know if this is required or not... */
+            DWORD64 ImageBase = g_pfnSymLoadModule64(hSymInst, (HANDLE)File, pszModulePath, NULL, 0x00400000, 0);
+            if (ImageBase)
+            {
+                pModPe->hSymInst    = hSymInst;
+                pModPe->ImageBase   = ImageBase;
+                *ppDbgMod = &pModPe->Core;
+                return rc;
+            }
+
+            DWORD Err = GetLastError();
+            rc = RTErrConvertFromWin32(Err);
+            AssertMsgFailed(("SymLoadModule64 failed: Err=%d rc=%Rrc\n", Err, rc));
+            g_pfnSymCleanup(hSymInst);
+        }
+        else
+        {
+            DWORD Err = GetLastError();
+            rc = RTErrConvertFromWin32(Err);
+            AssertMsgFailed(("SymInitialize failed: Err=%d rc=%Rrc\n", Err, rc));
+        }
+    }
+    else
+        AssertRC(rc);
+
+    RTMemFree(pModPe);
+    return rc;
+}
+
Index: trunk/kProfile/dbgline.cpp
===================================================================
--- trunk/kProfile/dbgline.cpp	(revision 3524)
+++ trunk/kProfile/dbgline.cpp	(revision 3524)
@@ -0,0 +1,74 @@
+/* $Id: $ */
+/** @file
+ *
+ * kProfile Mark 2 - Debug Info Line Number.
+ *
+ * Copyright (c) 2006 knut st. osmundsen <bird-src-spam@anduin.net.de>
+ *
+ *
+ * This file is part of kLIBC.
+ *
+ * kLIBC is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * kLIBC is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with kLIBC; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+
+/*******************************************************************************
+*   Header Files                                                               *
+*******************************************************************************/
+#include <iprt/err.h>
+#include <iprt/assert.h>
+#include <iprt/alloc.h>
+
+#include "dbg.h"
+
+
+/**
+ * Duplicates a line number.
+ *
+ * To save heap space, the returned line number will not own more heap space
+ * than it strictly need to. So, it's not possible to append stuff to the symbol
+ * or anything of that kind.
+ *
+ * @returns Pointer to the duplicate.
+ *          This must be freed using RTDbgSymbolFree().
+ * @param   pLine       The line number to be duplicated.
+ */
+RTDECL(PRTDBGLINE) RTDbgLineDup(PCRTDBGLINE pLine)
+{
+    AssertMsgReturn(VALID_PTR(pLine), ("%p\n", pLine), NULL);
+    size_t cb = OFFSETOF(RTDBGLINE, szFile[pLine->cchFile + 1]);
+    return (PRTDBGLINE)RTMemDup(pLine, cb);
+}
+
+
+/**
+ * Frees a line number obtained from the RTDbg API.
+ *
+ * @returns VINF_SUCCESS on success.
+ * @returns VERR_INVALID_POINTER if a NULL pointer or an !VALID_PTR() is passed in.
+ *
+ * @param   pLine       The line number to be freed.
+ */
+RTDECL(int) RTDbgLineFree(PRTDBGLINE pLine)
+{
+    if (!pLine)
+        return VERR_INVALID_POINTER;
+    AssertMsgReturn(VALID_PTR(pLine), ("%p\n", pLine), VERR_INVALID_POINTER);
+
+    RTMemFree(pLine);
+    return VINF_SUCCESS;
+}
+
Index: trunk/kProfile/dbgmodule.cpp
===================================================================
--- trunk/kProfile/dbgmodule.cpp	(revision 3524)
+++ trunk/kProfile/dbgmodule.cpp	(revision 3524)
@@ -0,0 +1,266 @@
+/* $Id: $ */
+/** @file
+ *
+ * kProfile Mark 2 - Debug Info Reader
+ *
+ * Copyright (c) 2006 knut st. osmundsen <bird-src-spam@anduin.net.de>
+ *
+ *
+ * This file is part of kLIBC.
+ *
+ * kLIBC is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * kLIBC is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with kLIBC; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+
+/*******************************************************************************
+*   Header Files                                                               *
+*******************************************************************************/
+#include <iprt/file.h>
+#include <iprt/string.h>
+#include <iprt/err.h>
+#include <iprt/assert.h>
+#include <iprt/alloc.h>
+
+#include "DBGInternal.h"
+#include "internal/ldrMZ.h"
+#include "internal/ldrPE.h"
+
+
+
+/**
+ * Opens the debug info for a specified executable module.
+ *
+ * @returns IPRT status code.
+ * @param   pszModulePath       The path to the executable module.
+ * @param   ppDbgMod            Where to store the debug module handle.
+ */
+RTDECL(int) RTDbgModuleOpen(const char *pszModulePath, PRTDBGMOD *ppDbgMod)
+{
+    /*
+     * Validate input.
+     */
+    AssertMsgReturn(VALID_PTR(pszModulePath), ("%p\n", pszModulePath), VERR_INVALID_POINTER);
+    AssertMsgReturn(*pszModulePath, ("%p\n", pszModulePath), VERR_INVALID_PARAMETER);
+    AssertMsgReturn(VALID_PTR(ppDbgMod), ("%p\n", ppDbgMod), VERR_INVALID_POINTER);
+    *ppDbgMod = NULL;
+
+    /*
+     * The file and try figure out the format.
+     */
+    RTFILE File;
+    int rc = RTFileOpen(&File, pszModulePath, RTFILE_O_READ | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    RTFOFF offHdr = 0;
+    union
+    {
+        uint32_t    au32[4];
+        uint16_t    au16[8];
+        uint8_t     ab[16];
+    } Buf;
+    rc = RTFileRead(File, &Buf, sizeof(Buf), NULL);
+    if (    RT_SUCCESS(rc)
+        &&  Buf.au16[0] == IMAGE_DOS_SIGNATURE)
+    {
+        /* new header? */
+        uint32_t offNewHeader;
+        rc = RTFileReadAt(File, OFFSETOF(IMAGE_DOS_HEADER, e_lfanew), &offNewHeader, sizeof(offNewHeader), NULL);
+        if (RT_SUCCESS(rc) && offNewHeader)
+        {
+            offHdr = offNewHeader;
+            rc = RTFileReadAt(File, offNewHeader, &Buf, sizeof(Buf), NULL);
+        }
+    }
+    if (RT_SUCCESS(rc))
+    {
+        if (Buf.au16[0] == IMAGE_DOS_SIGNATURE)
+            rc = VERR_NOT_SUPPORTED;
+#ifdef IMAGE_NE_SIGNATURE
+        else if (Buf.au16[0] == IMAGE_NE_SIGNATURE)
+            rc = VERR_NOT_SUPPORTED;
+#endif
+#ifdef IMAGE_LX_SIGNATURE
+        else if (Buf.au16[0] == IMAGE_LX_SIGNATURE)
+            rc = VERR_NOT_SUPPORTED;
+#endif
+#ifdef IMAGE_LE_SIGNATURE
+        else if (Buf.au16[0] == IMAGE_LE_SIGNATURE)
+            rc = VERR_NOT_SUPPORTED;
+#endif
+#ifdef IMAGE_ELF_SIGNATURE
+        else if (Buf.au32[0] == IMAGE_ELF_SIGNATURE)
+            rc = VERR_NOT_SUPPORTED;
+#endif
+#ifdef IMAGE_NT_SIGNATURE
+        else if (Buf.au32[0] == IMAGE_NT_SIGNATURE)
+            rc = rtDbgModPEOpen(File, offHdr, pszModulePath, ppDbgMod);
+#endif
+        /** @todo there are a number of text file formats too which I want to support. */
+        else
+            rc = VERR_NOT_SUPPORTED;
+
+    }
+
+    if (RT_FAILURE(rc))
+        RTFileClose(File);
+    return rc;
+}
+
+
+/**
+ * Validates a debug module handle.
+ * All necessary asserting will be taken care of here.
+ *
+ * @returns True / false.
+ * @param   pMod        The debug module handle.
+ */
+DECLINLINE(bool) rtDbgModIsValid(PRTDBGMOD pMod)
+{
+    AssertMsgReturn(VALID_PTR(pMod), ("%p", pMod), false);
+    AssertMsgReturn(pMod->u32Magic == RTDBGMOD_MAGIC, ("%RX32", pMod->u32Magic), false);
+    AssertMsgReturn(VALID_PTR(pMod->pOps), ("%p", pMod->pOps), false);
+    return true;
+}
+
+
+/**
+ * Closes the module.
+ *
+ * @returns IPRT status code.
+ * @param   pMod        The module handle.
+ */
+RTDECL(int) RTDbgModuleClose(PRTDBGMOD pMod)
+{
+    if (!rtDbgModIsValid(pMod))
+        return VERR_INVALID_PARAMETER;
+
+    int rc = pMod->pOps->pfnClose(pMod);
+    if (RT_SUCCESS(rc))
+        RTMemFree(pMod);
+    return rc;
+}
+
+
+/**
+ * Gets a symbol by segment:offset.
+ * This will be approximated to the nearest symbol if there is no exact match.
+ *
+ * @returns IPRT status code.
+ * @param   pMod        The module.
+ * @param   iSegment    The segment this offset is relative to.
+ *                      The -1 segment is special, it means that the addres is relative to
+ *                      the image base. The image base is where the first bit of the image
+ *                      is mapped during load.
+ * @param   off         The offset into the segment.
+ * @param   pSym        Where to store the symbol details.
+ */
+RTDECL(int) RTDbgModuleQuerySymbol(PRTDBGMOD pMod, int32_t iSegment, RTUINTPTR off, PRTDBGSYMBOL pSym)
+{
+    if (!rtDbgModIsValid(pMod))
+        return VERR_INVALID_PARAMETER;
+    AssertMsgReturn(VALID_PTR(pSym), ("%p", pSym), VERR_INVALID_POINTER);
+
+    return pMod->pOps->pfnQuerySymbol(pMod, iSegment, off, pSym);
+}
+
+
+/**
+ * Gets & allocates a symbol by segment:offset.
+ * This will be approximated to the nearest symbol if there is no exact match.
+ *
+ * @returns IPRT status code.
+ * @param   pMod        The module.
+ * @param   iSegment    The segment this offset is relative to.
+ *                      The -1 segment is special, it means that the addres is relative to
+ *                      the image base. The image base is where the first bit of the image
+ *                      is mapped during load.
+ * @param   off         The offset into the segment.
+ * @param   ppSym       Where to store the pointer to the symbol info.
+ *                      Free the returned symbol using RTDbgSymbolFree().
+ */
+RTDECL(int) RTDbgModuleQuerySymbolA(PRTDBGMOD pMod, int32_t iSegment, RTUINTPTR off, PPRTDBGSYMBOL ppSym)
+{
+    AssertMsgReturn(VALID_PTR(ppSym), ("%p\n", ppSym), VERR_INVALID_POINTER);
+
+    RTDBGSYMBOL Sym;
+    int rc = RTDbgModuleQuerySymbol(pMod, iSegment, off, &Sym);
+    if (RT_SUCCESS(rc))
+    {
+        *ppSym = RTDbgSymbolDup(&Sym);
+        if (!*ppSym)
+            rc = VERR_NO_MEMORY;
+    }
+    else
+        *ppSym = NULL;
+    return rc;
+}
+
+
+/**
+ * Gets a line number entry by segment:offset.
+ * This will be approximated to the nearest line number there is no exact match.
+ *
+ * @returns IPRT status code.
+ * @param   pMod        The module.
+ * @param   iSegment    The segment this offset is relative to.
+ *                      The -1 segment is special, it means that the addres is relative to
+ *                      the image base. The image base is where the first bit of the image
+ *                      is mapped during load.
+ * @param   off         The offset into the segment.
+ * @param   pLine       Where to store the line number details.
+ */
+RTDECL(int) RTDbgModuleQueryLine(PRTDBGMOD pMod, int32_t iSegment, RTUINTPTR off, PRTDBGLINE pLine)
+{
+    if (!rtDbgModIsValid(pMod))
+        return VERR_INVALID_PARAMETER;
+    AssertMsgReturn(VALID_PTR(pLine), ("%p", pLine), VERR_INVALID_POINTER);
+
+    return pMod->pOps->pfnQueryLine(pMod, iSegment, off, pLine);
+}
+
+
+/**
+ * Gets & allocates a line number entry by segment:offset.
+ * This will be approximated to the nearest line number there is no exact match.
+ *
+ * @returns IPRT status code.
+ * @param   pMod        The module.
+ * @param   iSegment    The segment this offset is relative to.
+ *                      The -1 segment is special, it means that the addres is relative to
+ *                      the image base. The image base is where the first bit of the image
+ *                      is mapped during load.
+ * @param   off         The offset into the segment.
+ * @param   ppLine      Where to store the pointer to the line number info.
+ *                      Free the returned line number using RTDbgLineFree().
+ */
+RTDECL(int) RTDbgModuleQueryLineA(PRTDBGMOD pMod, int32_t iSegment, RTUINTPTR off, PPRTDBGLINE ppLine)
+{
+    AssertMsgReturn(VALID_PTR(ppLine), ("%p\n", ppLine), VERR_INVALID_POINTER);
+
+    RTDBGLINE Line;
+    int rc = RTDbgModuleQueryLine(pMod, iSegment, off, &Line);
+    if (RT_SUCCESS(rc))
+    {
+        *ppLine = RTDbgLineDup(&Line);
+        if (!*ppLine)
+            rc = VERR_NO_MEMORY;
+    }
+    else
+        *ppLine = NULL;
+    return rc;
+}
+
Index: trunk/kProfile/dbgsymbol.cpp
===================================================================
--- trunk/kProfile/dbgsymbol.cpp	(revision 3524)
+++ trunk/kProfile/dbgsymbol.cpp	(revision 3524)
@@ -0,0 +1,74 @@
+/* $Id: $ */
+/** @file
+ *
+ * kProfile Mark 2 - Debug Info Symbol.
+ *
+ * Copyright (c) 2006 knut st. osmundsen <bird-src-spam@anduin.net.de>
+ *
+ *
+ * This file is part of kLIBC.
+ *
+ * kLIBC is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * kLIBC is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with kLIBC; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+
+/*******************************************************************************
+*   Header Files                                                               *
+*******************************************************************************/
+#include <iprt/err.h>
+#include <iprt/assert.h>
+#include <iprt/alloc.h>
+
+#include "dbg.h"
+
+
+/**
+ * Duplicates a symbol.
+ *
+ * To save heap space, the returned symbol will not own more heap space than
+ * it strictly need to. So, it's not possible to append stuff to the symbol
+ * or anything of that kind.
+ *
+ * @returns Pointer to the duplicate.
+ *          This must be freed using RTDbgSymbolFree().
+ * @param   pSymbol     The symbol to be duplicated.
+ */
+RTDECL(PRTDBGSYMBOL) RTDbgSymbolDup(PCRTDBGSYMBOL pSymbol)
+{
+    AssertMsgReturn(VALID_PTR(pSymbol), ("%p\n", pSymbol), NULL);
+    size_t cb = OFFSETOF(RTDBGSYMBOL, szName[pSymbol->cchName + 1]);
+    return (PRTDBGSYMBOL)RTMemDup(pSymbol, cb);
+}
+
+
+/**
+ * Frees a symbol obtained from the RTDbg API.
+ *
+ * @returns VINF_SUCCESS on success.
+ * @returns VERR_INVALID_POINTER if a NULL pointer or an !VALID_PTR() is passed in.
+ *
+ * @param   pSymbol     The symbol to be freed.
+ */
+RTDECL(int) RTDbgSymbolFree(PRTDBGSYMBOL pSymbol)
+{
+    if (!pSymbol)
+        return VERR_INVALID_POINTER;
+    AssertMsgReturn(VALID_PTR(pSymbol), ("%p\n", pSymbol), VERR_INVALID_POINTER);
+
+    RTMemFree(pSymbol);
+    return VINF_SUCCESS;
+}
+
Index: trunk/kProfile/dllmain-win32.cpp
===================================================================
--- trunk/kProfile/dllmain-win32.cpp	(revision 3524)
+++ trunk/kProfile/dllmain-win32.cpp	(revision 3524)
@@ -0,0 +1,49 @@
+
+
+
+/*******************************************************************************
+*   Header Files                                                               *
+*******************************************************************************/
+#include <Windows.h>
+
+#include "kProfileR3.h"
+
+
+
+/**
+ * The DLL Main for the IPRT DLL.
+ *
+ * This is required for profiling, it is not (currently) used for any
+ * other purposes. (It could be used to track the death of alian threads
+ * perhaps, if we decide to adopt them automatically.)
+ *
+ * @returns Success indicator.
+ * @param   hInstDll        The instance handle of the DLL. (i.e. the module handle)
+ * @param   fdwReason       The reason why we're here. This is a 'flag' for reasons of
+ *                          tradition, it's really a kind of enum.
+ * @param   pReserved       Reserved / undocumented something.
+ */
+BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, PVOID pReserved)
+{
+    switch (fdwReason)
+    {
+        case DLL_PROCESS_ATTACH:
+            if (kPrfInitialize())
+                return FALSE;
+            break;
+
+        case DLL_PROCESS_DETACH:
+            kPrfTerminate();
+            break;
+
+        case DLL_THREAD_ATTACH:
+            break;
+
+        case DLL_THREAD_DETACH:
+            kPrfTerminateThread();
+            break;
+    }
+
+    return TRUE;
+}
+
Index: trunk/kProfile/kPrf2-win32.def
===================================================================
--- trunk/kProfile/kPrf2-win32.def	(revision 3524)
+++ trunk/kProfile/kPrf2-win32.def	(revision 3524)
@@ -0,0 +1,5 @@
+LIBRARY kPrf2
+EXPORTS
+    _penter
+    _pexit
+    KPrfInit
Index: trunk/kProfile/kPrf2Read.cpp
===================================================================
--- trunk/kProfile/kPrf2Read.cpp	(revision 3524)
+++ trunk/kProfile/kPrf2Read.cpp	(revision 3524)
@@ -0,0 +1,511 @@
+/* $Id: $ */
+/** @file
+ *
+ * kProfiler Mark 2 - The reader and statistics producer.
+ *
+ * Copyright (c) 2006 knut st. osmundsen <bird-src-spam@anduin.net.de>
+ *
+ *
+ * This file is part of kLIBC.
+ *
+ * kLIBC is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * kLIBC is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with kLIBC; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+
+
+/*******************************************************************************
+*   Header Files                                                               *
+*******************************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+/* iprt dependencies */
+#include <iprt/types.h>
+#include <iprt/err.h>
+#include <iprt/avl.h>
+#include "dbg.h"
+#if 1
+# include <iprt/alloc.h>
+# define malloc(cb) RTMemAlloc(cb)
+# define free(pv) RTMemFree(pv)
+#endif
+
+
+/** @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) + (uintptr_t)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 uint32_t and mangles the type name.
+ * @internal
+ */
+#define KPRF_OFFSETOF(kPrfType, Member) ( (uint32_t)(uintptr_t)&((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, uint32_t *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, uint32_t 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", \
+                    (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,
+                       (uint32_t)&((PKPRF32STACK)0)->aFrames[pHdr->Hdr32.cMaxStackFrames],
+                       (uint32_t)&((PKPRF64STACK)0)->aFrames[pHdr->Hdr32.cMaxStackFrames]);
+
+    uintptr_t cbHeader = (uintptr_t)&pHdr->Hdr32.aiFunctions[pHdr->Hdr32.cFunctions] - (uintptr_t)pHdr;
+    if (    cbHeader != (uint32_t)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. */
+    uint32_t 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 (uint32_t 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.
+     */
+    uint32_t 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.
+     */
+    uint32_t 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;
+}
Index: trunk/kProfile/kPrfReader.h
===================================================================
--- trunk/kProfile/kPrfReader.h	(revision 3524)
+++ trunk/kProfile/kPrfReader.h	(revision 3524)
@@ -0,0 +1,45 @@
+
+
+#include <string>
+
+typedef
+
+/**
+ * Debug info cache.
+ *
+ * An objects of this class acts a frontend to the low-level
+ * debug info readers.
+ */
+class kPrfDebugInfoCache
+{
+public:
+    kPrfDebugInfoCache(unsigned cMaxModules = ~0U);
+    ~kPrfDebugInfoCache();
+
+    /** Resolves a symbol in a specific module. */
+    int findSymbol();
+    int findLine();
+};
+
+/**
+ * Internal class which does the reader job behind the API / commandline tool.
+ */
+class kPrfReader
+{
+public:
+    kPrfReader(const char *pszDataSetPath);
+    ~kPrfReader();
+
+    /** Analyses the data set. */
+    int analyse(int fSomeOptionsIHaventFiguredOutYet);
+
+    /** Writes the analysis report as HTML. */
+    int reportAsHtml(FILE *pOut);
+
+    /** Dumps the data set in a raw fashion to the specified file stream. */
+    int dump(FILE *pOut);
+
+protected:
+    /** Pointer to the debug info cache object. */
+    kPrfDebugInfoCache *pDbgCache;
+};
Index: trunk/kProfile/kProfileR3.cpp
===================================================================
--- trunk/kProfile/kProfileR3.cpp	(revision 3524)
+++ trunk/kProfile/kProfileR3.cpp	(revision 3524)
@@ -0,0 +1,1626 @@
+
+
+/*******************************************************************************
+*   Header Files                                                               *
+*******************************************************************************/
+#if defined(__WIN32__) || defined(__WIN64__)
+# include <windows.h>
+# include <psapi.h>
+# include <malloc.h>
+# define IN_RING3
+# include <iprt/stdint.h> /* Temporary IPRT convenience */
+# if _MSC_VER >= 1400
+#  include <intrin.h>
+#  define HAVE_INTRIN
+# endif
+
+#elif defined(__LINUX__) || defined(__FREEBSD__)
+# define KPRF_USE_PTHREAD
+# include <pthread.h>
+# include <stdint.h>
+# define KPRF_USE_MMAN
+# include <sys/mman.h>
+# include <sys/fcntl.h>
+# include <unistd.h>
+# include <stdlib.h>
+# ifndef O_BINARY
+#  define O_BINARY 0
+# endif
+
+#elif defined(__OS2__)
+# define INCL_BASE
+# include <os2s.h>
+# include <stdint.h>
+# include <sys/fmutex.h>
+
+#else
+# error "not ported to this OS..."
+#endif
+
+
+/*
+ * Instantiate the header.
+ */
+#define KPRF_NAME(Suffix)               KPrf##Suffix
+#define KPRF_TYPE(Prefix,Suffix)        Prefix##KPRF##Suffix
+#if defined(__WIN32__) || defined(__WIN64__) || defined(__OS2__)
+# define KPRF_DECL_FUNC(type, name)     extern "C"  __declspec(dllexport) type __cdecl KPRF_NAME(name)
+#else
+# define KPRF_DECL_FUNC(type, name)     extern "C" type KPRF_NAME(name)
+#endif
+#if 1
+# ifdef __GNUC__
+#  define KPRF_ASSERT(expr) do { if (!(expr)) { __asm__ __volatile__("int3\n\tnop\n\t");} } while (0)
+# else
+#  define KPRF_ASSERT(expr) do { if (!(expr)) { __asm int 3 \
+                            } } while (0)
+# endif
+#else
+# define KPRF_ASSERT(expr) do { } while (0)
+#endif
+
+#include "prfcore.h.h"
+
+
+
+/*******************************************************************************
+*   Structures and Typedefs                                                    *
+*******************************************************************************/
+/** Mutex lock type. */
+#if defined(KPRF_USE_PTHREAD)
+typedef pthread_mutex_t     KPRF_TYPE(,MUTEX);
+#elif defined(__WIN32__) || defined(__WIN64__)
+typedef CRITICAL_SECTION    KPRF_TYPE(,MUTEX);
+#elif defined(__OS2__)
+typedef struct _fmutex      KPRF_TYPE(,MUTEX);
+#endif
+/** Pointer to a mutex lock. */
+typedef KPRF_TYPE(,MUTEX)  *KPRF_TYPE(P,MUTEX);
+
+
+#if defined(KPRF_USE_PTHREAD)
+/** Read/Write lock type. */
+typedef pthread_rwlock_t    KPRF_TYPE(,RWLOCK);
+#elif defined(__WIN32__) || defined(__WIN64__) || defined(__OS2__)
+/** Read/Write lock state. */
+typedef enum KPRF_TYPE(,RWLOCKSTATE)
+{
+    RWLOCK_STATE_UNINITIALIZED = 0,
+    RWLOCK_STATE_SHARED,
+    RWLOCK_STATE_LOCKING,
+    RWLOCK_STATE_EXCLUSIVE,
+    RWLOCK_STATE_32BIT_HACK = 0x7fffffff
+} KPRF_TYPE(,RWLOCKSTATE);
+/** Update the state. */
+#define KPRF_RWLOCK_SETSTATE(pRWLock, enmNewState) \
+    kPrfAtomicSet32((volatile uint32_t *)&(pRWLock)->enmState, (uint32_t)(enmNewState))
+
+/** Read/Write lock type. */
+typedef struct KPRF_TYPE(,RWLOCK)
+{
+    /** This mutex serialize the access and updating of the members
+     * of this structure. */
+    KPRF_TYPE(,MUTEX)       Mutex;
+    /** The current number of readers. */
+    uint32_t                cReaders;
+    /** The number of readers waiting. */
+    uint32_t                cReadersWaiting;
+    /** The current number of waiting writers. */
+    uint32_t                cWritersWaiting;
+# if defined(__WIN32__) || defined(__WIN64__)
+    /** The handle of the event object on which the waiting readers block. (manual reset). */
+    HANDLE                  hevReaders;
+    /** The handle of the event object on which the waiting writers block. (manual reset). */
+    HANDLE                  hevWriters;
+# elif defined(__OS2__)
+    /** The handle of the event semaphore on which the waiting readers block. */
+    HEV                     hevReaders;
+    /** The handle of the event semaphore on which the waiting writers block. */
+    HEV                     hevWriters;
+# endif
+    /** The current state of the read-write lock. */
+    KPRF_TYPE(,RWLOCKSTATE) enmState;
+} KPRF_TYPE(,RWLOCK);
+#endif
+/** Pointer to a Read/Write lock. */
+typedef KPRF_TYPE(,RWLOCK) *KPRF_TYPE(P,RWLOCK);
+
+
+
+/*******************************************************************************
+*   Global Variables                                                           *
+*******************************************************************************/
+/** The TLS index / key. */
+#if defined(__WIN32__) || defined(__WIN64__)
+static DWORD                g_dwThreadTLS = TLS_OUT_OF_INDEXES;
+
+#elif defined(KPRF_USE_PTHREAD)
+static pthread_key_t        g_ThreadKey = (pthread_key_t)-1;
+
+#elif defined(__OS2__)
+static KPRF_TYPE(P,THREAD) *g_ppThread = NULL;
+
+#else
+# error "Not ported to your OS - or you're missing the OS define(s)."
+#endif
+
+/** Pointer to the profiler header. */
+static KPRF_TYPE(P,HDR)     g_pHdr = NULL;
+#define KPRF_GET_HDR()                  g_pHdr
+
+/** Whether the profiler is enabled or not. */
+static bool                 g_fEnabled = false;
+#define KPRF_IS_ACTIVE()                g_fEnabled
+
+
+/** The mutex protecting the threads in g_pHdr. */
+static KPRF_TYPE(,MUTEX)   g_ThreadsMutex;
+
+/** The mutex protecting the module segments in g_pHdr. */
+static KPRF_TYPE(,MUTEX)   g_ModSegsMutex;
+
+/** The read-write lock protecting the functions in g_pHdr. */
+static KPRF_TYPE(,RWLOCK)  g_FunctionsRWLock;
+
+
+
+/*******************************************************************************
+*   Internal Functions                                                         *
+*******************************************************************************/
+static KPRF_TYPE(P,THREAD) kPrfGetThreadAutoReg(void);
+#ifdef KPRF_USE_PTHREAD
+static void kPrfPThreadKeyDtor(void *pvThread);
+#endif
+
+
+/**
+ * Gets the pointer to the profiler data for the current thread.
+ *
+ * This implementation automatically adds unknown threads.
+ *
+ * @returns Pointer to the profiler thread data.
+ * @returns NULL if we're out of thread space.
+ */
+static inline KPRF_TYPE(P,THREAD) kPrfGetThread(void)
+{
+    KPRF_TYPE(P,THREAD) pThread;
+
+/* Win32/64 */
+#if defined(__WIN32__) || defined(__WIN64__)
+    pThread = (KPRF_TYPE(P,THREAD))TlsGetValue(g_dwThreadTLS);
+
+/* Posix Threads */
+#elif defined(KPRF_USE_PTHREAD)
+    pThread = (KPRF_TYPE(P,THREAD))pthread_getspecific(g_ThreadKey);
+
+#elif defined(__OS2__)
+    pThread = *g_ppThread;
+
+#else
+# error not implemented
+#endif
+    if (!pThread)
+        pThread = kPrfGetThreadAutoReg();
+    return pThread;
+}
+#define KPRF_GET_THREAD()               kPrfGetThread()
+
+
+/**
+ * The the ID of the current thread.
+ *
+ * @returns The thread id.
+ */
+static inline uintptr_t kPrfGetThreadId(void)
+{
+/* Win32/64 */
+#if defined(__WIN32__) || defined(__WIN64__)
+    uintptr_t ThreadId = (uintptr_t)GetCurrentThreadId();
+
+/* Posix Threads */
+#elif defined(KPRF_USE_PTHREAD)
+    uintptr_t ThreadId = (uintptr_t)pthread_self();
+
+#elif defined(__OS2__)
+    PTIB pTib;
+    PPIB pPib;
+    DosGetInfoBlocks(&pTib, &pPib);
+    ThreadId = pTib->tib_ptib2->tib2_ultid;
+
+#else
+# error not implemented
+#endif
+
+    return ThreadId;
+}
+#define KPRF_GET_THREADID()             kPrfGetThreadId()
+
+
+/**
+ * The the ID of the current process.
+ *
+ * @returns The process id.
+ */
+static inline uintptr_t kPrfGetProcessId(void)
+{
+/* Win32/64 */
+#if defined(__WIN32__) || defined(__WIN64__)
+    uintptr_t ThreadId = (uintptr_t)GetProcessId(GetCurrentProcess());
+
+#elif defined(__OS2__)
+    PTIB pTib;
+    PPIB pPib;
+    DosGetInfoBlocks(&pTib, &pPib);
+    ThreadId = pPib->pib_pid;
+
+#else
+    uintptr_t ThreadId = (uintptr_t)getpid();
+#endif
+
+    return ThreadId;
+}
+#define KPRF_GET_PROCESSID()            kPrfGetProcessId()
+
+
+/**
+ * Sets the pointer to the profiler data for the current thread.
+ *
+ * We require fast access to the profiler thread data, so we store
+ * it in a TLS (thread local storage) item/key where the implementation
+ * allows that.
+ *
+ * @param   pThread         The pointer to the profiler thread data for the current thread.
+ */
+static inline void kPrfSetThread(KPRF_TYPE(P,THREAD) pThread)
+{
+/* Win32/64 */
+#if defined(__WIN32__) || defined(__WIN64__)
+    BOOL fRc = TlsSetValue(g_dwThreadTLS, pThread);
+
+/* Posix Threads */
+#elif defined(KPRF_USE_PTHREAD)
+    int rc = pthread_setspecific(g_ThreadKey, pThread);
+
+#elif defined(__OS2__)
+    *g_ppThread = pThread;
+
+#else
+# error not implemented
+#endif
+}
+#define KPRF_SET_THREAD(pThread)        kPrfSetThread(pThread)
+
+
+/**
+ * Get the now timestamp.
+ * This must correspond to what the assembly code are doing.
+ */
+static inline uint64_t kPrfNow(void)
+{
+#if defined(HAVE_INTRIN)
+    return __rdtsc();
+# else
+    union
+    {
+        uint64_t u64;
+        struct
+        {
+            uint32_t u32Lo;
+            uint32_t u32Hi;
+        } s;
+    } u;
+# if defined(__GNUC__)
+    __asm__ __volatile__ ("rdtsc\n\t" : "=a" (u.s.u32Lo), "=d" (u.s.u32Hi));
+# else
+    __asm
+    {
+        rdtsc
+        mov     [u.s.u32Lo], eax
+        mov     [u.s.u32Hi], edx
+    }
+
+# endif
+    return u.u64;
+#endif
+}
+#define KPRF_NOW()                      kPrfNow()
+
+
+/**
+ * Atomically set a 32-bit value.
+ */
+static inline void kPrfAtomicSet32(volatile uint32_t *pu32, const uint32_t u32)
+{
+#if defined(HAVE_INTRIN)
+    _InterlockedExchange((int32_t *)pu32, (const int32_t)u32);
+
+#elif defined(__GNUC__)
+    __asm__ __volatile__("xchgl %0, %1\n\t"
+                         : "=m" (*pu32)
+                         : "r" (u32));
+
+#elif _MSC_VER
+    __asm
+    {
+        mov     edx, [pu32]
+        mov     eax, [u32]
+        xchg    [edx], eax
+    }
+
+#else
+    *pu32 = u32;
+#endif
+}
+#define KPRF_ATOMIC_SET32(a,b)          kPrfAtomicSet32(a, b)
+
+
+
+/**
+ * Atomically set a 64-bit value.
+ */
+static inline void kPrfAtomicSet64(volatile uint64_t *pu64, uint64_t u64)
+{
+#if defined(HAVE_INTRIN) && KPRF_BITS == 64
+    _InterlockedExchange64((int64_t *)pu64, (const int64_t)u64);
+
+#elif defined(__GNUC__) && KPRF_BITS == 64
+    __asm__ __volatile__("xchgq %0, %1\n\t"
+                         : "=m" (*pu64)
+                         : "r" (u64));
+
+#elif defined(__GNUC__) && KPRF_BITS == 32
+    __asm__ __volatile__("1:\n\t"
+                         "lock; cmpxchg8b %1\n\t"
+                         "jnz 1b\n\t"
+                         : "=A" (u64),
+                           "=m" (*pu64)
+                         : "0" (*pu64),
+                           "b" ( (uint32_t)u64 ),
+                           "c" ( (uint32_t)(u64 >> 32) ));
+
+#elif _MSC_VER
+    __asm
+    {
+        mov     ebx, dword ptr [u64]
+        mov     ecx, dword ptr [u64 + 4]
+        mov     esi, pu64
+        mov     eax, dword ptr [esi]
+        mov     edx, dword ptr [esi + 4]
+    retry:
+        lock cmpxchg8b [esi]
+        jnz retry
+    }
+#else
+    *pu64 = u64;
+#endif
+}
+#define KPRF_ATOMIC_SET64(a,b)          kPrfAtomicSet64(a, b)
+
+
+/**
+ * Atomically add a 32-bit integer to another.
+ */
+static inline void kPrfAtomicAdd32(volatile uint32_t *pu32, const uint32_t u32)
+{
+#if defined(HAVE_INTRIN)
+    _InterlockedExchangeAdd32((volatile int32_t *)pu32, (const int32_t)u32);
+
+#elif defined(__GNUC__)
+    __asm__ __volatile__("lock; addl %0, %1\n\t"
+                         : "=m" (*pu32)
+                         : "r" (u32));
+
+#elif _MSC_VER
+    __asm
+    {
+        mov     edx, [pu32]
+        mov     eax, dword ptr [u32]
+        lock add [edx], eax
+    }
+
+#else
+    *pu32 += u32;
+#endif
+}
+#define KPRF_ATOMIC_ADD32(a,b)          kPrfAtomicAdd32(a, b)
+#define KPRF_ATOMIC_INC32(a)            kPrfAtomicAdd32(a, 1);
+#define KPRF_ATOMIC_DEC32(a)            kPrfAtomicAdd32(a, (uint32_t)-1);
+
+
+/**
+ * Atomically add a 64-bit integer to another.
+ * Atomically isn't quite required, just a non-corruptive manner, assuming all updates are adds.
+ */
+static inline void kPrfAtomicAdd64(volatile uint64_t *pu64, const uint64_t u64)
+{
+#if defined(HAVE_INTRIN) && KPRF_BITS == 64
+    _InterlockedExchangeAdd64((volatile int64_t *)pu64, (const int64_t)u64);
+
+#elif defined(__GNUC__) && KPRF_BITS == 64
+    __asm__ __volatile__("lock; addq %0, %1\n\t"
+                         : "=m" (*pu64)
+                         : "r" (u64));
+
+#elif defined(__GNUC__) && KPRF_BITS == 32
+    __asm__ __volatile__("lock; addl %0, %2\n\t"
+                         "lock; adcl %1, %3\n\t"
+                         : "=m" (*(volatile uint32_t *)pu64),
+                           "=m" (*((volatile uint32_t *)pu64 + 1))
+                         : "r" ((uint32_t)u64),
+                           "r" ((uint32_t)(u64 >> 32)));
+
+#elif _MSC_VER
+    __asm
+    {
+        mov     edx, [pu64]
+        mov     eax, dword ptr [u64]
+        mov     ecx, dword ptr [u64 + 4]
+        lock add [edx], eax
+        lock adc [edx + 4], ecx
+    }
+
+#else
+    *pu64 += u64;
+#endif
+}
+#define KPRF_ATOMIC_ADD64(a,b)          kPrfAtomicAdd64(a, b)
+#define KPRF_ATOMIC_INC64(a)            kPrfAtomicAdd64(a, 1);
+
+
+/**
+ * Initializes a mutex.
+ *
+ * @returns 0 on success.
+ * @returns -1 on failure.
+ * @param   pMutex      The mutex to init.
+ */
+static int kPrfMutexInit(KPRF_TYPE(P,MUTEX) pMutex)
+{
+#if defined(KPRF_USE_PTHREAD)
+    if (!pthread_mutex_init(pMutex, NULL));
+        return 0;
+    return -1;
+
+#elif defined(__WIN32__) || defined(__WIN64__)
+    InitializeCriticalSection(pMutex);
+    return 0;
+
+#elif defined(__OS2__)
+    if (!_fmutex_create(pMutex, 0))
+        return 0;
+    return -1;
+#endif
+}
+
+/**
+ * Deletes a mutex.
+ *
+ * @param   pMutex      The mutex to delete.
+ */
+static void kPrfMutexDelete(KPRF_TYPE(P,MUTEX) pMutex)
+{
+#if defined(KPRF_USE_PTHREAD)
+    pthread_mutex_destroy(pMutex);
+
+#elif defined(__WIN32__) || defined(__WIN64__)
+    DeleteCriticalSection(pMutex);
+
+#elif defined(__OS2__)
+    _fmutex_close(pMutex);
+#endif
+}
+
+/**
+ * Locks a mutex.
+ * @param   pMutex      The mutex lock.
+ */
+static inline void kPrfMutexAcquire(KPRF_TYPE(P,MUTEX) pMutex)
+{
+#if defined(__WIN32__) || defined(__WIN64__)
+    EnterCriticalSection(pMutex);
+
+#elif defined(KPRF_USE_PTHREAD)
+    pthread_mutex_lock(pMutex);
+
+#elif defined(__OS2__)
+    fmutex_request(pMutex);
+#endif
+}
+
+
+/**
+ * Unlocks a mutex.
+ * @param   pMutex      The mutex lock.
+ */
+static inline void kPrfMutexRelease(KPRF_TYPE(P,MUTEX) pMutex)
+{
+#if defined(__WIN32__) || defined(__WIN64__)
+    LeaveCriticalSection(pMutex);
+
+#elif defined(KPRF_USE_PTHREAD)
+    pthread_mutex_lock(pMutex);
+
+#elif defined(__OS2__)
+    fmutex_request(pMutex);
+#endif
+}
+
+
+#define KPRF_THREADS_LOCK()             kPrfMutexAcquire(&g_ThreadsMutex)
+#define KPRF_THREADS_UNLOCK()           kPrfMutexRelease(&g_ThreadsMutex)
+
+#define KPRF_MODSEGS_LOCK()             kPrfMutexAcquire(&g_ModSegsMutex)
+#define KPRF_MODSEGS_UNLOCK()           kPrfMutexRelease(&g_ModSegsMutex)
+
+
+/**
+ * Initializes a read-write lock.
+ *
+ * @returns 0 on success.
+ * @returns -1 on failure.
+ * @param   pRWLock     The read-write lock to initialize.
+ */
+static inline int kPrfRWLockInit(KPRF_TYPE(P,RWLOCK) pRWLock)
+{
+#if defined(KPRF_USE_PTHREAD)
+    if (!pthread_rwlock_init(pRWLock, NULL))
+        return 0;
+    return -1;
+
+#elif defined(__WIN32__) || defined(__WIN64__) || defined(__OS2__)
+    if (kPrfMutexInit(&pRWLock->Mutex))
+        return -1;
+    pRWLock->cReaders = 0;
+    pRWLock->cReadersWaiting = 0;
+    pRWLock->cWritersWaiting = 0;
+    pRWLock->enmState = RWLOCK_STATE_SHARED;
+# if defined(__WIN32__) || defined(__WIN64__)
+    pRWLock->hevReaders = CreateEvent(NULL, TRUE, TRUE, NULL);
+    pRWLock->hevWriters = CreateEvent(NULL, FALSE, FALSE, NULL);
+    if (    pRWLock->hevReaders != INVALID_HANDLE_VALUE
+        &&  pRWLock->hevWriters != INVALID_HANDLE_VALUE)
+        return 0;
+    CloseHandle(pRWLock->hevReaders);
+    CloseHandle(pRWLock->hevWriters);
+
+# elif defined(__OS2__)
+    APIRET rc = DosCreateEventSem(NULL, &pRWLock->hevReaders, 0, TRUE);
+    if (!rc)
+    {
+        rc = DosCreateEventSem(NULL, &pRWLock->hevWriters, 0, TRUE);
+        if (!rc)
+            return 0;
+        pRWLock->hevWriters = NULLHANDLE;
+        DosCloseEventSem(pRWLock->hevReaders);
+    }
+    pRWLock->hevReaders = NULLHANDLE;
+# endif
+
+    pRWLock->enmState = RWLOCK_STATE_UNINITIALIZED;
+    kPrfMutexDelete(&pRWLock->Mutex);
+    return -1;
+#endif
+}
+
+
+/**
+ * Deleters a read-write lock.
+ *
+ * @param   pRWLock     The read-write lock to delete.
+ */
+static inline void kPrfRWLockDelete(KPRF_TYPE(P,RWLOCK) pRWLock)
+{
+#if defined(KPRF_USE_PTHREAD)
+    pthread_rwlock_destroy(pRWLock);
+
+#elif defined(__WIN32__) || defined(__WIN64__) || defined(__OS2__)
+    if (pRWLock->enmState == RWLOCK_STATE_UNINITIALIZED)
+        return;
+
+    pRWLock->enmState = RWLOCK_STATE_UNINITIALIZED;
+    kPrfMutexDelete(&pRWLock->Mutex);
+    pRWLock->cReaders = 0;
+    pRWLock->cReadersWaiting = 0;
+    pRWLock->cWritersWaiting = 0;
+# if defined(__WIN32__) || defined(__WIN64__)
+    CloseHandle(pRWLock->hevReaders);
+    pRWLock->hevReaders = INVALID_HANDLE_VALUE;
+    CloseHandle(pRWLock->hevWriters);
+    pRWLock->hevWriters = INVALID_HANDLE_VALUE;
+
+# elif defined(__OS2__)
+    DosCloseEventSem(pRWLock->hevReaders);
+    pRWLock->hevReaders = NULLHANDLE;
+    DosCloseEventSem(pRWLock->hevWriters);
+    pRWLock->hevWriters = NULLHANDLE;
+# endif
+#endif
+}
+
+
+/**
+ * Acquires read access to the read-write lock.
+ * @param   pRWLock     The read-write lock.
+ */
+static inline void kPrfRWLockAcquireRead(KPRF_TYPE(P,RWLOCK) pRWLock)
+{
+#if defined(KPRF_USE_PTHREAD)
+    pthread_rwlock_rdlock(pRWLock);
+
+#elif defined(__WIN32__) || defined(__WIN64__) || defined(__OS2__)
+    if (pRWLock->enmState == RWLOCK_STATE_UNINITIALIZED)
+        return;
+
+    kPrfMutexAcquire(&pRWLock->Mutex);
+    if (pRWLock->enmState == RWLOCK_STATE_SHARED)
+    {
+        KPRF_ATOMIC_INC32(&pRWLock->cReaders);
+        kPrfMutexRelease(&pRWLock->Mutex);
+        return;
+    }
+
+    for (;;)
+    {
+        /* have to wait */
+        KPRF_ATOMIC_INC32(&pRWLock->cReadersWaiting);
+# if defined(__WIN32__) || defined(__WIN64__)
+        HANDLE hev = pRWLock->hevReaders;
+        ResetEvent(hev);
+
+# elif defined(__OS2__)
+        HEV    hev = pRWLock->hevReaders;
+        ULONG cIgnored;
+        DosResetEventSem(hev, &cIgnored);
+
+# endif
+        kPrfMutexRelease(&pRWLock->Mutex);
+
+# if defined(__WIN32__) || defined(__WIN64__)
+        switch (WaitForSingleObject(hev, INFINITE))
+        {
+            case WAIT_IO_COMPLETION:
+            case WAIT_TIMEOUT:
+            case WAIT_OBJECT_0:
+                break;
+            case WAIT_ABANDONED:
+            default:
+                return;
+        }
+
+# elif defined(__OS2__)
+        switch (DosWaitEventSem(hev, SEM_INDEFINITE_WAIT))
+        {
+            case NO_ERROR:
+            case ERROR_SEM_TIMEOUT:
+            case ERROR_TIMEOUT:
+            case ERROR_INTERRUPT:
+                break;
+            default:
+                return;
+        }
+# endif
+
+        kPrfMutexAcquire(&pRWLock->Mutex);
+        if (pRWLock->enmState == RWLOCK_STATE_SHARED)
+        {
+            KPRF_ATOMIC_INC32(&pRWLock->cReaders);
+            KPRF_ATOMIC_DEC32(&pRWLock->cReadersWaiting);
+            kPrfMutexRelease(&pRWLock->Mutex);
+            return;
+        }
+    }
+#endif
+}
+
+
+/**
+ * Releases read access to the read-write lock.
+ * @param   pRWLock     The read-write lock.
+ */
+static inline void kPrfRWLockReleaseRead(KPRF_TYPE(P,RWLOCK) pRWLock)
+{
+#if defined(KPRF_USE_PTHREAD)
+    pthread_rwlock_unlock(pRWLock);
+
+#elif defined(__WIN32__) || defined(__WIN64__) || defined(__OS2__)
+    if (pRWLock->enmState == RWLOCK_STATE_UNINITIALIZED)
+        return;
+
+    /*
+     * If we're still in the shared state, or if there
+     * are more readers out there, or if there are no
+     * waiting writers, all we have to do is decrement an leave.
+     *
+     * That's the most frequent, thing and should be fast.
+     */
+    kPrfMutexAcquire(&pRWLock->Mutex);
+    KPRF_ATOMIC_DEC32(&pRWLock->cReaders);
+    if (    pRWLock->enmState == RWLOCK_STATE_SHARED
+        ||  pRWLock->cReaders
+        ||  !pRWLock->cWritersWaiting)
+    {
+        kPrfMutexRelease(&pRWLock->Mutex);
+        return;
+    }
+
+    /*
+     * Wake up one (or more on OS/2) waiting writers.
+     */
+# if defined(__WIN32__) || defined(__WIN64__)
+    SetEvent(pRWLock->hevWriters);
+# elif defined(__OS2__)
+    DosPostEvent(pRWLock->hevwriters);
+# endif
+    kPrfMutexRelease(&pRWLock->Mutex);
+
+#endif
+}
+
+
+/**
+ * Acquires write access to the read-write lock.
+ * @param   pRWLock     The read-write lock.
+ */
+static inline void kPrfRWLockAcquireWrite(KPRF_TYPE(P,RWLOCK) pRWLock)
+{
+#if defined(KPRF_USE_PTHREAD)
+    pthread_rwlock_wrlock(pRWLock);
+
+#elif defined(__WIN32__) || defined(__WIN64__) || defined(__OS2__)
+    if (pRWLock->enmState == RWLOCK_STATE_UNINITIALIZED)
+        return;
+
+    kPrfMutexAcquire(&pRWLock->Mutex);
+    if (    !pRWLock->cReaders
+        &&  (   pRWLock->enmState == RWLOCK_STATE_SHARED
+             || pRWLock->enmState == RWLOCK_STATE_LOCKING)
+        )
+    {
+        KPRF_RWLOCK_SETSTATE(pRWLock, RWLOCK_STATE_EXCLUSIVE);
+        kPrfMutexRelease(&pRWLock->Mutex);
+        return;
+    }
+
+    /*
+     * We'll have to wait.
+     */
+    if (pRWLock->enmState == RWLOCK_STATE_SHARED)
+        KPRF_RWLOCK_SETSTATE(pRWLock, RWLOCK_STATE_LOCKING);
+    KPRF_ATOMIC_INC32(&pRWLock->cWritersWaiting);
+    for (;;)
+    {
+# if defined(__WIN32__) || defined(__WIN64__)
+        HANDLE hev = pRWLock->hevWriters;
+# elif defined(__OS2__)
+        HEV    hev = pRWLock->hevWriters;
+# endif
+        kPrfMutexRelease(&pRWLock->Mutex);
+# if defined(__WIN32__) || defined(__WIN64__)
+        switch (WaitForSingleObject(hev, INFINITE))
+        {
+            case WAIT_IO_COMPLETION:
+            case WAIT_TIMEOUT:
+            case WAIT_OBJECT_0:
+                break;
+            case WAIT_ABANDONED:
+            default:
+                KPRF_ATOMIC_DEC32(&pRWLock->cWritersWaiting);
+                return;
+        }
+
+# elif defined(__OS2__)
+        switch (DosWaitEventSem(hev, SEM_INDEFINITE_WAIT))
+        {
+            case NO_ERROR:
+            case ERROR_SEM_TIMEOUT:
+            case ERROR_TIMEOUT:
+            case ERROR_INTERRUPT:
+                break;
+            default:
+                KPRF_ATOMIC_DEC32(&pRWLock->cWritersWaiting);
+                return;
+        }
+        ULONG cIgnored;
+        DosResetEventSem(hev, &cIgnored);
+# endif
+
+        /*
+         * Try acquire the lock.
+         */
+        kPrfMutexAcquire(&pRWLock->Mutex);
+        if (    !pRWLock->cReaders
+            &&  (   pRWLock->enmState == RWLOCK_STATE_SHARED
+                 || pRWLock->enmState == RWLOCK_STATE_LOCKING)
+            )
+        {
+            KPRF_RWLOCK_SETSTATE(pRWLock, RWLOCK_STATE_EXCLUSIVE);
+            KPRF_ATOMIC_DEC32(&pRWLock->cWritersWaiting);
+            kPrfMutexRelease(&pRWLock->Mutex);
+            return;
+        }
+    }
+#endif
+}
+
+
+/**
+ * Releases write access to the read-write lock.
+ * @param   pRWLock     The read-write lock.
+ */
+static inline void kPrfRWLockReleaseWrite(KPRF_TYPE(P,RWLOCK) pRWLock)
+{
+#if defined(KPRF_USE_PTHREAD)
+    pthread_rwlock_unlock(pRWLock);
+
+#elif defined(__WIN32__) || defined(__WIN64__) || defined(__OS2__)
+    if (pRWLock->enmState == RWLOCK_STATE_UNINITIALIZED)
+        return;
+
+    /*
+     * The common thing is that there are noone waiting.
+     * But, before that usual paranoia.
+     */
+    kPrfMutexAcquire(&pRWLock->Mutex);
+    if (pRWLock->enmState != RWLOCK_STATE_EXCLUSIVE)
+    {
+        kPrfMutexRelease(&pRWLock->Mutex);
+        return;
+    }
+    if (    !pRWLock->cReadersWaiting
+        &&  !pRWLock->cWritersWaiting)
+    {
+        KPRF_RWLOCK_SETSTATE(pRWLock, RWLOCK_STATE_SHARED);
+        kPrfMutexRelease(&pRWLock->Mutex);
+        return;
+    }
+
+    /*
+     * Someone is waiting, wake them up as we change the state.
+     */
+# if defined(__WIN32__) || defined(__WIN64__)
+    HANDLE hev = INVALID_HANDLE_VALUE;
+# elif defined(__OS2__)
+    HEV    hev = NULLHANDLE;
+# endif
+
+    if (pRWLock->cWritersWaiting)
+    {
+        KPRF_RWLOCK_SETSTATE(pRWLock, RWLOCK_STATE_LOCKING);
+        hev = pRWLock->hevWriters;
+    }
+    else
+    {
+        KPRF_RWLOCK_SETSTATE(pRWLock, RWLOCK_STATE_SHARED);
+        hev = pRWLock->hevReaders;
+    }
+# if defined(__WIN32__) || defined(__WIN64__)
+    SetEvent(hev);
+# elif defined(__OS2__)
+    DosPostEvent(pRWLock->hevwriters);
+# endif
+    kPrfMutexRelease(&pRWLock->Mutex);
+
+#endif
+}
+
+#define KPRF_FUNCS_WRITE_LOCK()         kPrfRWLockAcquireWrite(&g_FunctionsRWLock)
+#define KPRF_FUNCS_WRITE_UNLOCK()       kPrfRWLockReleaseWrite(&g_FunctionsRWLock)
+#define KPRF_FUNCS_READ_LOCK()          kPrfRWLockAcquireRead(&g_FunctionsRWLock)
+#define KPRF_FUNCS_READ_UNLOCK()        kPrfRWLockReleaseRead(&g_FunctionsRWLock)
+
+
+
+
+/**
+ * Finds the module segment which the address belongs to.
+ *
+ */
+static int kPrfGetModSeg(KPRF_TYPE(,UPTR) uAddress, char *pszPath, uint32_t cchPath, uint32_t *piSegment,
+                         KPRF_TYPE(P,UPTR) puBasePtr, KPRF_TYPE(P,UPTR) pcbSegmentMinusOne)
+{
+#if defined(__WIN32__) || defined(__WIN64__)
+    /*
+     * Enumerate the module handles.
+     */
+    HANDLE      hProcess = GetCurrentProcess();
+    DWORD       cbNeeded = 0;
+    HMODULE     hModIgnored;
+    if (    !EnumProcessModules(hProcess, &hModIgnored, sizeof(hModIgnored), &cbNeeded)
+        &&  GetLastError() != ERROR_BUFFER_OVERFLOW) /** figure out what this actually returns */
+        cbNeeded = 256 * sizeof(HMODULE);
+
+    cbNeeded += sizeof(HMODULE) * 32;
+    HMODULE *pahModules = (HMODULE *)alloca(cbNeeded);
+    if (EnumProcessModules(hProcess, pahModules, cbNeeded, &cbNeeded))
+    {
+        const unsigned cModules = cbNeeded / sizeof(HMODULE);
+        for (unsigned i = 0; i < cModules; i++)
+        {
+            __try
+            {
+                const uintptr_t uImageBase = (uintptr_t)pahModules[i];
+                union
+                {
+                    uint8_t            *pu8;
+                    PIMAGE_DOS_HEADER   pDos;
+                    PIMAGE_NT_HEADERS   pNt;
+                    PIMAGE_NT_HEADERS32 pNt32;
+                    PIMAGE_NT_HEADERS64 pNt64;
+                    uintptr_t           u;
+                } u;
+                u.u = uImageBase;
+
+                /* reject modules higher than the address. */
+                if (uAddress < u.u)
+                    continue;
+
+                /* Skip past the MZ header */
+                if (u.pDos->e_magic == IMAGE_DOS_SIGNATURE)
+                    u.pu8 += u.pDos->e_lfanew;
+
+                /* Ignore anything which isn't an NT header. */
+                if (u.pNt->Signature != IMAGE_NT_SIGNATURE)
+                    continue;
+
+                /* Extract necessary info from the optional header (comes in 32-bit and 64-bit variations, we simplify a bit). */
+                uint32_t                cbImage;
+                PIMAGE_SECTION_HEADER   paSHs;
+                if (u.pNt->FileHeader.SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER32))
+                {
+                    paSHs = (PIMAGE_SECTION_HEADER)(u.pNt32 + 1);
+                    cbImage = u.pNt32->OptionalHeader.SizeOfImage;
+                }
+                else if (u.pNt->FileHeader.SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER64))
+                {
+                    paSHs = (PIMAGE_SECTION_HEADER)(u.pNt64 + 1);
+                    cbImage = u.pNt64->OptionalHeader.SizeOfImage;
+                }
+                else
+                    continue;
+
+                /* Is our address within the image size */
+                uintptr_t uRVA = uAddress - (uintptr_t)pahModules[i];
+                if (uRVA >= cbImage)
+                    continue;
+
+                /*
+                 * Iterate the section headers and figure which section we're in.
+                 * (segment == section + 1)
+                 */
+                const uint32_t  cSHs = u.pNt->FileHeader.NumberOfSections;
+                if (uRVA < paSHs[0].VirtualAddress)
+                {
+                    /* the implicit header section */
+                    *puBasePtr          = uImageBase;
+                    *pcbSegmentMinusOne = paSHs[0].VirtualAddress - 1;
+                    *piSegment          = 0;
+                }
+                else
+                {
+                    uint32_t iSH = 0;
+                    for (;;)
+                    {
+                        if (iSH >= cSHs)
+                        {
+                            /* this shouldn't happen, but in case it does simply deal with it. */
+                            *puBasePtr          = paSHs[iSH - 1].VirtualAddress + paSHs[iSH - 1].Misc.VirtualSize + uImageBase;
+                            *pcbSegmentMinusOne = cbImage - *puBasePtr;
+                            *piSegment          = iSH + 1;
+                            break;
+                        }
+                        if (uRVA - paSHs[iSH].VirtualAddress < paSHs[iSH].Misc.VirtualSize)
+                        {
+                            *puBasePtr          = paSHs[iSH].VirtualAddress + uImageBase;
+                            *pcbSegmentMinusOne = paSHs[iSH].Misc.VirtualSize;
+                            *piSegment          = iSH + 1;
+                            break;
+                        }
+                        iSH++;
+                    }
+                }
+
+                /*
+                 * Finally, get the module name.
+                 * There are multiple ways, try them all before giving up.
+                 */
+                if (    !GetModuleFileNameEx(hProcess, pahModules[i], pszPath, cchPath)
+                    &&  !GetModuleFileName(pahModules[i], pszPath, cchPath)
+                    &&  !GetMappedFileName(hProcess, (PVOID)uAddress, pszPath, cchPath)
+                    &&  !GetModuleBaseName(hProcess, pahModules[i], pszPath, cchPath))
+                    *pszPath = '\0';
+                return 0;
+            }
+            __except (EXCEPTION_EXECUTE_HANDLER)
+            {
+            }
+        }
+    }
+
+#elif defined(__OS2__)
+    /*
+     * Just ask the loader.
+     */
+    ULONG   offObj = 0;
+    ULONG   iObj = 0;
+    HMODULE hmod = NULLHANDLE;
+    APIRET rc = DosQueryModFromEIP(&hmod, &iObj, cchPath, pszPath, &offObj, uAddress);
+    if (!rc)
+    {
+        *piSegment = iObj;
+        *puBasePtr = uAddress - offObj;
+        *pcbSegmentMinusOne = KPRF_ALIGN(offObj, 0x1000) - 1; /* minimum size */
+
+        /*
+         * Query the page attributes starting at the current page. The query will not enter
+         * into the next object since PAG_BASE is requested.
+         */
+        ULONG cb     = ~0UL;
+        ULONG fFlags = ~0UL;
+        uAddress &= ~(uintptr_t)0xfff;
+        rc = DosQueryMem((PVOID)(uAddress, &cb, &fFlags);
+        if (!rc)
+        {
+            *pcbSegmentMinusOne = (offObj & ~(uintptr_t)0xfff) + KPRF_ALIGN(cb, 0x1000) - 1;
+            if ((fFlags & PAG_BASE) && cb <= 0x1000) /* don't quite remember if PAG_BASE returns one page or not */
+            {
+                cb = ~0UL;
+                fFlags = ~0UL;
+                rc = DosQueryMem((PVOID)(uAddress + 0x1000), &cb, &fFlags);
+                if (!rc & !(fFlags & (PAG_BASE | PAG_FREE)))
+                    *pcbSegmentMinusOne += KPRF_ALIGN(cb, 0x1000);
+            }
+        }
+        return 0;
+    }
+
+#endif
+    /* The common fallback */
+    *pszPath = '\0';
+    *piSegment = 0;
+    *puBasePtr = 0;
+    *pcbSegmentMinusOne = ~(KPRF_TYPE(,UPTR))0;
+    return -1;
+}
+#define KPRF_GET_MODSEG(uAddress, pszPath, cchPath, piSegment, puBasePtr, pcbSegmentMinusOne) \
+                                        kPrfGetModSeg(uAddress, pszPath, cchPath, piSegment, puBasePtr, pcbSegmentMinusOne)
+
+
+
+
+/*
+ * Instantiate the implementation
+ */
+#include "prfcorepre.cpp.h"
+
+#include "prfcoremodseg.cpp.h"
+#include "prfcorefunction.cpp.h"
+#include "prfcore.cpp.h"
+#include "prfcoreinit.cpp.h"
+#include "prfcoreterm.cpp.h"
+
+#include "prfcorepost.cpp.h"
+
+
+
+
+
+/**
+ * Registers an unknown thread.
+ *
+ * @returns Pointer to the registered thread.
+ */
+static KPRF_TYPE(P,THREAD) kPrfGetThreadAutoReg(void)
+{
+    uintptr_t uStackBasePtr;
+
+#if 0
+    /** @todo I'm sure Win32 has a way of obtaining the top and bottom of the stack, OS/2 did...
+     * Some limit stuff in posix / ansi also comes to mind... */
+
+#elif defined(__OS2__)
+    PTIB pTib;
+    PPIB pPib;
+    DosGetInfoBlocks(&pTib, &pPib); /* never fails except if you give it bad input, thus 'Get' not 'Query'. */
+    /* I never recall which of these is the right one... */
+    uStackBasePtr = (uintptr_t)pTib->tib_pstack < (uintptr_t)pTib->tib_pstack_limit
+        ? (uintptr_t)pTib->tib_pstack
+        : (uintptr_t)pTib->tib_pstack_limit;
+
+#else
+    /* the default is top of the current stack page (assuming a page to be 4KB) */
+    uStackBasePtr = (uintptr_t)&uStackBasePtr;
+    uStackBasePtr = (uStackBasePtr + 0xfff) & ~(uintptr_t)0xfff;
+#endif
+
+    return KPRF_NAME(RegisterThread)(uStackBasePtr, "");
+}
+
+
+/**
+ * Get a env.var. variable.
+ *
+ * @returns pszValue.
+ * @param   pszVar      The variable name.
+ * @param   pszValue    Where to store the value.
+ * @param   cchValue    The size of the value buffer.
+ * @param   pszDefault  The default value.
+ */
+static char *kPrfGetEnvString(const char *pszVar, char *pszValue, uint32_t cchValue, const char *pszDefault)
+{
+#if defined(__WIN32__) || defined(__WIN64__)
+    if (GetEnvironmentVariable(pszVar, pszValue, cchValue))
+        return pszValue;
+
+#elif defined(__OS2__)
+    PSZ pszValue;
+    if (    !DosScanEnv((PCSZ)pszVar, &pszValue)
+        &&  !*pszValue)
+        pszDefault = pszValue;
+
+#else
+    const char *pszTmp = getenv(pszVar);
+    if (pszTmp)
+        pszDefault = pszTmp;
+
+#endif
+
+    /*
+     * Copy the result into the buffer.
+     */
+    char *psz = pszValue;
+    while (*pszDefault && cchValue-- > 1)
+        *psz++ = *pszDefault++;
+    *psz = '\0';
+
+    return pszValue;
+}
+
+
+/**
+ * The the value of an env.var.
+ *
+ * @returns The value of the env.var.
+ * @returns The default if the value was not found.
+ * @param   pszVar      The variable name.
+ * @param   uDefault    The default value.
+ */
+static uint32_t kPrfGetEnvValue(const char *pszVar, uint32_t uDefault)
+{
+#if defined(__WIN32__) || defined(__WIN64__)
+    char szBuf[128];
+    const char *pszValue = szBuf;
+    if (!GetEnvironmentVariable(pszVar, szBuf, sizeof(szBuf)))
+        pszValue = NULL;
+
+#elif defined(__OS2__)
+    PSZ pszValue;
+    if (DosScanEnv((PCSZ)pszVar, &pszValue))
+        pszValue = NULL;
+
+#else
+    const char *pszValue = getenv(pszVar);
+
+#endif
+
+    /*
+     * Discard the obvious stuff.
+     */
+    if (!pszValue)
+        return uDefault;
+    while (*pszValue == ' ' || *pszValue == '\t')
+        pszValue++;
+    if (!*pszValue)
+        return uDefault;
+
+    /*
+     * Interpret the value.
+     */
+    unsigned    uBase = 10;
+    uint32_t    uValue = 0;
+    const char *psz = pszValue;
+
+    /* prefix - only hex */
+    if (*psz == '0' && (psz[1] == 'x' || psz[1] == 'X'))
+    {
+        uBase = 16;
+        psz += 2;
+    }
+
+    /* read the value */
+    while (*psz)
+    {
+        unsigned char ch = (unsigned char)*psz;
+        if (ch >= '0' && ch <= '9')
+            ch -= '0';
+        else if (   uBase > 10
+                 && ch >= 'a' && ch <= 'f')
+            ch -= 'a' + 10;
+        else if (   uBase > 10
+                 && ch >= 'a' && ch <= 'F')
+            ch -= 'a' + 10;
+        else
+            break;
+        uValue *= uBase;
+        uValue += ch;
+    }
+
+    /* postfixes */
+    switch (*psz)
+    {
+        case 'm':
+        case 'M':
+            uValue *= 1024*1024;
+            break;
+
+        case 'k':
+        case 'K':
+            uValue *= 1024;
+            break;
+    }
+
+    /*
+     * If the value is still 0, we return the default.
+     */
+    return uValue ? uValue : uDefault;
+}
+
+
+/**
+ * Allocates memory.
+ *
+ * @returns Pointer to the allocated memory.
+ * @returns NULL on failure.
+ * @param   cb      The amount of memory (in bytes) to allocate.
+ */
+static void *kPrfAllocMem(uint32_t cb)
+{
+#if defined(__WIN32__) || defined(__WIN64__)
+    void *pv = VirtualAlloc(NULL, cb, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
+
+#elif defined(KPRF_USE_MMAN)
+    void *pv = mmap(NULL, cb, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+
+#elif defined(__OS2__)
+    void *pv;
+# ifdef INCL_DOSEXAPIS
+    if (DosAllocMemEx(&pv, cb, PAG_READ | PAG_WRITE | PAG_EXECUTE | PAG_COMMIT | OBJ_FORK))s
+# else
+    if (DosAllocMem(&pv, cb, PAG_READ | PAG_WRITE | PAG_EXECUTE | PAG_COMMIT))
+# endif
+        pvBuf = NULL;
+
+#else
+# error not implemented
+#endif
+    return pv;
+}
+
+
+/**
+ * Frees memory.
+ *
+ * @param   pv      The memory to free.
+ */
+static void kPrfFreeMem(void *pv)
+{
+#if defined(__WIN32__) || defined(__WIN64__)
+    VirtualFree(pv, 0, MEM_RELEASE);
+
+#elif defined(KPRF_USE_MMAN)
+    munmap(pv, 0); /** @todo check if 0 is allowed here.. */
+
+#elif defined(__OS2__)
+# ifdef INCL_DOSEXAPIS
+    DosFreeMemEx(&pv);
+# else
+    DosFreeMem(&pv);
+# endif
+
+#else
+# error not implemented
+#endif
+}
+
+
+/**
+ * Writes a data buffer to a new file.
+ *
+ * Any existing file will be overwritten.
+ *
+ *
+ * @returns 0 on success.
+ * @returns -1 on failure.
+ *
+ * @param   pszName     The name of the file.
+ * @param   pvData      The data to write.
+ * @param   cbData      The amount of data to write.
+ */
+static int kPrfWriteFile(const char *pszName, const void *pvData, uint32_t cbData)
+{
+#if defined(__WIN32__) || defined(__WIN64__)
+    int rc = -1;
+    HANDLE hFile = CreateFile(pszName,GENERIC_WRITE, FILE_SHARE_READ, NULL,
+                              CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE);
+    if (hFile != INVALID_HANDLE_VALUE)
+    {
+        DWORD dwWritten;
+        if (    WriteFile(hFile, pvData, cbData, &dwWritten, NULL)
+            &&  dwWritten == cbData)
+            rc = 0;
+        CloseHandle(hFile);
+    }
+    return rc;
+
+#elif defined(__OS2__)
+    HFILE hFile;
+    ULONG ulAction = 0;
+    APIRET rc = DosOpen(pszName, &hFile, &ulAction, cbData, FILE_NORMAL,
+                        OPEN_ACTION_REPLACE_IF_EXISTS | OPEN_ACTION_CREATE_IF_NEW,
+                        OPEN_ACCESS_WRITEONLY | OPEN_SHARE_DENYWRITE | OPEN_FLAGS_NOINHERIT | OPEN_FLAGS_SEQUENTIAL,
+                        NULL);
+    if (!rc)
+    {
+        ULONG cbWritten;
+        rc = DosWrite(hFile, pvData, cbData, &cbWritten);
+        if (!rc && cbWritten != cbData)
+            rc = -1;
+        DosClose(hFile);
+    }
+    return rc ? -1 : 0;
+
+#else
+    int rc = -1;
+    int fd = open(pszName, O_WRONLY | O_CREAT | O_BINARY | O_TRUNC, 0666);
+    if (fd >= 0)
+    {
+        if (write(fd, pvData, cbData) == cbData)
+            rc = 0;
+        close(fd);
+    }
+    return rc;
+
+#endif
+}
+
+
+
+/**
+ * Initializes and start the profiling.
+ *
+ * This should typically be called from some kind of module init
+ * function, so we can start profiling upon/before entering main().
+ *
+ * @returns 0 on success
+ * @returns -1 on failure.
+ *
+ */
+int kPrfInitialize(void)
+{
+    /*
+     * Only initialize once.
+     */
+    if (KPRF_GET_HDR())
+        return 0;
+
+    /*
+     * Initial suggestions.
+     */
+    uint32_t    cbModSegs  = kPrfGetEnvValue("KPRF2_CBMODSEGS",  128*1024);
+    uint32_t    cFunctions = kPrfGetEnvValue("KPRF2_CFUNCTIONS", 8192);
+    uint32_t    cThreads   = kPrfGetEnvValue("KPRF2_CTHREADS",   256);
+    uint32_t    cStacks    = kPrfGetEnvValue("KPRF2_CSTACKS",    48);
+    uint32_t    cFrames    = kPrfGetEnvValue("KPRF2_CFRAMES",    448);
+
+    uint32_t cb = KPRF_NAME(CalcSize)(cFunctions, cbModSegs, cThreads, cStacks, cFrames);
+
+    /*
+     * Allocate and initialize the data set.
+     */
+    void *pvBuf = kPrfAllocMem(cb);
+    if (!pvBuf)
+        return -1;
+
+    KPRF_TYPE(P,HDR) pHdr = KPRF_NAME(Init)(pvBuf, cb, cFunctions, cbModSegs, cThreads, cStacks, cFrames);
+    if (pHdr)
+    {
+        /*
+         * Initialize semaphores.
+         */
+        if (!kPrfMutexInit(&g_ThreadsMutex))
+        {
+            if (!kPrfMutexInit(&g_ModSegsMutex))
+            {
+                if (!kPrfRWLockInit(&g_FunctionsRWLock))
+                {
+                    /*
+                     * Allocate the TLS entry.
+                     */
+#if defined(__WIN32__) || defined(__WIN64__)
+                    g_dwThreadTLS = TlsAlloc();
+                    if (g_dwThreadTLS != TLS_OUT_OF_INDEXES)
+
+#elif defined(KPRF_USE_PTHREAD)
+                    int rc = pthread_key_create(&g_ThreadKey, kPrfPThreadKeyDtor);
+                    if (!rc)
+
+#elif defined(__OS2__)
+                    int rc = DosAllocThreadLocalMemory(sizeof(void *), (PULONG*)&g_ppThread); /** @todo check if this is a count or a size. */
+                    if (!rc)
+
+#endif
+                    {
+                        g_pHdr = pHdr;
+                        g_fEnabled = true;
+                        return 0;
+                    }
+                    kPrfRWLockDelete(&g_FunctionsRWLock);
+                }
+                kPrfMutexDelete(&g_ModSegsMutex);
+            }
+            kPrfMutexDelete(&g_ThreadsMutex);
+        }
+    }
+    kPrfFreeMem(pvBuf);
+    return -1;
+}
+
+
+/**
+ * Stops, dumps, and terminates the profiling.
+ *
+ * This should typically be called from some kind of module destruction
+ * function, so we can profile parts of the termination sequence too.
+ *
+ * @returns 0 on success
+ * @returns -1 on failure.
+ *
+ */
+int kPrfTerminate(void)
+{
+    /*
+     * Stop the profiling.
+     * As a safety precaution, sleep a little bit to allow threads
+     * still at large inside profiler code some time to get out.
+     */
+    g_fEnabled = false;
+    KPRF_TYPE(P,HDR) pHdr = g_pHdr;
+    g_pHdr = NULL;
+    if (!pHdr)
+        return -1;
+
+#if defined(__WIN32__) || defined(__WIN64__)
+    Sleep(10);
+#elif defined(__OS2__)
+    DosSleep(10);
+#else
+    usleep(10000);
+#endif
+
+    /*
+     * Unwind all active threads and so forth.
+     */
+    KPRF_NAME(TerminateAll)(pHdr);
+
+    /*
+     * Use the stack space to fill in process details.
+     */
+#if defined(__WIN32__) || defined(__WIN64__)
+    /* all is one single string */
+    const char *pszCommandLine = GetCommandLine();
+    if (pszCommandLine)
+        KPRF_NAME(SetCommandLine)(pHdr, 1, &pszCommandLine);
+
+#elif defined(__OS2__) || defined(__OS2__)
+    PTIB pTib;
+    PPIB pPib;
+    DosGetInfoBlocks(&pTib, &pPib);
+    if (pPib->pib_pchcmd)
+    {
+        /* Tradition say that the commandline is made up of two zero terminate strings
+         * - first the executable name, then the arguments. Similar to what unix does,
+         *   only completely mocked up because of the CMD.EXE tradition.
+         */
+        const char *apszArgs[2];
+        apszArgs[0] = pPib->pib_pchcmd;
+        apszArgs[1] = pPib->pib_pchcmd;
+        while (apszArgs[1][0])
+            apszArgs[1]++;
+        apszArgs[1]++;
+        KPRF_NAME(SetCommandLine)(pHdr, 2, apszArgs);
+    }
+
+#else
+    /* linux can read /proc/self/something I guess. Don't know about the rest... */
+
+#endif
+
+    /*
+     * Write the file to disk.
+     */
+    char szName[260 + 16];
+    kPrfGetEnvString("KPRF2_FILE", szName, sizeof(szName) - 16, "kPrf2-");
+
+    /* append the process id */
+    uintptr_t pid = kPrfGetProcessId();
+    char *psz = szName;
+    while (*psz)
+        psz++;
+
+    static char s_szDigits[0x11] = "0123456789abcdef";
+    uint32_t uShift = KPRF_BITS - 4;
+    while (     uShift > 0
+           &&   !(pid & (0xf << uShift)))
+        uShift -= 4;
+    *psz++ = s_szDigits[(pid >> uShift) & 0xf];
+     while (uShift > 0)
+     {
+         uShift -= 4;
+         *psz++ = s_szDigits[(pid >> uShift) & 0xf];
+     }
+
+    /* .kPrf2 */
+    *psz++ = '.';
+    *psz++ = 'k';
+    *psz++ = 'P';
+    *psz++ = 'r';
+    *psz++ = 'f';
+    *psz++ = '2';
+    *psz++ = '\0';
+
+    /* write the file. */
+    int rc = kPrfWriteFile(szName, pHdr, pHdr->cb);
+
+    /*
+     * Free resources.
+     */
+    kPrfFreeMem(pHdr);
+#if defined(__WIN32__) || defined(__WIN64__)
+    TlsFree(g_dwThreadTLS);
+    g_dwThreadTLS = TLS_OUT_OF_INDEXES;
+
+#elif defined(KPRF_USE_PTHREAD)
+    pthread_key_delete(g_ThreadKey);
+    g_ThreadKey = (pthread_key_t)-1;
+
+#elif defined(__OS2__)
+    DosFreeThreadLocalMemory((PULONG)g_ppThread);
+    g_ppThread = NULL;
+
+#else
+# error "port me!"
+#endif
+
+    kPrfMutexDelete(&g_ThreadsMutex);
+    kPrfMutexDelete(&g_ModSegsMutex);
+    kPrfRWLockDelete(&g_FunctionsRWLock);
+
+    return rc;
+}
+
+
+/**
+ * Terminate the current thread.
+ */
+void kPrfTerminateThread(void)
+{
+    KPRF_NAME(DeregisterThread)();
+}
+
+
+#ifdef KPRF_USE_PTHREAD
+/**
+ * TLS destructor.
+ */
+static void kPrfPThreadKeyDtor(void *pvThread)
+{
+    KPRF_TYPE(P,HDR) pHdr = KPRF_GET_HDR();
+    if (pHdr)
+    {
+        KPRF_TYPE(P,THREAD) pThread = (KPRF_TYPE(P,THREAD))pvThread;
+        pthread_setspecific(g_ThreadKey, pvThread);
+        KPRF_NAME(TerminateThread)(pHdr, pThread, KPRF_NOW());
+        pthread_setspecific(g_ThreadKey, NULL);
+    }
+}
+#endif
+
Index: trunk/kProfile/kProfileR3.h
===================================================================
--- trunk/kProfile/kProfileR3.h	(revision 3524)
+++ trunk/kProfile/kProfileR3.h	(revision 3524)
@@ -0,0 +1,36 @@
+/* $Id: $ */
+/** @file
+ *
+ * kProfiler Mark 2 - Internal header, Ring-3.
+ *
+ * Copyright (c) 2006 knut st. osmundsen <bird-src-spam@anduin.net.de>
+ *
+ *
+ * This file is part of kLIBC.
+ *
+ * kLIBC is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * kLIBC is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with kLIBC; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef __kProfileR3_h__
+#define __kProfileR3_h__
+
+int kPrfInitialize(void);
+int kPrfTerminate(void);
+void kPrfTerminateThread(void);
+
+#endif
+
+
Index: trunk/kProfile/prfcore.cpp.h
===================================================================
--- trunk/kProfile/prfcore.cpp.h	(revision 3524)
+++ trunk/kProfile/prfcore.cpp.h	(revision 3524)
@@ -0,0 +1,627 @@
+/* $Id: $ */
+/** @file
+ *
+ * kProfiler - Core Code (template).
+ *
+ * Copyright (c) 2006 knut st. osmundsen <bird-src-spam@anduin.net.de>
+ *
+ *
+ * This file is part of kLIBC.
+ *
+ * kLIBC is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * kLIBC is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with kLIBC; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+
+/**
+ * Gets a function, create a new one if necessary.
+ */
+static KPRF_TYPE(P,FUNC) KPRF_NAME(GetFunction)(KPRF_TYPE(P,HDR) pHdr, KPRF_TYPE(,UPTR) uPC)
+{
+    /*
+     * Perform a binary search of the function lookup table.
+     */
+    KPRF_TYPE(P,FUNC) paFunctions = KPRF_OFF2PTR(P,FUNC, pHdr->offFunctions, pHdr);
+
+    KPRF_FUNCS_READ_LOCK();
+    int32_t iStart = 0;
+    int32_t iLast  = pHdr->cFunctions - 1;
+    int32_t i      = iLast / 2;
+    for (;;)
+    {
+        uint32_t iFunction = pHdr->aiFunctions[i];
+        KPRF_TYPE(,IPTR) iDiff = uPC - paFunctions[iFunction].uEntryPtr;
+        if (!iDiff)
+        {
+            KPRF_FUNCS_READ_UNLOCK();
+            return &paFunctions[iFunction];
+        }
+        if (iLast == iStart)
+            break;
+        if (iDiff < 0)
+            iLast = i - 1;
+        else
+            iStart = i + 1;
+        if (iLast < iStart)
+            break;
+        i = iStart + (iLast - iStart) / 2;
+    }
+    KPRF_FUNCS_READ_UNLOCK();
+
+    /*
+     * It wasn't found, try add it.
+     */
+    if (pHdr->cFunctions < pHdr->cMaxFunctions)
+        return KPRF_NAME(NewFunction)(pHdr, uPC);
+    return NULL;
+}
+
+
+/**
+ * Unwind one frame.
+ */
+static uint64_t* KPRF_NAME(UnwindOne)(KPRF_TYPE(P,HDR) pHdr, KPRF_TYPE(P,STACK) pStack, KPRF_TYPE(,UPTR) uPC, uint64_t TS)
+{
+    /*
+     * Pop off the frame and update the frame below / thread.
+     */
+    KPRF_TYPE(P,FRAME) pFrame = &pStack->aFrames[--pStack->cFrames];
+    uint64_t *pCurOverheadTicks;
+    if (pStack->cFrames)
+    {
+        KPRF_TYPE(P,FRAME) pTopFrame = pFrame - 1;
+        pTopFrame->OverheadTicks += pFrame->OverheadTicks + pFrame->CurOverheadTicks;
+        pTopFrame->SleepTicks    += pFrame->SleepTicks;
+        pTopFrame->OnTopOfStackStart = TS;
+        pTopFrame->CurOverheadTicks  = 0;
+
+        pCurOverheadTicks = &pTopFrame->CurOverheadTicks;
+    }
+    else
+    {
+        KPRF_TYPE(P,THREAD) pThread = KPRF_OFF2PTR(P,THREAD, pStack->offThread, pHdr);
+        pThread->ProfiledTicks  += TS - pFrame->OnStackStart - pFrame->CurOverheadTicks - pFrame->OverheadTicks - pFrame->SleepTicks;
+        pThread->OverheadTicks  += pFrame->OverheadTicks + pFrame->CurOverheadTicks;
+        pThread->SleepTicks     += pFrame->SleepTicks;
+
+        pCurOverheadTicks = &pThread->OverheadTicks;
+    }
+
+    /*
+     * Update the function (if any).
+     */
+    if (pFrame->offFunction)
+    {
+        KPRF_TYPE(P,FUNC) pFunc = KPRF_OFF2PTR(P,FUNC, pFrame->offFunction, pHdr);
+
+        /* Time on stack */
+        uint64_t Ticks = TS - pFrame->OnStackStart;
+        Ticks -= pFrame->OverheadTicks + pFrame->CurOverheadTicks + pFrame->SleepTicks;
+/** @todo adjust overhead */
+KPRF_ASSERT(!(Ticks >> 63));
+        if (pFunc->OnStack.MinTicks > Ticks)
+            KPRF_ATOMIC_SET64(&pFunc->OnStack.MinTicks, Ticks);
+        if (pFunc->OnStack.MaxTicks < Ticks)
+            KPRF_ATOMIC_SET64(&pFunc->OnStack.MaxTicks, Ticks);
+        KPRF_ATOMIC_ADD64(&pFunc->OnStack.SumTicks, Ticks);
+
+        /* Time on top of stack */
+        Ticks = TS - pFrame->OnTopOfStackStart;
+        Ticks -= pFrame->CurOverheadTicks;
+        Ticks += pFrame->OnTopOfStackTicks;
+/** @todo adjust overhead */
+KPRF_ASSERT(!(Ticks >> 63));
+        if (pFunc->OnTopOfStack.MinTicks > Ticks)
+            KPRF_ATOMIC_SET64(&pFunc->OnTopOfStack.MinTicks, Ticks);
+        if (pFunc->OnTopOfStack.MaxTicks < Ticks)
+            KPRF_ATOMIC_SET64(&pFunc->OnTopOfStack.MaxTicks, Ticks);
+        KPRF_ATOMIC_ADD64(&pFunc->OnTopOfStack.SumTicks, Ticks);
+
+        /* calls */
+        if (pFrame->cCalls)
+            KPRF_ATOMIC_ADD64(&pFunc->cCalls, pFrame->cCalls);
+    }
+
+    return pCurOverheadTicks;
+}
+
+
+/**
+ * Unwinds the stack.
+ */
+static uint64_t* KPRF_NAME(UnwindInt)(KPRF_TYPE(P,HDR) pHdr, KPRF_TYPE(P,STACK) pStack, KPRF_TYPE(,UPTR) uPC, KPRF_TYPE(,UPTR) uFramePtr, uint64_t TS)
+{
+    /** @todo need to deal with alternative stacks! */
+
+    /*
+     * Pop the stack until we're down below the current frame (uFramePtr).
+     */
+    int32_t iFrame = pStack->cFrames - 1;
+    KPRF_TYPE(P,FRAME) pFrame = &pStack->aFrames[iFrame];
+    /* the most frequent case first. */
+    if (uFramePtr == pFrame->uFramePtr)
+        return KPRF_NAME(UnwindOne)(pHdr, pStack, uPC, TS);
+
+    /* none?  */
+    if (pFrame->uFramePtr > uFramePtr)
+        return &pFrame->CurOverheadTicks;
+
+    /* one or more, possibly all */
+    uint64_t *pCurOverheadTicks = KPRF_NAME(UnwindOne)(pHdr, pStack, uPC, TS);
+    pFrame++;
+    if (    iFrame > 0
+        &&  pFrame->uFramePtr <= uFramePtr)
+    {
+        KPRF_TYPE(P,THREAD) pThread = KPRF_OFF2PTR(P,THREAD, pStack->offThread, pHdr);
+        pThread->cUnwinds++;        /* (This is the reason for what looks like a bad loop unrolling.) */
+
+        pCurOverheadTicks = KPRF_NAME(UnwindOne)(pHdr, pStack, uPC, TS);
+        iFrame -= 2;
+        pFrame++;
+        while (     iFrame >= 0
+               &&   pFrame->uFramePtr <= uFramePtr)
+        {
+            pCurOverheadTicks = KPRF_NAME(UnwindOne)(pHdr, pStack, uPC, TS);
+            iFrame--;
+            pFrame++;
+        }
+    }
+
+    return pCurOverheadTicks;
+}
+
+
+
+/**
+ * Enter function.
+ *
+ * @returns Where to account overhead.
+ * @returns NULL if profiling is inactive.
+ *
+ * @param   uPC         The program counter register. (not relative)
+ * @param   uFramePtr   The stack frame address. This must match the one passed to kPrfLeave. (not relative)
+ * @param   TS          The timestamp when we entered into the profiler.
+ *                      This must not be modified touched!
+ *
+ * @internal ?
+ */
+KPRF_DECL_FUNC(uint64_t *, Enter)(KPRF_TYPE(,UPTR) uPC, KPRF_TYPE(,UPTR) uFramePtr, const uint64_t TS)
+{
+    /*
+     * Is profiling active ?
+     */
+    if (!KPRF_IS_ACTIVE())
+        return NULL;
+
+    /*
+     * Get the header and adjust input addresses.
+     */
+    KPRF_TYPE(P,HDR)    pHdr = KPRF_GET_HDR();
+    if (!pHdr)
+        return NULL;
+    const KPRF_TYPE(,UPTR) uBasePtr = pHdr->uBasePtr;
+    if (uBasePtr)
+    {
+        uFramePtr -= uBasePtr;
+        uPC       -= uBasePtr;
+    }
+
+    /*
+     * Get the current thread. Reject unknown, inactive (in whatever way),
+     * and thread which has performed a stack switch.
+     */
+    KPRF_TYPE(P,THREAD) pThread = KPRF_GET_THREAD();
+    if (!pThread)
+        return NULL;
+    KPRF_TYPE(,THREADSTATE) enmThreadState = pThread->enmState;
+    if (    enmThreadState != KPRF_TYPE(,THREADSTATE_ACTIVE)
+        &&  enmThreadState != KPRF_TYPE(,THREADSTATE_OVERFLOWED)
+        )
+        return NULL;
+    if (pThread->uStackBasePtr < uFramePtr)                                         /* ASSUMES stack direction */
+    {
+        pThread->cStackSwitchRejects++;
+        return NULL;
+    }
+    pThread->enmState = KPRF_TYPE(,THREADSTATE_SUSPENDED);
+
+
+    /*
+     * Update the thread statistics.
+     */
+    pThread->cCalls++;
+    KPRF_TYPE(,UPTR) cbStack = pThread->uStackBasePtr - uFramePtr;                 /* ASSUMES stack direction */
+    if (pThread->cbMaxStack < cbStack)
+        pThread->cbMaxStack = cbStack;
+
+    /*
+     * Check if an longjmp or throw has taken place.
+     * This check will not work if a stack switch has taken place (can fix that later).
+     */
+    KPRF_TYPE(P,STACK) pStack = KPRF_OFF2PTR(P,STACK, pThread->offStack, pHdr);
+    uint32_t iFrame = pStack->cFrames;
+    KPRF_TYPE(P,FRAME) pFrame = &pStack->aFrames[iFrame];
+    if (    iFrame
+        &&  pThread->uStackBasePtr >= uFramePtr                                     /* ASSUMES stack direction */
+        &&  pFrame[-1].uFramePtr + (KPRF_BITS - 8) / 8 < uFramePtr)                 /* ASSUMES stack direction */
+    {
+        KPRF_NAME(UnwindInt)(pHdr, pStack, uPC, uFramePtr, TS);
+        iFrame = pStack->cFrames;
+    }
+
+    /*
+     * Allocate a new stack frame.
+     */
+    if (iFrame >= pHdr->cMaxStackFrames)
+    {
+        /* overflow */
+        pThread->enmState = KPRF_TYPE(,THREADSTATE_OVERFLOWED);
+        pThread->cOverflows += enmThreadState != KPRF_TYPE(,THREADSTATE_OVERFLOWED);
+        return &pStack->aFrames[iFrame - 1].CurOverheadTicks;
+    }
+    pStack->cFrames++;
+
+    /*
+     * Update the old top frame if any.
+     */
+    if (iFrame)
+    {
+        KPRF_TYPE(P,FRAME) pOldFrame = pFrame - 1;
+        pOldFrame->OnTopOfStackTicks += TS - pOldFrame->OnTopOfStackStart;
+        pOldFrame->cCalls++;
+    }
+
+    /*
+     * Fill in the new frame.
+     */
+    pFrame->CurOverheadTicks    = 0;
+    pFrame->OverheadTicks       = 0;
+    pFrame->SleepTicks          = 0;
+    pFrame->OnStackStart        = TS;
+    pFrame->OnTopOfStackStart   = TS;
+    pFrame->OnTopOfStackTicks   = 0;
+    pFrame->cCalls              = 0;
+    pFrame->uFramePtr           = uFramePtr;
+
+    /*
+     * Find the relevant function.
+     */
+    KPRF_TYPE(P,FUNC) pFunc = KPRF_NAME(GetFunction)(pHdr, uPC);
+    if (pFunc)
+    {
+        pFrame->offFunction = KPRF_PTR2OFF(pFunc, pHdr);
+        pFunc->cOnStack++;
+    }
+    else
+        pFrame->offFunction = 0;
+
+    /*
+     * Nearly done, We only have to reactivate the thread and account overhead.
+     * The latter is delegated to the caller.
+     */
+    pThread->enmState = KPRF_TYPE(,THREADSTATE_ACTIVE);
+    return &pFrame->CurOverheadTicks;
+}
+
+
+/**
+ * Leave function.
+ *
+ * @returns Where to account overhead.
+ * @returns NULL if profiling is inactive.
+ *
+ * @param   uPC         The program counter register.
+ * @param   uFramePtr   The stack frame address. This must match the one passed to kPrfEnter.
+ * @param   TS          The timestamp when we entered into the profiler.
+ *                      This must not be modified because the caller could be using it!
+ * @internal
+ */
+KPRF_DECL_FUNC(uint64_t *, Leave)(KPRF_TYPE(,UPTR) uPC, KPRF_TYPE(,UPTR) uFramePtr, const uint64_t TS)
+{
+    /*
+     * Is profiling active ?
+     */
+    if (!KPRF_IS_ACTIVE())
+        return NULL;
+
+    /*
+     * Get the header and adjust input addresses.
+     */
+    KPRF_TYPE(P,HDR)    pHdr = KPRF_GET_HDR();
+    if (!pHdr)
+        return NULL;
+    const KPRF_TYPE(,UPTR) uBasePtr = pHdr->uBasePtr;
+    if (uBasePtr)
+    {
+        uFramePtr -= uBasePtr;
+        uPC       -= uBasePtr;
+    }
+
+    /*
+     * Get the current thread and suspend profiling of the thread until we leave this function.
+     * Also reject threads which aren't active in some way.
+     */
+    KPRF_TYPE(P,THREAD) pThread = KPRF_GET_THREAD();
+    if (!pThread)
+        return NULL;
+    KPRF_TYPE(,THREADSTATE) enmThreadState = pThread->enmState;
+    if (    enmThreadState != KPRF_TYPE(,THREADSTATE_ACTIVE)
+        &&  enmThreadState != KPRF_TYPE(,THREADSTATE_OVERFLOWED)
+        )
+        return NULL;
+    KPRF_TYPE(P,STACK) pStack = KPRF_OFF2PTR(P,STACK, pThread->offStack, pHdr);
+    if (!pStack->cFrames)
+        return NULL;
+    pThread->enmState = KPRF_TYPE(,THREADSTATE_SUSPENDED);
+
+    /*
+     * Unwind the stack down to and including the entry indicated by uFramePtr.
+     * Leave it to the caller to update the overhead.
+     */
+    uint64_t *pCurOverheadTicks = KPRF_NAME(UnwindInt)(pHdr, pStack, uPC, uFramePtr, TS);
+
+    pThread->enmState = enmThreadState;
+    return pCurOverheadTicks;
+}
+
+
+/**
+ * Register the current thread.
+ *
+ * A thread can only be profiled if it has been registered by a call to this function.
+ *
+ * @param   uPC             The program counter register.
+ * @param   uStackBasePtr   The base of the stack.
+ */
+KPRF_DECL_FUNC(KPRF_TYPE(P,THREAD), RegisterThread)(KPRF_TYPE(,UPTR) uStackBasePtr, const char *pszName)
+{
+    /*
+     * Get the header and adjust input address.
+     * (It doesn't matter whether we're active or not.)
+     */
+    KPRF_TYPE(P,HDR)    pHdr = KPRF_GET_HDR();
+    if (!pHdr)
+        return NULL;
+    const KPRF_TYPE(,UPTR) uBasePtr = pHdr->uBasePtr;
+    if (uBasePtr)
+        uStackBasePtr -= uBasePtr;
+
+
+    /*
+     * Allocate a thread and a stack.
+     */
+    KPRF_THREADS_LOCK();
+    if (pHdr->cThreads < pHdr->cMaxThreads)
+    {
+        KPRF_TYPE(P,STACK) pStack = KPRF_OFF2PTR(P,STACK, pHdr->offStacks, pHdr);
+        uint32_t            cLeft = pHdr->cMaxStacks;
+        do
+        {
+            if (!pStack->offThread)
+            {
+                /* init the stack. */
+                pStack->cFrames   = 0;
+                pStack->offThread = pHdr->offThreads + pHdr->cbThread * pHdr->cThreads++;
+                pHdr->cStacks++;
+
+                /* init the thread */
+                KPRF_TYPE(P,THREAD) pThread = KPRF_OFF2PTR(P,THREAD, pStack->offThread, pHdr);
+                pThread->ThreadId       = KPRF_GET_THREADID();
+                unsigned i = 0;
+                if (pszName)
+                    while (i < sizeof(pThread->szName) - 1 && *pszName)
+                        pThread->szName[i++] = *pszName++;
+                while (i < sizeof(pThread->szName))
+                    pThread->szName[i++] = '\0';
+                pThread->enmState       = KPRF_TYPE(,THREADSTATE_SUSPENDED);
+                pThread->Reserved0      = KPRF_TYPE(,THREADSTATE_TERMINATED);
+                pThread->uStackBasePtr  = uStackBasePtr;
+                pThread->cbMaxStack     = 0;
+                pThread->cCalls         = 0;
+                pThread->cOverflows     = 0;
+                pThread->cStackSwitchRejects = 0;
+                pThread->cUnwinds       = 0;
+                pThread->ProfiledTicks  = 0;
+                pThread->OverheadTicks  = 0;
+                pThread->SleepTicks     = 0;
+                pThread->offStack       = KPRF_PTR2OFF(pStack, pHdr);
+
+
+                /* set the thread and make it active. */
+                KPRF_THREADS_UNLOCK();
+                KPRF_SET_THREAD(pThread);
+                pThread->enmState = KPRF_TYPE(,THREADSTATE_ACTIVE);
+                return pThread;
+            }
+
+            /* next */
+            pStack = KPRF_TYPE(P,STACK)(((KPRF_TYPE(,UPTR))pStack + pHdr->cbStack));
+        } while (--cLeft > 0);
+    }
+
+    KPRF_THREADS_UNLOCK();
+    return NULL;
+}
+
+
+/**
+ * Terminates a thread.
+ *
+ * To terminate the current thread use DeregisterThread(), because that
+ * cleans up the TLS entry too.
+ *
+ * @param   pHdr        The profiler data set header.
+ * @param   pThread     The thread to terminate.
+ * @param   TS          The timestamp to use when terminating the thread.
+ */
+KPRF_DECL_FUNC(void, TerminateThread)(KPRF_TYPE(P,HDR) pHdr, KPRF_TYPE(P,THREAD) pThread, uint64_t TS)
+{
+    if (pThread->enmState == KPRF_TYPE(,THREADSTATE_TERMINATED))
+        return;
+    pThread->enmState = KPRF_TYPE(,THREADSTATE_TERMINATED);
+
+    /*
+     * Unwind the entire stack.
+     */
+    if (pThread->offStack)
+    {
+        KPRF_TYPE(P,STACK) pStack = KPRF_OFF2PTR(P,STACK, pThread->offStack, pHdr);
+        for (uint32_t cFrames = pStack->cFrames; cFrames > 0; cFrames--)
+            KPRF_NAME(UnwindOne)(pHdr, pStack, 0, TS);
+
+        /*
+         * Free the stack.
+         */
+        pThread->offStack = 0;
+        KPRF_THREADS_LOCK();
+        pStack->offThread = 0;
+        pHdr->cStacks--;
+        KPRF_THREADS_UNLOCK();
+    }
+}
+
+
+/**
+ * Deregister (terminate) the current thread.
+ */
+KPRF_DECL_FUNC(void, DeregisterThread)(void)
+{
+    uint64_t TS = KPRF_NOW();
+
+    /*
+     * Get the header, then get the thread and mark it terminated.
+     * (It doesn't matter whether we're active or not.)
+     */
+    KPRF_TYPE(P,HDR)    pHdr = KPRF_GET_HDR();
+    if (!pHdr)
+        return;
+
+    KPRF_TYPE(P,THREAD) pThread = KPRF_GET_THREAD();
+    KPRF_SET_THREAD(NULL);
+    if (!pThread)
+        return;
+    KPRF_NAME(TerminateThread)(pHdr, pThread, TS);
+}
+
+
+/**
+ * Resumes / restarts a thread.
+ *
+ * @param   fReset  If set the stack is reset.
+ */
+KPRF_DECL_FUNC(void, ResumeThread)(int fReset)
+{
+    uint64_t TS = KPRF_NOW();
+
+    /*
+     * Get the header, then get the thread and mark it terminated.
+     * (It doesn't matter whether we're active or not.)
+     */
+    KPRF_TYPE(P,HDR)    pHdr = KPRF_GET_HDR();
+    if (!pHdr)
+        return;
+
+    KPRF_TYPE(P,THREAD) pThread = KPRF_GET_THREAD();
+    if (!pThread)
+        return;
+    if (pThread->enmState != KPRF_TYPE(,THREADSTATE_SUSPENDED))
+        return;
+
+    /*
+     * Reset (unwind) the stack?
+     */
+    KPRF_TYPE(P,STACK) pStack = KPRF_OFF2PTR(P,STACK, pThread->offStack, pHdr);
+    if (fReset)
+    {
+        uint32_t cFrames = pStack->cFrames;
+        while (cFrames-- > 0)
+            KPRF_NAME(UnwindOne)(pHdr, pStack, 0, TS);
+    }
+    /*
+     * If we've got any thing on the stack, we'll have to stop the sleeping period.
+     */
+    else if (pStack->cFrames > 0)
+    {
+        KPRF_TYPE(P,FRAME) pFrame = &pStack->aFrames[pStack->cFrames - 1];
+
+        /* update the sleeping time and set the start of the new top-of-stack period. */
+        pFrame->SleepTicks += TS - pFrame->OnTopOfStackStart;
+        pFrame->OnTopOfStackStart = TS;
+    }
+    /** @todo we're not accounting overhead here! */
+
+    /*
+     * We're done, switch the thread to active state.
+     */
+    pThread->enmState = KPRF_TYPE(,THREADSTATE_ACTIVE);
+}
+
+
+/**
+ * Suspend / completes a thread.
+ *
+ * The thread will be in a suspend state where the time will be accounted for as sleeping.
+ *
+ * @param   fUnwind     If set the stack is unwound and the thread statistics updated.
+ */
+KPRF_DECL_FUNC(void, SuspendThread)(int fUnwind)
+{
+    uint64_t TS = KPRF_NOW();
+
+    /*
+     * Get the header, then get the thread and mark it terminated.
+     * (It doesn't matter whether we're active or not.)
+     */
+    KPRF_TYPE(P,HDR)    pHdr = KPRF_GET_HDR();
+    if (!pHdr)
+        return;
+
+    KPRF_TYPE(P,THREAD) pThread = KPRF_GET_THREAD();
+    if (!pThread)
+        return;
+    if (    pThread->enmState != KPRF_TYPE(,THREADSTATE_ACTIVE)
+        &&  pThread->enmState != KPRF_TYPE(,THREADSTATE_OVERFLOWED)
+        &&  (pThread->enmState != KPRF_TYPE(,THREADSTATE_SUSPENDED) || fUnwind))
+        return;
+
+    pThread->enmState = KPRF_TYPE(,THREADSTATE_SUSPENDED);
+
+    /*
+     * Unwind the stack?
+     */
+    KPRF_TYPE(P,STACK) pStack = KPRF_OFF2PTR(P,STACK, pThread->offStack, pHdr);
+    if (fUnwind)
+    {
+        uint32_t cFrames = pStack->cFrames;
+        while (cFrames-- > 0)
+            KPRF_NAME(UnwindOne)(pHdr, pStack, 0, TS);
+    }
+    /*
+     * If we've got any thing on the stack, we'll have to record the sleeping period
+     * of the thread. If not we'll ignore it (for now at least).
+     */
+    else if (pStack->cFrames > 0)
+    {
+        KPRF_TYPE(P,FRAME) pFrame = &pStack->aFrames[pStack->cFrames - 1];
+
+        /* update the top of stack time and set the start of the sleep period. */
+        pFrame->OnTopOfStackTicks += TS - pFrame->OnTopOfStackStart;
+        pFrame->OnTopOfStackStart = TS;
+    }
+
+    /** @todo we're not accounting overhead here! */
+}
+
+
Index: trunk/kProfile/prfcore.h.h
===================================================================
--- trunk/kProfile/prfcore.h.h	(revision 3524)
+++ trunk/kProfile/prfcore.h.h	(revision 3524)
@@ -0,0 +1,377 @@
+/* $Id: $ */
+/** @file
+ *
+ * kProfiler Core Header (template).
+ *
+ * Copyright (c) 2006 knut st. osmundsen <bird-src-spam@anduin.net.de>
+ *
+ *
+ * This file is part of kLIBC.
+ *
+ * kLIBC is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * kLIBC is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with kLIBC; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+
+/** @def KPRF_NAME
+ * Mixed case name macro.
+ */
+#ifndef KPRF_NAME
+# define KPRF_NAME(Name)        Name
+#endif
+
+/** @def KPRF_TYPE
+ * Upper case type name macro.
+ */
+#ifndef KPRF_TYPE
+# define KPRF_TYPE(Prefix,Name) Prefix##Name
+#endif
+
+/** @type KPRF_DECL_FUNC
+ * The calling convention used.
+ */
+#ifndef KPRF_DECL_FUNC
+# define KPRF_DECL_FUNC(type, name) type name
+#endif
+
+/** @def KPRF_BITS
+ * The bitsize of the format.
+ */
+#ifndef KPRF_BITS
+# define KPRF_BITS  32
+#endif
+
+/** @type UPTR
+ * The basic unsigned interger pointer type.
+ */
+/** @type IPTR
+ * The basic signed interger pointer type.
+ */
+#if KPRF_BITS == 16
+typedef uint16_t KPRF_TYPE(,UPTR);
+typedef int16_t  KPRF_TYPE(,IPTR);
+#elif KPRF_BITS == 32
+typedef uint32_t KPRF_TYPE(,UPTR);
+typedef int32_t  KPRF_TYPE(,IPTR);
+#elif KPRF_BITS == 64
+typedef uint64_t KPRF_TYPE(,UPTR);
+typedef int64_t  KPRF_TYPE(,IPTR);
+#else
+# error "KPRF_BITS has an invalid value. Supported values are 16, 32 and 64."
+#endif
+/** @type KPRF_TYPE(P,UPTR)
+ * Pointer to the basic pointer type.
+ */
+typedef KPRF_TYPE(,UPTR) *KPRF_TYPE(P,UPTR);
+
+
+/**
+ * Various constants.
+ */
+enum KPRF_TYPE(,CONSTANTS)
+{
+    /** Magic for the profiler header. (Unix Epoc) */
+    KPRF_TYPE(,HDR_MAGIC) = 0x19700101
+};
+
+
+/**
+ * The profile data header.
+ */
+typedef struct KPRF_TYPE(,HDR)
+{
+    /** [0] The magic number for file data. (KPRF_TYPE(,HDR_MAGIC)) */
+    uint32_t                u32Magic;
+    /** [4] KPRF_BITS. */
+    uint32_t                cFormatBits;
+    /** [8] The base address which all pointers should be relative to. */
+    KPRF_TYPE(,UPTR)        uBasePtr;
+#if KPRF_BITS <= 16
+    /** [a] Reserved. */
+    uint16_t                u16Reserved;
+#endif
+#if KPRF_BITS <= 32
+    /** [c] Reserved. */
+    uint32_t                u32Reserved;
+#endif
+    /** [10] The size of this data set. */
+    uint32_t                cb;
+    /** [10] The allocated data set size. */
+    uint32_t                cbAllocated;
+
+    /** [18] The max number of functions the function table can hold. */
+    uint32_t                cMaxFunctions;
+    /** [1c] The current number of functions in the function table. */
+    uint32_t                cFunctions;
+    /** [20] The offset of the function table (relative to this header). */
+    uint32_t                offFunctions;
+    /** [24] The size of a function entry. */
+    uint32_t                cbFunction;
+
+    /** [28] The max number of bytes the module segments can occupy. */
+    uint32_t                cbMaxModSegs;
+    /** [2c] The current size of the module segment records. */
+    uint32_t                cbModSegs;
+    /** [30] The offset of the module segment records (relative to this header). */
+    uint32_t                offModSegs;
+
+    /** [34] The max number of threads the thread table can contain. */
+    uint32_t                cMaxThreads;
+    /** [38] The current number of threads in the thread table. */
+    uint32_t                cThreads;
+    /** [3c] The offset of the thread table (relative to this header). */
+    uint32_t                offThreads;
+    /** [40] The size of a thread entry. */
+    uint32_t                cbThread;
+
+    /** [44] The max number of stacks the stack table can contain. */
+    uint32_t                cMaxStacks;
+    /** [48] The max number of stacks.
+     * Unlike the other members, the stacks can be reused. It follows that
+     * this count doesn't specify the number of used slots from the start. */
+    uint32_t                cStacks;
+    /** [4c] The offset of the thread table (relative to this header).
+     * This is usually 0 in a stored data set. */
+    uint32_t                offStacks;
+    /** [50] The size of a stack. */
+    uint32_t                cbStack;
+    /** [54] The maxium stack depth. */
+    uint32_t                cMaxStackFrames;
+
+    /** [58] The process commandline.
+     * Might not always apply is will be 0 in those cases. This is normally written
+     * where the stacks used to be.
+     */
+    uint32_t                offCommandLine;
+    /** [5c] The length of the command line. (excludes the terminator). */
+    uint32_t                cchCommandLine;
+
+    /** [60]  The function lookup table (it contains indexes).
+     * This is sorted by address so that a binary search can be performed.
+     * Access to this table is managed externally, but generally a read/write lock is employed. */
+    uint32_t                aiFunctions[1];
+} KPRF_TYPE(,HDR);
+/** Pointer to a profiler data header. */
+typedef KPRF_TYPE(,HDR) *KPRF_TYPE(P,HDR);
+/** Pointer to a const profiler data header. */
+typedef const KPRF_TYPE(,HDR) *KPRF_TYPE(PC,HDR);
+
+
+/**
+ * Time statistics.
+ */
+typedef struct KPRF_TYPE(,TIMESTAT)      /** @todo bad names and descriptions! */
+{
+    /** The minimum period */
+    uint64_t volatile       MinTicks;
+    /** The maximum period */
+    uint64_t volatile       MaxTicks;
+    /** The sum of all periods. */
+    uint64_t volatile       SumTicks;
+} KPRF_TYPE(,TIMESTAT);
+/** Pointer to time statistics. */
+typedef KPRF_TYPE(,TIMESTAT) *KPRF_TYPE(P,TIMESTAT);
+/** Pointer to const time statistics. */
+typedef const KPRF_TYPE(,TIMESTAT) *KPRF_TYPE(PC,TIMESTAT);
+
+
+/**
+ * A Module Segment.
+ */
+typedef struct KPRF_TYPE(,MODSEG)
+{
+    /** The address of the segment. (relative address) */
+    KPRF_TYPE(,UPTR)        uBasePtr;
+    /** The size of the segment minus one (so the entire address space can be covered). */
+    KPRF_TYPE(,UPTR)        cbSegmentMinusOne;
+    /** The segment number. (0 based) */
+    uint32_t                iSegment;
+    /** Flag indicating whether this segment is loaded or not.
+     * (A 16-bit value was choosen out of convenience, all that's stored is 0 or 1 anyway.) */
+    uint16_t                fLoaded;
+    /** The length of the path.
+     * This is used to calculate the length of the record: offsetof(MODSEG, szPath) + cchPath + 1 */
+    uint16_t                cchPath;
+    /** The module name. */
+    char                    szPath[1];
+} KPRF_TYPE(,MODSEG);
+/** Pointer to a module segment. */
+typedef KPRF_TYPE(,MODSEG) *KPRF_TYPE(P,MODSEG);
+/** Pointer to a const module segment. */
+typedef const KPRF_TYPE(,MODSEG) *KPRF_TYPE(PC,MODSEG);
+
+
+/**
+ * The profiler data for a function.
+ */
+typedef struct KPRF_TYPE(,FUNC)
+{
+    /** The entry address of the function. (relative address)
+     * This is the return address of the entry hook (_mcount, _penter, _ProfileHook32, ...). */
+    KPRF_TYPE(,UPTR)        uEntryPtr;
+    /** Offset (relative to the profiler header) of the module segment to which this function belongs. */
+    uint32_t                offModSeg;
+
+    /** The number times on the stack. */
+    uint64_t volatile       cOnStack;
+    /** The number of calls made from this function. */
+    uint64_t volatile       cCalls;
+
+    /** Time on stack. */
+    KPRF_TYPE(,TIMESTAT)    OnStack;
+    /** Time on top of the stack, i.e. executing. */
+    KPRF_TYPE(,TIMESTAT)    OnTopOfStack;
+
+    /** @todo recursion */
+
+} KPRF_TYPE(,FUNC);
+/** Pointer to the profiler data for a function. */
+typedef KPRF_TYPE(,FUNC)       *KPRF_TYPE(P,FUNC);
+/** Pointer to the const profiler data for a function. */
+typedef const KPRF_TYPE(,FUNC) *KPRF_TYPE(PC,FUNC);
+
+
+/**
+ * Stack frame.
+ */
+typedef struct KPRF_TYPE(,FRAME)
+{
+    /** The accumulated overhead.
+     * Over head is accumulated by the parent frame when a child is poped off the stack. */
+    uint64_t                OverheadTicks;
+    /** The current (top of stack) overhead. */
+    uint64_t                CurOverheadTicks;
+    /** The accumulated sleep ticks.
+     * It's possible to notify the profiler that the thread is being put into a wait/sleep/yield
+     * state. The time spent sleeping is transfered to the parent frame when poping of a child one. */
+    uint64_t                SleepTicks;
+    /** The start of the on-stack period. */
+    uint64_t                OnStackStart;
+    /** The accumulated time on top (excludes overhead (sleep doesn't apply here obviously)). */
+    uint64_t                OnTopOfStackTicks;
+    /** The start of the current on-top-of-stack period.
+     * This is also to mark the start of a sleeping period, the ResumeThread function will always
+     * treat it as the start of the suspend period. */
+    uint64_t                OnTopOfStackStart;
+    /** The number of calls made from this stack frame. */
+    uint64_t                cCalls;
+    /** Stack address of this frame.
+     * This is used to detect throw and longjmp, and is also used to deal with overflow. (relative address) */
+    KPRF_TYPE(,UPTR)       uFramePtr;
+    /** Offset (relative to the profiler header) to the function record.
+     * This is 0 if we're out of function space. */
+    uint32_t                offFunction;
+} KPRF_TYPE(,FRAME);
+/** Pointer to a stack frame. */
+typedef KPRF_TYPE(,FRAME) *KPRF_TYPE(P,FRAME);
+/** Pointer to a const stack frame. */
+typedef const KPRF_TYPE(,FRAME) *KPRF_TYPE(PC,FRAME);
+
+
+/**
+ * Stack.
+ */
+typedef struct KPRF_TYPE(,STACK)
+{
+    /** The offset (relative to the profiler header) of the thread owning the stack.
+     * This is zero if not in use, and non-zero if in use. */
+    uint32_t                    offThread;
+    /** The number of active stack frames. */
+    uint32_t                    cFrames;
+    /** The stack frames.
+     * The actual size of this array is specified in the header. */
+    KPRF_TYPE(,FRAME)          aFrames[1];
+} KPRF_TYPE(,STACK);
+/** Pointer to a stack. */
+typedef KPRF_TYPE(,STACK) *KPRF_TYPE(P,STACK);
+/** Pointer to a const stack. */
+typedef const KPRF_TYPE(,STACK) *KPRF_TYPE(PC,STACK);
+
+
+/**
+ * The thread state.
+ */
+typedef enum KPRF_TYPE(,THREADSTATE)
+{
+    /** The thread hasn't been used yet. */
+    KPRF_TYPE(,THREADSTATE_UNUSED) = 0,
+    /** The thread is activly being profiled.
+     * A thread is added in the suspended state and then activated when
+     * starting to execute the first function.
+     */
+    KPRF_TYPE(,THREADSTATE_ACTIVE),
+    /** The thread is currently suspended from profiling.
+     * Upon entering profiler code the thread is suspended, it's reactivated
+     * upon normal return.
+     */
+    KPRF_TYPE(,THREADSTATE_SUSPENDED),
+    /** The thread is currently suspended due of stack overflow.
+     * When we overflow the stack frame array, the thread enter the overflow state. In this
+     * state nothing is profiled but we keep looking for the exit of the top frame. */
+    KPRF_TYPE(,THREADSTATE_OVERFLOWED),
+    /** The thread is terminated.
+     * When we received a thread termination notification the thread is unwinded, statistics
+     * updated and the state changed to terminated. A terminated thread cannot be revivied. */
+    KPRF_TYPE(,THREADSTATE_TERMINATED),
+
+    /** Ensure 32-bit size. */
+    KPRF_TYPE(,THREADSTATE_32BIT_HACK) = 0x7fffffff
+} KPRF_TYPE(,THREADSTATE);
+
+
+/**
+ * Thread statistics and stack.
+ */
+typedef struct KPRF_TYPE(,THREAD)
+{
+    /** The native thread id. */
+    uint64_t                    ThreadId;
+    /** The thread name. (optional) */
+    char                        szName[32];
+    /** The thread current thread state. */
+    KPRF_TYPE(,THREADSTATE)     enmState;
+    /** Alignment. */
+    KPRF_TYPE(,THREADSTATE)     Reserved0;
+    /** The base pointer of the thread stack. (relative address) */
+    KPRF_TYPE(,UPTR)            uStackBasePtr;
+    /** The maximum depth of the thread stack (bytes). */
+    KPRF_TYPE(,UPTR)            cbMaxStack;
+    /** The number of calls done by this thread. */
+    uint64_t                    cCalls;
+    /** The number of times the stack overflowed. */
+    uint64_t                    cOverflows;
+    /** The number of times stack entries has been rejected because of a stack switch. */
+    uint64_t                    cStackSwitchRejects;
+    /** The number of times the stack has been unwinded more than one frame. */
+    uint64_t                    cUnwinds;
+
+    /** The profiled ticks. (This does not include sleep or overhead ticks.)
+     * This is the accumulated on-stack values for the final stack frames. */
+    uint64_t                    ProfiledTicks;
+    /** The accumulated overhead of this thread. */
+    uint64_t                    OverheadTicks;
+    /** The accumulated sleep ticks for this thread.
+     * See KPRF_TYPE(,FRAME)::SleepTicks for details. */
+    uint64_t                    SleepTicks;
+
+    /** The offset of the stack. */
+    uint32_t                    offStack;
+} KPRF_TYPE(,THREAD);
+/** Pointer to a thread. */
+typedef KPRF_TYPE(,THREAD) *KPRF_TYPE(P,THREAD);
+/** Pointer to a const thread. */
+typedef const KPRF_TYPE(,THREAD) *KPRF_TYPE(PC,THREAD);
+
+
Index: trunk/kProfile/prfcorefunction.cpp.h
===================================================================
--- trunk/kProfile/prfcorefunction.cpp.h	(revision 3524)
+++ trunk/kProfile/prfcorefunction.cpp.h	(revision 3524)
@@ -0,0 +1,123 @@
+/* $Id: $ */
+/** @file
+ *
+ * kProfiler - Core New Function Code (template).
+ *
+ * Copyright (c) 2006 knut st. osmundsen <bird-src-spam@anduin.net.de>
+ *
+ *
+ * This file is part of kLIBC.
+ *
+ * kLIBC is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * kLIBC is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with kLIBC; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+
+/**
+ * Creates a new function.
+ *
+ * @returns Pointer to the new function.
+ * @returns NULL if we're out of space.
+ */
+static KPRF_TYPE(P,FUNC) KPRF_NAME(NewFunction)(KPRF_TYPE(P,HDR) pHdr,KPRF_TYPE(,UPTR) uPC)
+{
+    /*
+     * First find the position of the function (it might actually have been inserted by someone else by now too).
+     */
+    KPRF_FUNCS_WRITE_LOCK();
+
+    KPRF_TYPE(P,FUNC) paFunctions = KPRF_OFF2PTR(P,FUNC, pHdr->offFunctions, pHdr);
+    int32_t iStart = 0;
+    int32_t iLast  = pHdr->cFunctions - 1;
+    int32_t i      = iLast / 2;
+    for (;;)
+    {
+        uint32_t iFunction = pHdr->aiFunctions[i];
+        KPRF_TYPE(,IPTR) iDiff = uPC - paFunctions[iFunction].uEntryPtr;
+        if (!iDiff)
+        {
+            KPRF_FUNCS_WRITE_UNLOCK();
+            return &paFunctions[iFunction];
+        }
+        if (iLast == iStart)
+            break;
+        if (iDiff < 0)
+            iLast = i - 1;
+        else
+            iStart = i + 1;
+        if (iLast < iStart)
+            break;
+        i = iStart + (iLast - iStart) / 2;
+    }
+
+    /*
+     * Adjust the index so we're exactly in the right spot.
+     * (I've too much of a headache to figure out if the above loop leaves us where we should be.)
+     */
+    const int32_t iNew = pHdr->cFunctions;
+    if (paFunctions[pHdr->aiFunctions[i]].uEntryPtr > uPC)
+    {
+        while (     i > 0
+               &&   paFunctions[pHdr->aiFunctions[i - 1]].uEntryPtr > uPC)
+            i--;
+    }
+    else
+    {
+        while (     i < iNew
+               &&   paFunctions[pHdr->aiFunctions[i]].uEntryPtr < uPC)
+            i++;
+    }
+
+    /*
+     * Ensure that there still is space for the function.
+     */
+    if (iNew >= (int32_t)pHdr->cMaxFunctions)
+    {
+        KPRF_FUNCS_WRITE_UNLOCK();
+        return NULL;
+    }
+    pHdr->cFunctions++;
+    KPRF_TYPE(P,FUNC) pNew = &paFunctions[iNew];
+
+    /* init the new function entry */
+    pNew->uEntryPtr  = uPC;
+    pNew->offModSeg  = 0;
+    pNew->cOnStack   = 0;
+    pNew->cCalls     = 0;
+    pNew->OnStack.MinTicks      = ~(uint64_t)0;
+    pNew->OnStack.MaxTicks      = 0;
+    pNew->OnStack.SumTicks      = 0;
+    pNew->OnTopOfStack.MinTicks = ~(uint64_t)0;
+    pNew->OnTopOfStack.MaxTicks = 0;
+    pNew->OnTopOfStack.SumTicks = 0;
+
+    /* shift the function index array and insert the new one. */
+    int32_t j = iNew;
+    while (j > i)
+    {
+        pHdr->aiFunctions[j] = pHdr->aiFunctions[j - 1];
+        j--;
+    }
+    pHdr->aiFunctions[i] = iNew;
+    KPRF_FUNCS_WRITE_UNLOCK();
+
+    /*
+     * Record the module segment (i.e. add it if it's new).
+     */
+    pNew->offModSeg = KPRF_NAME(RecordModSeg)(pHdr, uPC);
+
+    return pNew;
+}
+
Index: trunk/kProfile/prfcoreinit.cpp.h
===================================================================
--- trunk/kProfile/prfcoreinit.cpp.h	(revision 3524)
+++ trunk/kProfile/prfcoreinit.cpp.h	(revision 3524)
@@ -0,0 +1,187 @@
+/* $Id: $ */
+/** @file
+ *
+ * kProfiler - Core Initialization Code (template).
+ *
+ * Copyright (c) 2006 knut st. osmundsen <bird-src-spam@anduin.net.de>
+ *
+ *
+ * This file is part of kLIBC.
+ *
+ * kLIBC is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * kLIBC is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with kLIBC; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+
+/**
+ * Calculates the size of the profiler data set.
+ *
+ * @returns The size of the data set in bytes.
+ *
+ * @param   cMaxFunctions       The max number of functions.
+ * @param   cbMaxModSeg         The max bytes for module segments.
+ * @param   cMaxThreads         The max number of threads.
+ * @param   cMaxStacks          The max number of stacks. (should be less or equal to the max number of threads)
+ * @param   cMaxStackFrames     The max number of frames on each of the stacks.
+ *
+ * @remark  This function does not input checks, it only aligns it. The caller is
+ *          responsible for the input to make some sense.
+ */
+KPRF_DECL_FUNC(uint32_t, CalcSize)(uint32_t cMaxFunctions, uint32_t cbMaxModSegs, uint32_t cMaxThreads, uint32_t cMaxStacks, uint32_t cMaxStackFrames)
+{
+    /*
+     * Normalize input.
+     */
+    KPRF_SETMIN_ALIGN(cMaxFunctions, 16, 16);
+    KPRF_SETMIN_ALIGN(cbMaxModSegs, KPRF_SIZEOF(MODSEG), 32);
+    KPRF_SETMIN_ALIGN(cMaxThreads, 1, 1);
+    KPRF_SETMIN_ALIGN(cMaxStacks, 1, 1);
+    KPRF_SETMIN_ALIGN(cMaxStackFrames, 32, 32);
+
+    /*
+     * Calc the size from the input.
+     * We do not take overflows into account, stupid user means stupid result.
+     */
+    uint32_t cb = KPRF_OFFSETOF(HDR, aiFunctions[cMaxFunctions]);
+    uint32_t cbTotal = KPRF_ALIGN(cb, 32);
+
+    cb = cMaxFunctions * KPRF_SIZEOF(FUNC);
+    cbTotal += KPRF_ALIGN(cb, 32);
+
+    cbTotal += cbMaxModSegs;
+
+    cb = cMaxThreads * KPRF_SIZEOF(THREAD);
+    cbTotal += KPRF_ALIGN(cb, 32);
+
+    cb = cMaxStacks * KPRF_SIZEOF(STACK);
+    cbTotal += KPRF_ALIGN(cb, 32);
+
+    cb = cMaxStackFrames * cMaxStacks * KPRF_SIZEOF(FRAME);
+    cbTotal += KPRF_ALIGN(cb, 32);
+
+    return cbTotal;
+}
+
+
+/**
+ * Initializes the profiler data set.
+ *
+ * @returns Pointer to the initialized profiler header on success.
+ * @returns NULL if the input doesn't add up.
+ *
+ * @param   pvData              Where to initialize the profiler data set.
+ * @param   cbData              The size of the available data.
+ * @param   cMaxFunctions       The max number of functions.
+ * @param   cbMaxModSeg         The max bytes for module segments.
+ * @param   cMaxThreads         The max number of threads.
+ * @param   cMaxStacks          The max number of stacks. (should be less or equal to the max number of threads)
+ * @param   cMaxStackFrames     The max number of frames on each of the stacks.
+ *
+ */
+KPRF_DECL_FUNC(KPRF_TYPE(P,HDR), Init)(void *pvData, uint32_t cbData, uint32_t cMaxFunctions, uint32_t cbMaxModSegs,
+                                       uint32_t cMaxThreads, uint32_t cMaxStacks, uint32_t cMaxStackFrames)
+{
+    /*
+     * Normalize the input.
+     */
+    if (!pvData)
+        return NULL;
+    KPRF_SETMIN_ALIGN(cMaxFunctions, 16, 16);
+    KPRF_SETMIN_ALIGN(cbMaxModSegs, KPRF_SIZEOF(MODSEG), 32);
+    KPRF_SETMIN_ALIGN(cMaxThreads, 1, 1);
+    KPRF_SETMIN_ALIGN(cMaxStacks, 1, 1);
+    KPRF_SETMIN_ALIGN(cMaxStackFrames, 32, 32);
+
+    /*
+     * The header.
+     */
+    uint32_t off = 0;
+    uint32_t cb = KPRF_OFFSETOF(HDR, aiFunctions[cMaxFunctions]);
+    cb = KPRF_ALIGN(cb, 32);
+    if (cbData < off + cb || off > off + cb)
+        return NULL;
+    KPRF_TYPE(P,HDR) pHdr = (KPRF_TYPE(P,HDR))pvData;
+
+    /* the core header */
+    pHdr->u32Magic          = 0;        /* Set at the very end */
+    pHdr->cFormatBits       = KPRF_BITS;
+    pHdr->uBasePtr          = 0;        /* Can be set afterwards using SetBasePtr. */
+#if KPRF_BITS <= 16
+    pHdr->u16Reserved       = 0;
+#endif
+#if KPRF_BITS <= 32
+    pHdr->u32Reserved       = 0;
+#endif
+    pHdr->cb                = cbData;
+    pHdr->cbAllocated       = cbData;
+
+    /* functions */
+    off += cb;
+    cb = cMaxFunctions * KPRF_SIZEOF(FUNC);
+    cb = KPRF_ALIGN(cb, 32);
+    if (cbData < off + cb || off > off + cb)
+        return NULL;
+    pHdr->cMaxFunctions     = cMaxFunctions;
+    pHdr->cFunctions        = 0;
+    pHdr->offFunctions      = off;
+    pHdr->cbFunction        = KPRF_SIZEOF(FUNC);
+
+    /* modsegs */
+    off += cb;
+    cb = KPRF_ALIGN(cbMaxModSegs, 32);
+    if (cbData < off + cb || off > off + cb)
+        return NULL;
+    pHdr->cbMaxModSegs      = cbMaxModSegs;
+    pHdr->cbModSegs         = 0;
+    pHdr->offModSegs        = off;
+
+    /* threads */
+    off += cb;
+    cb = cMaxThreads * KPRF_SIZEOF(THREAD);
+    cb = KPRF_ALIGN(cb, 32);
+    if (cbData < off + cb || off > off + cb)
+        return NULL;
+    pHdr->cMaxThreads       = cMaxThreads;
+    pHdr->cThreads          = 0;
+    pHdr->offThreads        = off;
+    pHdr->cbThread          = KPRF_SIZEOF(THREAD);
+
+    /* stacks */
+    off += cb;
+    cb = cMaxStacks * KPRF_OFFSETOF(STACK, aFrames[cMaxStackFrames]);
+    cb = KPRF_ALIGN(cb, 32);
+    if (cbData < off + cb || off > off + cb)
+        return NULL;
+    pHdr->cMaxStacks        = cMaxStacks;
+    pHdr->cStacks           = 0;
+    pHdr->offStacks         = off;
+    pHdr->cbStack           = KPRF_OFFSETOF(STACK, aFrames[cMaxStackFrames]);
+    pHdr->cMaxStackFrames   = cMaxStackFrames;
+
+    /* commandline */
+    pHdr->offCommandLine    = 0;
+    pHdr->cchCommandLine    = 0;
+
+    /* the final size */
+    pHdr->cb                = off + cb;
+
+
+    /*
+     * Done.
+     */
+    pHdr->u32Magic = KPRF_TYPE(,HDR_MAGIC);
+    return pHdr;
+}
+
Index: trunk/kProfile/prfcoremodseg.cpp.h
===================================================================
--- trunk/kProfile/prfcoremodseg.cpp.h	(revision 3524)
+++ trunk/kProfile/prfcoremodseg.cpp.h	(revision 3524)
@@ -0,0 +1,194 @@
+/* $Id: $ */
+/** @file
+ *
+ * kProfiler - Core Module Segment Code (template header).
+ *
+ * Copyright (c) 2006 knut st. osmundsen <bird-src-spam@anduin.net.de>
+ *
+ *
+ * This file is part of kLIBC.
+ *
+ * kLIBC is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * kLIBC is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with kLIBC; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+
+/**
+ * Adds a module segment.
+ *
+ * @returns Offset to the module if existing or successfully added
+ * @returns 0 if not found.
+ *
+ * @param   pHdr        The profiler header.
+ * @param   pModSeg     Pointer to the module segment to insert (it's copied of course).
+ * @param   off         The offset into the modseg area which has been searched.
+ *                      (This is relative to the first moddule segment record (at pHdr->offModSegs).)
+ */
+static uint32_t KPRF_NAME(InsertModSeg)(KPRF_TYPE(P,HDR) pHdr, KPRF_TYPE(PC,MODSEG) pModSeg, uint32_t off)
+{
+    /*
+     * Lookup the module segment, inserting it if not found (and there is room).
+     */
+    for (;;)
+    {
+        if (off >= pHdr->cbModSegs)
+        {
+            /*
+             * It was the end, let's try insert it.
+             *
+             * This is where we lock the modseg stuff. The deal is that we
+             * serialize the actual inserting without blocking lookups. This
+             * means that we may end up with potential racing inserts, but
+             * unless there is a large amount of modules being profiled that's
+             * probably not going to be much of a problem. Anyway if we race,
+             * we'll simply have to search the new additions before we add our
+             * own stuff.
+             */
+            KPRF_MODSEGS_LOCK();
+            if (off >= pHdr->cbModSegs)
+            {
+                uint32_t cbModSeg = KPRF_OFFSETOF(MODSEG, szPath[pModSeg->cchPath + 1]);
+                cbModSeg = KPRF_ALIGN(cbModSeg, KPRF_SIZEOF(UPTR));
+                if (off + cbModSeg <= pHdr->cbMaxModSegs)
+                {
+                    KPRF_TYPE(P,MODSEG) pNew = KPRF_OFF2PTR(P,MODSEG, off + pHdr->offModSegs, pHdr);
+                    pNew->uBasePtr          = pModSeg->uBasePtr;
+                    pNew->cbSegmentMinusOne = pModSeg->cbSegmentMinusOne;
+                    pNew->iSegment          = pModSeg->iSegment;
+                    pNew->fLoaded           = pModSeg->fLoaded;
+                    pNew->cchPath           = pModSeg->cchPath;
+
+                    int32_t iPath = pModSeg->cchPath;
+                    do  pNew->szPath[iPath] = pModSeg->szPath[iPath];
+                    while (--iPath >= 0);
+
+                    /* commit it */
+                    KPRF_ATOMIC_SET32(&pHdr->cbModSegs, off + cbModSeg);
+                    off += pHdr->offModSegs;
+                }
+                else
+                    off = 0;
+                KPRF_MODSEGS_UNLOCK();
+                return off;
+            }
+            KPRF_MODSEGS_UNLOCK();
+            /* someone raced us, check the new entries. */
+        }
+
+        /*
+         * Match?
+         */
+        KPRF_TYPE(PC,MODSEG) pCur = KPRF_OFF2PTR(P,MODSEG, off + pHdr->offModSegs, pHdr);
+        if (    pCur->uBasePtr          == pModSeg->uBasePtr
+            &&  pCur->fLoaded           == pModSeg->fLoaded
+            &&  pCur->cchPath           == pModSeg->cchPath
+            &&  pCur->iSegment          == pModSeg->iSegment
+            &&  pCur->cbSegmentMinusOne == pModSeg->cbSegmentMinusOne
+           )
+        {
+            int32_t iPath = pModSeg->cchPath;
+
+            for (;;)
+            {
+                if (!iPath--)
+                    return off + pHdr->offModSegs;
+                if (pModSeg->szPath[iPath] != pCur->szPath[iPath])
+                    break;
+            }
+            /* didn't match, continue searching */
+        }
+        uint32_t cbCur = KPRF_OFFSETOF(MODSEG, szPath[pCur->cchPath + 1]);
+        off += KPRF_ALIGN(cbCur, KPRF_SIZEOF(UPTR));
+    }
+}
+
+
+/**
+ * Queries data for and inserts a new module segment.
+ *
+ *
+ * @returns Offset to the module if existing or successfully added
+ * @returns 0 if not found.
+ *
+ * @param   pHdr        The profiler header.
+ * @param   uPC         Address within the module.
+ * @param   off         The offset into the modseg area which has been searched.
+ *                      (This is relative to the first moddule segment record (at pHdr->offModSegs).)
+ */
+static uint32_t KPRF_NAME(NewModSeg)(KPRF_TYPE(P,HDR) pHdr, KPRF_TYPE(,UPTR) uPC, uint32_t off)
+{
+    /*
+     * Query the module name and object of the function.
+     */
+#pragma pack(1)
+    struct
+    {
+        KPRF_TYPE(,MODSEG) ModSeg;
+        char               szMorePath[260];
+    } s;
+#pragma pack()
+    if (KPRF_GET_MODSEG(uPC + pHdr->uBasePtr, s.ModSeg.szPath, sizeof(s.ModSeg.szPath) + sizeof(s.szMorePath),
+                        &s.ModSeg.iSegment, &s.ModSeg.uBasePtr, &s.ModSeg.cbSegmentMinusOne))
+        return 0;
+    s.ModSeg.uBasePtr -= pHdr->uBasePtr;
+    s.ModSeg.fLoaded = 1;
+    s.ModSeg.cchPath = 0;
+    while (s.ModSeg.szPath[s.ModSeg.cchPath])
+        s.ModSeg.cchPath++;
+
+    return KPRF_NAME(InsertModSeg)(pHdr, &s.ModSeg, off);
+}
+
+
+/**
+ * Record a module segment.
+ *
+ * This is an internal worker for recording a module segment when adding
+ * a new function.
+ *
+ * @returns Offset to the module if existing or successfully added
+ * @returns 0 if not found.
+ *
+ * @param   pHdr        The profiler header.
+ * @param   uPC         Address within the module.
+ */
+static uint32_t KPRF_NAME(RecordModSeg)(KPRF_TYPE(P,HDR) pHdr, KPRF_TYPE(,UPTR) uPC)
+{
+    /*
+     * Lookup the module segment, inserting it if not found (and there is room).
+     */
+    uint32_t off = 0;
+    KPRF_TYPE(PC,MODSEG) pCur = KPRF_OFF2PTR(P,MODSEG, pHdr->offModSegs, pHdr);
+    const uint32_t cbModSegs = pHdr->cbModSegs;
+    for (;;)
+    {
+        /* done and not found? */
+        if (off >= cbModSegs)
+            return KPRF_NAME(NewModSeg)(pHdr, uPC, off);
+
+        /*
+         * Match?
+         */
+        if (    pCur->fLoaded
+            &&  uPC - pCur->uBasePtr <= pCur->cbSegmentMinusOne)
+            return off + pHdr->offModSegs;
+
+        uint32_t cbCur = KPRF_OFFSETOF(MODSEG, szPath[pCur->cchPath + 1]);
+        cbCur = KPRF_ALIGN(cbCur, KPRF_SIZEOF(UPTR));
+        off += cbCur;
+        pCur = (KPRF_TYPE(PC,MODSEG))((uint8_t *)pCur + cbCur);
+    }
+}
+
Index: trunk/kProfile/prfcorepost.cpp.h
===================================================================
--- trunk/kProfile/prfcorepost.cpp.h	(revision 3524)
+++ trunk/kProfile/prfcorepost.cpp.h	(revision 3524)
@@ -0,0 +1,37 @@
+/* $Id: $ */
+/** @file
+ *
+ * kProfiler Core Post Code Header (template).
+ *
+ * Copyright (c) 2006 knut st. osmundsen <bird-src-spam@anduin.net.de>
+ *
+ *
+ * This file is part of kLIBC.
+ *
+ * kLIBC is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * kLIBC is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with kLIBC; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+
+/*
+ * Clean up all our defines.
+ */
+#undef KPRF_OFFSETOF
+#undef KPRF_ALIGN
+#undef KPRF_SETMIN_ALIGN
+#undef KPRF_PTR2OFF
+#undef KPRF_OFF2PTREx
+#undef KPRF_OFF2PTR
+
Index: trunk/kProfile/prfcorepre.cpp.h
===================================================================
--- trunk/kProfile/prfcorepre.cpp.h	(revision 3524)
+++ trunk/kProfile/prfcorepre.cpp.h	(revision 3524)
@@ -0,0 +1,198 @@
+/* $Id: $ */
+/** @file
+ *
+ * kProfiler - Core Pre Code (template).
+ *
+ * Copyright (c) 2006 knut st. osmundsen <bird-src-spam@anduin.net.de>
+ *
+ *
+ * This file is part of kLIBC.
+ *
+ * kLIBC is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * kLIBC is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with kLIBC; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+
+/** @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) + (KPRF_TYPE(,UPTR))pHdr) )
+
+/** @def KPRF_PTR2OFF
+ * Internal helper for converting a pointer to a offset.
+ * @internal
+ */
+#define KPRF_PTR2OFF(ptr, pHdr) \
+    ( (KPRF_TYPE(,UPTR))(ptr) - (KPRF_TYPE(,UPTR))(pHdr) )
+
+/** @def KPRF_ALIGN
+ * The usual align macro.
+ * @internal
+ */
+#define KPRF_ALIGN(n, align) ( ((n) + ( (align) - 1)) & ~((align) - 1) )
+
+/** @def KPRF_SETMIN_ALIGN
+ * Ensures a minimum and aligned value.
+ * @internal
+ */
+#define KPRF_SETMIN_ALIGN(n, min, align) \
+    do { \
+        if ((n) < (min)) \
+            (n) = (min); \
+        else { \
+            const uint32_t u32 = ((n) + ( (align) - 1)) & ~((align) - 1); \
+            if (u32 >= (n)) \
+                (n) = u32; \
+        } \
+    } while (0)
+
+/** @def KPRF_OFFSETOF
+ * My usual extended OFFSETOF macro, except this returns uint32_t and mangles the type name.
+ * @internal
+ */
+#define KPRF_OFFSETOF(kPrfType, Member) ( (uint32_t)(uintptr_t)&((KPRF_TYPE(P,kPrfType))0)->Member )
+
+/** @def PRF_SIZEOF
+ * Size of a kPrf type.
+ * @internal
+ */
+#define KPRF_SIZEOF(kPrfType)       sizeof(KPRF_TYPE(,kPrfType))
+
+
+/** @def KPRF_NOW
+ * Gets the current timestamp.
+ */
+#ifndef KPRF_NOW
+# error "KPRF_NOW isn't defined!"
+#endif
+
+/** @def KRPF_IS_ACTIVE
+ * Checks if profiling is activated or not.
+ * The idea is to use some global variable for disabling and enabling
+ * profiling in order to deal with init/term issues.
+ */
+#ifndef KPRF_IS_ACTIVE
+# define KPRF_IS_ACTIVE()           1
+#endif
+
+/** @def KPRF_GET_HDR
+ * Gets the pointer to the profiler data header.
+ */
+#ifndef KPRF_GET_HDR
+# error "KPRF_GET_HDR isn't defined!"
+#endif
+
+/** @def KPRF_GET_THREADID
+ * Gets native thread id. This must be unique.
+ */
+#ifndef KPRF_GET_THREADID
+# error "KPRF_GET_THREADID isn't defined!"
+#endif
+
+/** @def KPRF_SET_THREAD
+ * Sets the pointer to the current thread so we can get to it
+ * without doing a linear search by thread id.
+ */
+#ifndef KPRF_SET_THREAD
+# error "KPRF_SET_THREAD isn't defined!"
+#endif
+
+/** @def KPRF_GET_THREAD
+ * Gets the pointer to the current thread as set by KPRF_SET_THREAD.
+ */
+#ifndef KPRF_GET_THREAD
+# error "KPRF_GET_THREAD isn't defined!"
+#endif
+
+/** @def KPRF_MODSEGS_LOCK
+ * Lock the module segment for updating.
+ */
+#ifndef KPRF_MODSEGS_LOCK
+# define KPRF_MODSEGS_LOCK()            do { } while (0)
+#endif
+
+/** @def KPRF_MODSEGS_UNLOCK
+ * Unlock the module segments.
+ */
+#ifndef KPRF_MODSEGS_UNLOCK
+# define KPRF_MODSEGS_UNLOCK()          do { } while (0)
+#endif
+
+/** @def KPRF_THREADS_LOCK
+ * Lock the threads for updating.
+ */
+#ifndef KPRF_THREADS_LOCK
+# define KPRF_THREADS_LOCK()            do { } while (0)
+#endif
+
+/** @def KPRF_THREADS_UNLOCK
+ * Unlock the threads.
+ */
+#ifndef KPRF_THREADS_UNLOCK
+# define KPRF_THREADS_UNLOCK()          do { } while (0)
+#endif
+
+/** @def KPRF_FUNCS_READ_LOCK
+ * Lock the functions for reading.
+ */
+#ifndef KPRF_FUNCS_READ_LOCK
+# define KPRF_FUNCS_READ_LOCK()         do { } while (0)
+#endif
+
+/** @def KPRF_FUNCS_READ_UNLOCK
+ * Releases a read lock on the functions.
+ */
+#ifndef KPRF_FUNCS_READ_UNLOCK
+# define KPRF_FUNCS_READ_UNLOCK()       do { } while (0)
+#endif
+
+/** @def KPRF_FUNCS_WRITE_LOCK
+ * Lock the functions for updating.
+ */
+#ifndef KPRF_FUNCS_WRITE_LOCK
+# define KPRF_FUNCS_WRITE_LOCK()        do { } while (0)
+#endif
+
+/** @def KPRF_FUNCS_WRITE_UNLOCK
+ * Releases a write lock on the functions.
+ */
+#ifndef KPRF_FUNCS_WRITE_UNLOCK
+# define KPRF_FUNCS_WRITE_UNLOCK()      do { } while (0)
+#endif
+
+
+/** @def KPRF_ATOMIC_SET32
+ * Atomically set a 32-bit value.
+ */
+#ifndef KPRF_ATOMIC_SET32
+# define KPRF_ATOMIC_SET32(pu32, u32)   do { *(pu32) = (u32); } while (0)
+#endif
+
+/** @def KPRF_ATOMIC_SET64
+ * Atomically (well, in a safe way) adds to a 64-bit value.
+ */
+#ifndef KPRF_ATOMIC_ADD64
+# define KPRF_ATOMIC_ADD64(pu64, u64)   do { *(pu64) += (u64); } while (0)
+#endif
+
+/** @def KPRF_ATOMIC_SET64
+ * Atomically (well, in a safe way) increments a 64-bit value.
+ */
+#ifndef KPRF_ATOMIC_INC64
+# define KPRF_ATOMIC_INC64(pu64)        KPRF_ATOMIC_ADD64(pu64, 1)
+#endif
+
Index: trunk/kProfile/prfcorereloc.cpp.h
===================================================================
--- trunk/kProfile/prfcorereloc.cpp.h	(revision 3524)
+++ trunk/kProfile/prfcorereloc.cpp.h	(revision 3524)
@@ -0,0 +1,43 @@
+/* $Id: $ */
+/** @file
+ *
+ * kProfiler - core Relocation Code (template).
+ *
+ * Copyright (c) 2006 knut st. osmundsen <bird-src-spam@anduin.net.de>
+ *
+ *
+ * This file is part of kLIBC.
+ *
+ * kLIBC is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * kLIBC is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with kLIBC; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+
+/**
+ * Set (or modify) the base pointer for the profiler.
+ *
+ * The purpose of the base pointer is to allow profiling of relocatable code. Set the
+ * base pointer right after initializing the data set, and update it when relocating
+ * the code (both by calling this function), and Bob's your uncle! :-)
+ *
+ * @param   pHdr        The header returned from the initializer.
+ * @param   uBasePtr    The new base pointer value.
+ */
+KPRF_DECL_FUNC(void, SetBasePtr)(KPRF_TYPE(P,HDR) pHdr, KPRF_TYPE(,UPTR) uBasePtr)
+{
+    pHdr->uBasePtr = uBasePtr;
+}
+
+
Index: trunk/kProfile/prfcoreterm.cpp.h
===================================================================
--- trunk/kProfile/prfcoreterm.cpp.h	(revision 3524)
+++ trunk/kProfile/prfcoreterm.cpp.h	(revision 3524)
@@ -0,0 +1,138 @@
+/* $Id: $ */
+/** @file
+ *
+ * kProfiler - Core Termination Code (template).
+ *
+ * Copyright (c) 2006 knut st. osmundsen <bird-src-spam@anduin.net.de>
+ *
+ *
+ * This file is part of kLIBC.
+ *
+ * kLIBC is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * kLIBC is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with kLIBC; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+
+/**
+ * Unwinds and terminates all the threads, and frees the stack space.
+ *
+ * @returns The new data set size. (pHdr->cb)
+ * @param   pHdr        The profiler data set header.
+ */
+KPRF_DECL_FUNC(uint32_t, TerminateAll)(KPRF_TYPE(P,HDR) pHdr)
+{
+    uint64_t TS = KPRF_NOW();
+    if (!pHdr)
+        return 0;
+
+    /*
+     * Iterate the threads and terminate all which are non-terminated.
+     */
+    KPRF_TYPE(P,THREAD) paThread = KPRF_OFF2PTR(P,THREAD, pHdr->offThreads, pHdr);
+    for (uint32_t i = 0; i < pHdr->cThreads; i++)
+    {
+        KPRF_TYPE(P,THREAD) pCur = &paThread[i];
+        switch (pCur->enmState)
+        {
+            /* these states needs no work. */
+            case KPRF_TYPE(,THREADSTATE_TERMINATED):
+            case KPRF_TYPE(,THREADSTATE_UNUSED):
+            default:
+                break;
+
+            /* these are active and requires unwinding.*/
+            case KPRF_TYPE(,THREADSTATE_ACTIVE):
+            case KPRF_TYPE(,THREADSTATE_SUSPENDED):
+            case KPRF_TYPE(,THREADSTATE_OVERFLOWED):
+                KPRF_NAME(TerminateThread)(pHdr, pCur, TS);
+                break;
+        }
+    }
+
+
+    /*
+     * Free the stacks.
+     */
+    if (pHdr->offStacks)
+    {
+        /* only if the stack is at the end of the data set. */
+        const uint32_t cbStacks = KPRF_ALIGN(pHdr->cMaxStacks * pHdr->cbStack, 32);
+        if (pHdr->offStacks + cbStacks == pHdr->cb)
+            pHdr->cb -= cbStacks;
+        pHdr->offStacks = 0;
+    }
+
+    return pHdr->cb;
+}
+
+
+/**
+ * Sets the commandline.
+ *
+ * This is typically done after TerminateAll, when the stacks has
+ * been freed up and there is plenty free space.
+ *
+ * @returns The new data set size. (pHdr->cb)
+ * @param   pHdr        The profiler data set header.
+ * @param   cArgs       The number of arguments in the array.
+ * @param   papszArgs   Pointer to an array of arguments.
+ */
+KPRF_DECL_FUNC(uint32_t, SetCommandLine)(KPRF_TYPE(P,HDR) pHdr, unsigned cArgs, const char * const *papszArgs)
+{
+    if (!pHdr)
+        return 0;
+
+    /*
+     * Any space at all?
+     */
+    if (pHdr->cb + 16 > pHdr->cbAllocated) /* 16 bytes min */
+        return pHdr->cb;
+
+    /*
+     * Encode untill we run out of space.
+     */
+    pHdr->offCommandLine = pHdr->cb;
+    char *psz = (char *)pHdr + pHdr->cb;
+    char *pszMax = (char *)pHdr + pHdr->cbAllocated - 1;
+    for (unsigned i = 0; i < cArgs && psz + 7 < pszMax; i++)
+    {
+        if (i > 0)
+            *psz++ = ' ';
+        *psz++ = '\'';
+        const char *pszArg = papszArgs[i];
+        while (psz < pszMax)
+        {
+            char ch = *pszArg++;
+            if (!ch)
+                break;
+            if (ch == '\'')
+            {
+                if (psz + 1 >= pszMax)
+                    break;
+                *psz++ = '\\';
+            }
+            *psz++ = ch;
+        }
+        if (psz < pszMax)
+            *psz++ = '\'';
+    }
+    *psz++ = '\0';
+    pHdr->cb = psz - (char *)pHdr;
+    pHdr->cchCommandLine = pHdr->cb - pHdr->offCommandLine - 1;
+
+    return pHdr->cb;
+}
+
+
Index: trunk/kProfile/prfreader.cpp.h
===================================================================
--- trunk/kProfile/prfreader.cpp.h	(revision 3524)
+++ trunk/kProfile/prfreader.cpp.h	(revision 3524)
@@ -0,0 +1,1576 @@
+/* $Id: $ */
+/** @file
+ *
+ * kProfiler Mark 2 - Reader template.
+ *
+ * Copyright (c) 2006 knut st. osmundsen <bird-src-spam@anduin.net.de>
+ *
+ *
+ * This file is part of kLIBC.
+ *
+ * kLIBC is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * kLIBC is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with kLIBC; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+
+/**
+ * Validates the non-header parts of a data-set.
+ *
+ * @returns true if valid.
+ * @returns false if invalid. (written description to pOut)
+ *
+ * @param   pHdr        Pointer to the data set.
+ * @param   cb          The size of the data set.
+ * @param   pOut        Where to write error messages.
+ */
+static bool KPRF_NAME(IsValid)(KPRF_TYPE(PC,HDR) pHdr, uint32_t cb, FILE *pOut)
+{
+    KPRF_TYPE(,UPTR) uMaxPtr = ~(KPRF_TYPE(,UPTR))0 - pHdr->uBasePtr;
+
+    /*
+     * Iterate the module segments.
+     */
+    uint32_t off = pHdr->offModSegs;
+    while (off < pHdr->offModSegs + pHdr->cbModSegs)
+    {
+        KPRF_TYPE(PC,MODSEG) pCur = KPRF_OFF2PTR(PC,MODSEG, off, pHdr);
+        uint32_t cbCur = KPRF_OFFSETOF(MODSEG, szPath[pCur->cchPath + 1]);
+        cbCur = KPRF_ALIGN(cbCur, KPRF_SIZEOF(UPTR));
+        if (cbCur + off > pHdr->offModSegs + pHdr->cbModSegs)
+        {
+            fprintf(pOut, "The module segment record at 0x%x is too long!\n", off);
+            return false;
+        }
+        if (pCur->uBasePtr > uMaxPtr)
+            fprintf(pOut, "warning: The module segment record at 0x%x has a too high base address.\n", off);
+
+        if (strlen(pCur->szPath) != pCur->cchPath)
+        {
+            fprintf(pOut, "The module segment record at 0x%x has an invalid path length 0x%x it the actual length is 0x%x\n",
+                    pCur->cchPath, strlen(pCur->szPath));
+            return false;
+        }
+
+        /* next */
+        off += cbCur;
+    }
+
+
+    /*
+     * Iterate the functions.
+     */
+    KPRF_TYPE(PC,FUNC) paFuncs = KPRF_OFF2PTR(PC,FUNC, pHdr->offFunctions, pHdr);
+    for (uint32_t i = 0; i < pHdr->cFunctions; i++)
+    {
+        KPRF_TYPE(PC,FUNC) pCur = &paFuncs[i];
+        if (pCur->uEntryPtr > uMaxPtr)
+            fprintf(pOut, "warning: Function 0x%x has a too high base address.\n", i);
+        if (pCur->offModSeg)
+        {
+            if (    pCur->offModSeg < pHdr->offModSegs
+                ||  pCur->offModSeg >= pHdr->offModSegs + pHdr->cbModSegs
+                ||  pCur->offModSeg != KPRF_ALIGN(pCur->offModSeg, sizeof(pCur->uEntryPtr))
+               )
+            {
+                fprintf(pOut, "Function 0x%x has an invalid offModSeg value (0x%x).\n", i, pCur->offModSeg);
+                return false;
+            }
+            /** @todo more validation here.. */
+        }
+    }
+
+
+    /*
+     * Validate the threads.
+     */
+    KPRF_TYPE(PC,THREAD) paThreads = KPRF_OFF2PTR(PC,THREAD, pHdr->offThreads, pHdr);
+    for (uint32_t i = 0; i < pHdr->cThreads; i++)
+    {
+        KPRF_TYPE(PC,THREAD) pCur = &paThreads[i];
+        if (pCur->uStackBasePtr > uMaxPtr)
+            fprintf(pOut, "warning: Thread 0x%x has a too high base address.\n", i);
+        switch (pCur->enmState)
+        {
+            case KPRF_TYPE(,THREADSTATE_ACTIVE):
+            case KPRF_TYPE(,THREADSTATE_SUSPENDED):
+            case KPRF_TYPE(,THREADSTATE_OVERFLOWED):
+            case KPRF_TYPE(,THREADSTATE_TERMINATED):
+                break;
+            default:
+                fprintf(pOut, "Thread 0x%x has an invalid state value (0x%x).\n", i, pCur->enmState);
+                return false;
+        }
+    }
+
+
+    return true;
+}
+
+
+/**
+ * Dumps a file of a particular format.
+ *
+ * @returns 0 on success. (you might want to check the pOut state)
+ * @returns -1 on failure.
+ *
+ * @param   pHdr        Pointer to the data set.
+ * @param   pOut        The output file. This is opened for text writing.
+ * @param   pReader     The reader object.
+ */
+static int KPRF_NAME(Dump)(KPRF_TYPE(PC,HDR) pHdr, FILE *pOut)
+{
+    /*
+     * Any commandline?
+     */
+    if (pHdr->offCommandLine)
+        fprintf(pOut,
+                "Commandline: %s (%d  bytes)\n",
+                (char *)KPRF_OFF2PTR(PC,MODSEG, pHdr->offCommandLine, pHdr), /* stupid, stupid, type hacking. */
+                pHdr->cchCommandLine);
+
+    /*
+     * Dump the module segments.
+     */
+    fprintf(pOut,
+            "Module Segments: off=0x%x 0x%x/0x%x (bytes)\n"
+            "----------------\n",
+            pHdr->offModSegs, pHdr->cbModSegs, pHdr->cbMaxModSegs);
+    uint32_t off = pHdr->offModSegs;
+    while (off < pHdr->offModSegs + pHdr->cbModSegs)
+    {
+        KPRF_TYPE(PC,MODSEG) pCur = KPRF_OFF2PTR(PC,MODSEG, off, pHdr);
+        uint32_t cbCur = KPRF_OFFSETOF(MODSEG, szPath[pCur->cchPath + 1]);
+        cbCur = KPRF_ALIGN(cbCur, KPRF_SIZEOF(UPTR));
+
+        fprintf(pOut,
+                "0x%04x: iSegment=0x%08x uBasePtr=%" KPRF_FMT_UPTR " szPath='%s' (%d bytes)\n",
+                off, pCur->iSegment, pCur->uBasePtr, pCur->szPath, pCur->cchPath);
+
+        /* next */
+        off += cbCur;
+    }
+    fprintf(pOut, "\n");
+
+    /*
+     * Dump the functions.
+     */
+    fprintf(pOut,
+            "Functions: off=0x%x 0x%x/0x%x\n"
+            "----------\n",
+            pHdr->offFunctions, pHdr->cFunctions, pHdr->cMaxFunctions);
+    KPRF_TYPE(PC,FUNC) paFuncs = KPRF_OFF2PTR(PC,FUNC, pHdr->offFunctions, pHdr);
+    for (uint32_t i = 0; i < pHdr->cFunctions; i++)
+    {
+        KPRF_TYPE(PC,FUNC) pCur = &paFuncs[i];
+        fprintf(pOut, "0x%04x: uEntryPtr=%" KPRF_FMT_UPTR " cOnStack=0x%" KPRF_FMT_X64 " cCalls=0x%" KPRF_FMT_X64 "\n"
+                      "       OnStack={0x%" KPRF_FMT_X64 ", 0x%" KPRF_FMT_X64 ", 0x%" KPRF_FMT_X64 "}\n"
+                      "  OnTopOfStack={0x%" KPRF_FMT_X64 ", 0x%" KPRF_FMT_X64 ", 0x%" KPRF_FMT_X64 "}\n",
+                i, pCur->uEntryPtr, pCur->cOnStack, pCur->cCalls,
+                pCur->OnStack.MinTicks, pCur->OnStack.MaxTicks,  pCur->OnStack.SumTicks,
+                pCur->OnTopOfStack.MinTicks, pCur->OnTopOfStack.MaxTicks,  pCur->OnTopOfStack.SumTicks);
+        if (pCur->offModSeg)
+        {
+            KPRF_TYPE(PC,MODSEG) pModSeg = KPRF_OFF2PTR(PC,MODSEG, pCur->offModSeg, pHdr);
+            fprintf(pOut, "  offModSeg=0x%08x iSegment=0x%02x uBasePtr=%" KPRF_FMT_UPTR " szPath='%s' (%d bytes)\n",
+                    pCur->offModSeg, pModSeg->iSegment, pModSeg->uBasePtr, pModSeg->szPath, pModSeg->cchPath);
+
+#if 1
+            PRTDBGMOD pMod;
+            int rc = RTDbgModuleOpen(pModSeg->szPath, &pMod);
+            if (RT_SUCCESS(rc))
+            {
+                RTDBGSYMBOL Sym;
+                rc = RTDbgModuleQuerySymbol(pMod, pModSeg->iSegment, (RTUINTPTR)(pCur->uEntryPtr - pModSeg->uBasePtr), &Sym);
+                if (RT_SUCCESS(rc))
+                {
+                    fprintf(pOut, "  %s\n", Sym.szName);
+                }
+                RTDbgModuleClose(pMod);
+            }
+#endif
+
+        }
+    }
+    fprintf(pOut, "\n");
+
+    /*
+     * Dump the threads.
+     */
+    fprintf(pOut,
+            "Threads: off=0x%x 0x%x/0x%x (Stacks=0x%x/0x%x cMaxStackFrames=0x%x)\n"
+            "--------\n",
+            pHdr->offThreads, pHdr->cThreads, pHdr->cMaxThreads, pHdr->cStacks, pHdr->cMaxStacks, pHdr->cMaxStackFrames);
+    KPRF_TYPE(PC,THREAD) paThreads = KPRF_OFF2PTR(PC,THREAD, pHdr->offThreads, pHdr);
+    for (uint32_t i = 0; i < pHdr->cThreads; i++)
+    {
+        KPRF_TYPE(PC,THREAD) pCur = &paThreads[i];
+        fprintf(pOut,
+                "0x%02x: ThreadId=0x%08" KPRF_FMT_X64 " enmState=%d szName='%s'\n"
+                "  uStackBasePtr=%" KPRF_FMT_UPTR " cbMaxStack=%" KPRF_FMT_UPTR "\n"
+                "  cCalls=0x%" KPRF_FMT_X64 " cOverflows=0x%" KPRF_FMT_X64 " cStackSwitchRejects=0x%" KPRF_FMT_X64 "\n"
+                "  cUnwinds=0x%" KPRF_FMT_X64 " ProfiledTicks=0x%" KPRF_FMT_X64 " OverheadTicks=0x%" KPRF_FMT_X64 "\n",
+                i, pCur->ThreadId, pCur->enmState, pCur->szName,
+                pCur->uStackBasePtr, pCur->cbMaxStack,
+                pCur->cCalls, pCur->cOverflows, pCur->cStackSwitchRejects,
+                pCur->cUnwinds, pCur->ProfiledTicks, pCur->OverheadTicks);
+    }
+
+    return 0;
+}
+
+
+/** Pointer to a report module.
+ * @internal */
+typedef struct KPRF_TYPE(,REPORTMOD) *KPRF_TYPE(P,REPORTMOD);
+/** Pointer to a report module segment.
+ * @internal */
+typedef struct KPRF_TYPE(,REPORTMODSEG) *KPRF_TYPE(P,REPORTMODSEG);
+
+
+/**
+ * A report module segment.
+ *
+ * @internal
+ */
+typedef struct KPRF_TYPE(,REPORTMODSEG)
+{
+    /** AVL node core. The key is the data set offset of the module segment record. */
+    AVLU32NODECORE              Core;
+    /** Pointer to the next segment for the module. */
+    KPRF_TYPE(P,REPORTMODSEG)   pNext;
+    /** Pointer to the module segment data in the data set. */
+    KPRF_TYPE(PC,MODSEG)        pModSeg;
+    /** Pointer to the module this segment belongs to. */
+    KPRF_TYPE(P,REPORTMOD)      pMod;
+    /** The time this segment has spent on the stack.. */
+    uint64_t                    OnStackTicks;
+    /** The time this segment has spent on the top of the stack.. */
+    uint64_t                    OnTopOfStackTicks;
+    /** The number of profiled functions from this segment. */
+    uint32_t                    cFunctions;
+} KPRF_TYPE(,REPORTMODSEG), *KPRF_TYPE(P,REPORTMODSEG);
+
+
+/**
+ * A report module segment.
+ *
+ * @internal
+ */
+typedef struct KPRF_TYPE(,REPORTMOD)
+{
+    /** The module number. */
+    uint32_t                    iMod;
+    /** Pointer to the next module in the list. */
+    KPRF_TYPE(P,REPORTMOD)      pNext;
+    /** Pointer to the list of segments belonging to this module. */
+    KPRF_TYPE(P,REPORTMODSEG)   pFirstSeg;
+    /** The debug module handle. */
+    PRTDBGMOD                   pDbgMod;
+    /** The time this segment has spent on the stack.. */
+    uint64_t                    OnStackTicks;
+    /** The time this segment has spent on the top of the stack.. */
+    uint64_t                    OnTopOfStackTicks;
+    /** The number of profiled functions from this segment. */
+    uint32_t                    cFunctions;
+} KPRF_TYPE(,REPORTMOD), *KPRF_TYPE(P,REPORTMOD);
+
+
+/**
+ * A report function.
+ *
+ * @internal
+ */
+typedef struct KPRF_TYPE(,REPORTFUNC)
+{
+    /** Pointer to the function data in the data set. */
+    KPRF_TYPE(PC,FUNC)          pFunc;
+    /** Pointer to the module segment this function belongs to. (can be NULL) */
+    KPRF_TYPE(P,REPORTMODSEG)   pModSeg;
+    /** Pointer to the function symbol. */
+    PRTDBGSYMBOL                pSym;
+    /** Pointer to the function line number. */
+    PRTDBGLINE                  pLine;
+} KPRF_TYPE(,REPORTFUNC), *KPRF_TYPE(P,REPORTFUNC);
+
+
+/**
+ * Compares two REPROTFUNC records to determin which has the higher on-stack time.
+ */
+static int KPRF_NAME(FuncCompareOnStack)(const void *pv1, const void *pv2)
+{
+    KPRF_TYPE(PC,FUNC) p1 = (*(KPRF_TYPE(P,REPORTFUNC) *)pv1)->pFunc;
+    KPRF_TYPE(PC,FUNC) p2 = (*(KPRF_TYPE(P,REPORTFUNC) *)pv2)->pFunc;
+    if (p1->OnStack.SumTicks > p2->OnStack.SumTicks)
+        return -1;
+    if (p1->OnStack.SumTicks < p2->OnStack.SumTicks)
+        return 1;
+    if (p1->OnStack.MaxTicks > p2->OnStack.MaxTicks)
+        return -1;
+    if (p1->OnStack.MaxTicks < p2->OnStack.MaxTicks)
+        return 1;
+    if (p1->OnStack.MinTicks > p2->OnStack.MinTicks)
+        return -1;
+    if (p1->OnStack.MinTicks < p2->OnStack.MinTicks)
+        return 1;
+    if (p1 < p2)
+        return -1;
+    return 1;
+}
+
+
+/**
+ * Compares two REPROTFUNC records to determin which has the higher on-stack average time.
+ */
+static int KPRF_NAME(FuncCompareOnStackAvg)(const void *pv1, const void *pv2)
+{
+    KPRF_TYPE(PC,FUNC) p1 = (*(KPRF_TYPE(P,REPORTFUNC) *)pv1)->pFunc;
+    KPRF_TYPE(PC,FUNC) p2 = (*(KPRF_TYPE(P,REPORTFUNC) *)pv2)->pFunc;
+    if (p1->OnStack.SumTicks / p1->cOnStack > p2->OnStack.SumTicks / p2->cOnStack)
+        return -1;
+    if (p1->OnStack.SumTicks / p1->cOnStack < p2->OnStack.SumTicks / p2->cOnStack)
+        return 1;
+    return KPRF_NAME(FuncCompareOnStack)(pv1, pv2);
+}
+
+
+/**
+ * Compares two REPROTFUNC records to determin which has the higher on-stack min time.
+ */
+static int KPRF_NAME(FuncCompareOnStackMin)(const void *pv1, const void *pv2)
+{
+    KPRF_TYPE(PC,FUNC) p1 = (*(KPRF_TYPE(P,REPORTFUNC) *)pv1)->pFunc;
+    KPRF_TYPE(PC,FUNC) p2 = (*(KPRF_TYPE(P,REPORTFUNC) *)pv2)->pFunc;
+    if (p1->OnStack.MinTicks > p2->OnStack.MinTicks)
+        return -1;
+    if (p1->OnStack.MinTicks < p2->OnStack.MinTicks)
+        return 1;
+    return KPRF_NAME(FuncCompareOnStack)(pv1, pv2);
+}
+
+
+/**
+ * Compares two REPROTFUNC records to determin which has the higher on-stack max time.
+ */
+static int KPRF_NAME(FuncCompareOnStackMax)(const void *pv1, const void *pv2)
+{
+    KPRF_TYPE(PC,FUNC) p1 = (*(KPRF_TYPE(P,REPORTFUNC) *)pv1)->pFunc;
+    KPRF_TYPE(PC,FUNC) p2 = (*(KPRF_TYPE(P,REPORTFUNC) *)pv2)->pFunc;
+    if (p1->OnStack.MaxTicks > p2->OnStack.MaxTicks)
+        return -1;
+    if (p1->OnStack.MaxTicks < p2->OnStack.MaxTicks)
+        return 1;
+    return KPRF_NAME(FuncCompareOnStack)(pv1, pv2);
+}
+
+
+/**
+ * Compares two REPROTFUNC records to determin which has the higher on-stack time.
+ */
+static int KPRF_NAME(FuncCompareOnTopOfStack)(const void *pv1, const void *pv2)
+{
+    KPRF_TYPE(PC,FUNC) p1 = (*(KPRF_TYPE(P,REPORTFUNC) *)pv1)->pFunc;
+    KPRF_TYPE(PC,FUNC) p2 = (*(KPRF_TYPE(P,REPORTFUNC) *)pv2)->pFunc;
+    if (p1->OnTopOfStack.SumTicks > p2->OnTopOfStack.SumTicks)
+        return -1;
+    if (p1->OnTopOfStack.SumTicks < p2->OnTopOfStack.SumTicks)
+        return 1;
+    if (p1->OnTopOfStack.MaxTicks > p2->OnTopOfStack.MaxTicks)
+        return -1;
+    if (p1->OnTopOfStack.MaxTicks < p2->OnTopOfStack.MaxTicks)
+        return 1;
+    if (p1->OnTopOfStack.MinTicks > p2->OnTopOfStack.MinTicks)
+        return -1;
+    if (p1->OnTopOfStack.MinTicks < p2->OnTopOfStack.MinTicks)
+        return 1;
+    if (p1 < p2)
+        return -1;
+    return 1;
+}
+
+
+/**
+ * Compares two REPROTFUNC records to determin which has the higher on-stack average time.
+ */
+static int KPRF_NAME(FuncCompareOnTopOfStackAvg)(const void *pv1, const void *pv2)
+{
+    KPRF_TYPE(PC,FUNC) p1 = (*(KPRF_TYPE(P,REPORTFUNC) *)pv1)->pFunc;
+    KPRF_TYPE(PC,FUNC) p2 = (*(KPRF_TYPE(P,REPORTFUNC) *)pv2)->pFunc;
+    if (p1->OnTopOfStack.SumTicks / p1->cOnStack > p2->OnTopOfStack.SumTicks / p2->cOnStack)
+        return -1;
+    if (p1->OnTopOfStack.SumTicks / p1->cOnStack < p2->OnTopOfStack.SumTicks / p2->cOnStack)
+        return 1;
+    return KPRF_NAME(FuncCompareOnTopOfStack)(pv1, pv2);
+}
+
+
+/**
+ * Compares two REPROTFUNC records to determin which has the higher on-stack min time.
+ */
+static int KPRF_NAME(FuncCompareOnTopOfStackMin)(const void *pv1, const void *pv2)
+{
+    KPRF_TYPE(PC,FUNC) p1 = (*(KPRF_TYPE(P,REPORTFUNC) *)pv1)->pFunc;
+    KPRF_TYPE(PC,FUNC) p2 = (*(KPRF_TYPE(P,REPORTFUNC) *)pv2)->pFunc;
+    if (p1->OnTopOfStack.MinTicks > p2->OnTopOfStack.MinTicks)
+        return -1;
+    if (p1->OnTopOfStack.MinTicks < p2->OnTopOfStack.MinTicks)
+        return 1;
+    return KPRF_NAME(FuncCompareOnTopOfStack)(pv1, pv2);
+}
+
+
+/**
+ * Compares two REPROTFUNC records to determin which has the higher on-stack min time.
+ */
+static int KPRF_NAME(FuncCompareOnTopOfStackMax)(const void *pv1, const void *pv2)
+{
+    KPRF_TYPE(PC,FUNC) p1 = (*(KPRF_TYPE(P,REPORTFUNC) *)pv1)->pFunc;
+    KPRF_TYPE(PC,FUNC) p2 = (*(KPRF_TYPE(P,REPORTFUNC) *)pv2)->pFunc;
+    if (p1->OnTopOfStack.MaxTicks > p2->OnTopOfStack.MaxTicks)
+        return -1;
+    if (p1->OnTopOfStack.MaxTicks < p2->OnTopOfStack.MaxTicks)
+        return 1;
+    return KPRF_NAME(FuncCompareOnTopOfStack)(pv1, pv2);
+}
+
+
+/**
+ * Compares two REPROTFUNC records to determin which has the higher call to count.
+ */
+static int KPRF_NAME(FuncCompareCallsTo)(const void *pv1, const void *pv2)
+{
+    KPRF_TYPE(PC,FUNC) p1 = (*(KPRF_TYPE(P,REPORTFUNC) *)pv1)->pFunc;
+    KPRF_TYPE(PC,FUNC) p2 = (*(KPRF_TYPE(P,REPORTFUNC) *)pv2)->pFunc;
+    if (p1->cOnStack > p2->cOnStack)
+        return -1;
+    if (p1->cOnStack < p2->cOnStack)
+        return 1;
+    return KPRF_NAME(FuncCompareOnStack)(pv1, pv2);
+}
+
+
+/**
+ * Compares two REPROTFUNC records to determin which has the higher call from count.
+ */
+static int KPRF_NAME(FuncCompareCallsFrom)(const void *pv1, const void *pv2)
+{
+    KPRF_TYPE(PC,FUNC) p1 = (*(KPRF_TYPE(P,REPORTFUNC) *)pv1)->pFunc;
+    KPRF_TYPE(PC,FUNC) p2 = (*(KPRF_TYPE(P,REPORTFUNC) *)pv2)->pFunc;
+    if (p1->cCalls > p2->cCalls)
+        return -1;
+    if (p1->cCalls < p2->cCalls)
+        return 1;
+    return KPRF_NAME(FuncCompareOnTopOfStack)(pv1, pv2);
+}
+
+
+/**
+ * A report thread.
+ *
+ * @internal
+ */
+typedef struct KPRF_TYPE(,REPORTTHREAD)
+{
+    /** Pointer to the thread data in the data set. */
+    KPRF_TYPE(PC,THREAD)        pThread;
+} KPRF_TYPE(,REPORTTHREAD), *KPRF_TYPE(P,REPORTTHREAD);
+
+
+/**
+ * Data-set analysis report.
+ *
+ * This is an internal structure to store temporary data between the
+ * analysis stage and the priting state.
+ *
+ * @internal
+ */
+typedef struct KPRF_TYPE(,REPORT)
+{
+    /** Pointer to the data set. */
+    KPRF_TYPE(PC,HDR)   pHdr;
+
+    /** @name Data-set item wrappers.
+     * @{ */
+    /** Pointer to the array of threads. */
+    KPRF_TYPE(P,REPORTTHREAD)   paThreads;
+    /** Pointer to the array of functions. */
+    KPRF_TYPE(P,REPORTFUNC)     paFunctions;
+    /** Pointer to the head of the module list. */
+    KPRF_TYPE(P,REPORTMOD)      pFirstMod;
+    /** The number of modules in the list. */
+    uint32_t                    cMods;
+    /** The module segment tree. */
+    PAVLU32NODECORE             pModSegTree;
+    /** The number of module segments in the tree. */
+    uint32_t                    cModSegs;
+    /** @} */
+
+    /** @name Sorting.
+     * @{ */
+    /** Pointer to the array of threads. */
+    KPRF_TYPE(P,REPORTTHREAD)  *papSortedThreads;
+    /** Pointer to the array of functions. */
+    KPRF_TYPE(P,REPORTFUNC)    *papSortedFunctions;
+    /** @} */
+
+    /** @name Accumulated Thread Data.
+     * @{ */
+    /** Sum of the profiled ticks. */
+    uint64_t                    ProfiledTicks;
+    /** Sum of the overhead ticks. */
+    uint64_t                    OverheadTicks;
+    /** Sum of the sleep ticks. */
+    uint64_t                    SleepTicks;
+    /** Sum of calls performed. */
+    uint64_t                    cCalls;
+    /** @} */
+
+} KPRF_TYPE(,REPORT), *KPRF_TYPE(P,REPORT), **KPRF_TYPE(PP,REPORT);
+
+
+/**
+ * Allocates and initializes a report.
+ *
+ * @returns Pointer to the report on success.
+ * @returns NULL on failure.
+ */
+static KPRF_TYPE(P,REPORT) KPRF_NAME(NewReport)(KPRF_TYPE(PC,HDR) pHdr)
+{
+    /*
+     * Allocate memory for the report.
+     * Everything but the mods and modsegs is allocated in the same block as the report.
+     */
+    size_t cb = KPRF_ALIGN(KPRF_SIZEOF(REPORT), 32);
+    uintptr_t offThreads = cb;
+    cb += KPRF_ALIGN(KPRF_SIZEOF(REPORTTHREAD) * pHdr->cThreads, 32);
+    uintptr_t offFunctions = cb;
+    cb += KPRF_ALIGN(KPRF_SIZEOF(REPORTFUNC) * pHdr->cFunctions, 32);
+    uintptr_t offSortedThreads = cb;
+    cb += KPRF_ALIGN(sizeof(KPRF_TYPE(P,REPORTTHREAD)) * pHdr->cThreads, 32);
+    uintptr_t offSortedFunctions = cb;
+    cb += KPRF_ALIGN(sizeof(KPRF_TYPE(P,REPORTFUNC)) * pHdr->cFunctions, 32);
+    KPRF_TYPE(P,REPORT) pReport = (KPRF_TYPE(P,REPORT))malloc(cb);
+    if (!pReport)
+        return NULL;
+
+    /*
+     * Initialize it.
+     */
+    pReport->pHdr = pHdr;
+    pReport->paThreads = (KPRF_TYPE(P,REPORTTHREAD))((uint8_t *)pReport + offThreads);
+    pReport->paFunctions = (KPRF_TYPE(P,REPORTFUNC))((uint8_t *)pReport + offFunctions);
+    pReport->pFirstMod = NULL;
+    pReport->cMods = 0;
+    pReport->pModSegTree = NULL;
+    pReport->cModSegs = 0;
+    pReport->papSortedThreads = (KPRF_TYPE(P,REPORTTHREAD) *)((uint8_t *)pReport + offSortedThreads);
+    pReport->papSortedFunctions = (KPRF_TYPE(P,REPORTFUNC) *)((uint8_t *)pReport + offSortedFunctions);
+    pReport->ProfiledTicks = 0;
+    pReport->OverheadTicks = 0;
+    pReport->SleepTicks = 0;
+    pReport->cCalls = 0;
+
+    return pReport;
+}
+
+
+/**
+ * AVL callback for deleting a module segment node.
+ *
+ * @returns 0
+ * @param   pCore       The tree node to delete.
+ * @param   pvParam     User parameter, ignored.
+ */
+static DECLCALLBACK(int) KPRF_NAME(DeleteModSeg)(PAVLU32NODECORE pCore, void *pvParam)
+{
+    free(pCore);
+    return 0;
+}
+
+
+/**
+ * Releases all the resources held be a report.
+ *
+ * @param   pReport     The report to delete.
+ */
+static void KPRF_NAME(DeleteReport)(KPRF_TYPE(P,REPORT) pReport)
+{
+    /*
+     * The list and AVL.
+     */
+    while (pReport->pFirstMod)
+    {
+        KPRF_TYPE(P,REPORTMOD) pFree = pReport->pFirstMod;
+        pReport->pFirstMod = pFree->pNext;
+        RTDbgModuleClose(pFree->pDbgMod);
+        free(pFree);
+    }
+
+    RTAvlU32Destroy(&pReport->pModSegTree, KPRF_NAME(DeleteModSeg), NULL);
+
+    /*
+     * The function debug info.
+     */
+    uint32_t i = pReport->pHdr->cFunctions;
+    while (i-- > 0)
+    {
+        RTDbgSymbolFree(pReport->paFunctions[i].pSym);
+        RTDbgLineFree(pReport->paFunctions[i].pLine);
+    }
+
+    /*
+     * The report it self.
+     */
+    pReport->pHdr = NULL;
+    free(pReport);
+}
+
+
+/**
+ * Builds the module segment tree and the list of modules.
+ *
+ * @returns 0 on success.
+ * @returns -1 on failure.
+ * @param   pReport     The report to work on.
+ */
+static int KPRF_NAME(AnalyseModSegs)(KPRF_TYPE(P,REPORT) pReport)
+{
+    const uint32_t offEnd = pReport->pHdr->offModSegs + pReport->pHdr->cbModSegs;
+    uint32_t off = pReport->pHdr->offModSegs;
+    while (off < offEnd)
+    {
+        KPRF_TYPE(PC,MODSEG) pCur = KPRF_OFF2PTR(PC,MODSEG, off, pReport->pHdr);
+        uint32_t cbCur = KPRF_OFFSETOF(MODSEG, szPath[pCur->cchPath + 1]);
+        cbCur = KPRF_ALIGN(cbCur, KPRF_SIZEOF(UPTR));
+
+        /*
+         * Create a new modseg record.
+         */
+        KPRF_TYPE(P,REPORTMODSEG) pSeg = (KPRF_TYPE(P,REPORTMODSEG))malloc(sizeof(*pSeg));
+        if (!pSeg)
+            return -1;
+
+        pSeg->Core.Key = off;
+        pSeg->pModSeg = pCur;
+        pSeg->pMod = NULL; /* below */
+        pSeg->OnStackTicks = 0;
+        pSeg->OnTopOfStackTicks = 0;
+        pSeg->cFunctions = 0;
+
+        if (!RTAvlU32Insert(&pReport->pModSegTree, &pSeg->Core))
+        {
+            free(pSeg);
+            return -1;
+        }
+        pReport->cModSegs++;
+
+        /*
+         * Search for the module record.
+         */
+        KPRF_TYPE(P,REPORTMOD) pMod = pReport->pFirstMod;
+        while (     pMod
+               &&   (   pMod->pFirstSeg->pModSeg->cchPath != pCur->cchPath
+                     || memcmp(pMod->pFirstSeg->pModSeg->szPath, pCur->szPath, pCur->cchPath)))
+            pMod = pMod->pNext;
+        if (pMod)
+        {
+            /** @todo sort segments */
+            pSeg->pMod = pMod;
+            pSeg->pNext = pMod->pFirstSeg;
+            pMod->pFirstSeg = pSeg;
+        }
+        else
+        {
+            KPRF_TYPE(P,REPORTMOD) pMod = (KPRF_TYPE(P,REPORTMOD))malloc(sizeof(*pMod) + pCur->cchPath);
+            if (!pMod)
+                return -1;
+            pSeg->pMod = pMod;
+            pSeg->pNext = NULL;
+            pMod->iMod = pReport->cMods++;
+            pMod->pNext = pReport->pFirstMod;
+            pReport->pFirstMod = pMod;
+            pMod->pFirstSeg = pSeg;
+            pMod->pDbgMod = NULL;
+            pMod->OnStackTicks = 0;
+            pMod->OnTopOfStackTicks = 0;
+            pMod->cFunctions = 0;
+
+            int rc = RTDbgModuleOpen(pSeg->pModSeg->szPath, &pMod->pDbgMod);
+            if (RT_FAILURE(rc))
+                pMod->pDbgMod = NULL;
+        }
+
+        /* next */
+        off += cbCur;
+    }
+
+    return 0;
+}
+
+
+/**
+ * Initializes the function arrays.
+ *
+ * @returns 0 on success.
+ * @returns -1 on failure.
+ * @param   pReport     The report to work on.
+ */
+static int KPRF_NAME(AnalyseFunctions)(KPRF_TYPE(P,REPORT) pReport)
+{
+    uint32_t iFunc = pReport->pHdr->cFunctions;
+    KPRF_TYPE(PC,FUNC) pFunc = KPRF_OFF2PTR(PC,FUNC, pReport->pHdr->offFunctions + iFunc * sizeof(*pFunc), pReport->pHdr);
+    KPRF_TYPE(P,REPORTFUNC) pReportFunc = &pReport->paFunctions[iFunc];
+    while (iFunc-- > 0)
+    {
+        pFunc--;
+        pReportFunc--;
+
+        pReport->papSortedFunctions[iFunc] = pReportFunc;
+        pReportFunc->pFunc = pFunc;
+        pReportFunc->pModSeg = (KPRF_TYPE(P,REPORTMODSEG))RTAvlU32Get(&pReport->pModSegTree, pFunc->offModSeg);
+        pReportFunc->pSym = NULL;
+        pReportFunc->pLine = NULL;
+        if (pReportFunc->pModSeg)
+        {
+            /* Collect module segment and module statistics. */
+            KPRF_TYPE(P,REPORTMODSEG) pModSeg = pReportFunc->pModSeg;
+            pModSeg->cFunctions++;
+            pModSeg->OnStackTicks += pFunc->OnStack.SumTicks;
+            pModSeg->OnTopOfStackTicks += pFunc->OnTopOfStack.SumTicks;
+
+            KPRF_TYPE(P,REPORTMOD) pMod = pModSeg->pMod;
+            pMod->cFunctions++;
+            pMod->OnStackTicks += pFunc->OnStack.SumTicks;
+            pMod->OnTopOfStackTicks += pFunc->OnTopOfStack.SumTicks;
+
+            /* Get debug info. */
+            RTUINTPTR offSegment = (RTUINTPTR)(pFunc->uEntryPtr - pModSeg->pModSeg->uBasePtr);
+            int rc = RTDbgModuleQuerySymbolA(pMod->pDbgMod, pModSeg->pModSeg->iSegment, offSegment, &pReportFunc->pSym);
+            /** @todo check displacement! */
+            if (RT_FAILURE(rc))
+                pReportFunc->pSym = NULL;
+            rc = RTDbgModuleQueryLineA(pMod->pDbgMod, pModSeg->pModSeg->iSegment, offSegment, &pReportFunc->pLine);
+            if (RT_FAILURE(rc))
+                pReportFunc->pLine = NULL;
+        }
+    }
+    return 0;
+}
+
+
+/**
+ * Initializes the thread arrays.
+ *
+ * @returns 0 on success.
+ * @returns -1 on failure.
+ * @param   pReport     The report to work on.
+ */
+static int KPRF_NAME(AnalyseThreads)(KPRF_TYPE(P,REPORT) pReport)
+{
+    uint32_t iThread = pReport->pHdr->cThreads;
+    KPRF_TYPE(PC,THREAD) pThread = KPRF_OFF2PTR(PC,THREAD, pReport->pHdr->offThreads + iThread * sizeof(*pThread), pReport->pHdr);
+    KPRF_TYPE(P,REPORTTHREAD) pReportThread = &pReport->paThreads[iThread];
+    while (iThread-- > 0)
+    {
+        pThread--;
+        pReportThread--;
+
+        pReport->papSortedThreads[iThread] = pReportThread;
+        pReportThread->pThread = pThread;
+
+        /* collect statistics */
+        pReport->ProfiledTicks += pThread->ProfiledTicks;
+        pReport->OverheadTicks += pThread->OverheadTicks;
+        pReport->SleepTicks += pThread->SleepTicks;
+        pReport->cCalls += pThread->cCalls;
+
+    }
+    return 0;
+}
+
+
+/**
+ * Analyses the data set, producing a report.
+ *
+ * @returns 0 on success.
+ * @returns -1 on failure.
+ *
+ * @param   pHdr        The data set.
+ * @param   ppReport    Where to store the report.
+ */
+static int KPRF_NAME(Analyse)(KPRF_TYPE(PC,HDR) pHdr, KPRF_TYPE(PP,REPORT) ppReport)
+{
+    *ppReport = NULL;
+
+    /* allocate it */
+    KPRF_TYPE(P,REPORT) pReport = KPRF_NAME(NewReport)(pHdr);
+    if (!pReport)
+        return -1;
+
+    /* read module segments */
+    int rc = KPRF_NAME(AnalyseModSegs)(pReport);
+    if (!rc)
+    {
+        /* read functions. */
+        rc = KPRF_NAME(AnalyseFunctions)(pReport);
+        if (!rc)
+        {
+            /* read threads */
+            rc = KPRF_NAME(AnalyseThreads)(pReport);
+            if (!rc)
+            {
+                *ppReport = pReport;
+                return 0;
+            }
+        }
+    }
+
+    KPRF_NAME(DeleteReport)(pReport);
+    return rc;
+}
+
+
+/**
+ * Writes row with 32-bit value.
+ * @internal
+ */
+static void KPRF_NAME(HtmlWriteRowU32X32)(FILE *pOut, const char *pszName, uint32_t u32, const char *pszUnit)
+{
+    fprintf(pOut,
+            "  <tr>\n"
+            "    <th>%s</th>\n"
+            "    <td colspan=\"6\">%u (0x%x)%s%s</td>\n"
+            "  </tr>\n",
+            pszName,
+            u32, u32, pszUnit ? " " : "", pszUnit ? pszUnit : "");
+}
+
+
+/**
+ * Writes row with 32-bit value.
+ * @internal
+ */
+static void KPRF_NAME(HtmlWriteRowU32)(FILE *pOut, const char *pszName, uint32_t u32, const char *pszUnit)
+{
+    fprintf(pOut,
+            "  <tr>\n"
+            "    <th>%s</th>\n"
+            "    <td colspan=\"6\">%u%s%s</td>\n"
+            "  </tr>\n",
+            pszName,
+            u32, pszUnit ? " " : "", pszUnit ? pszUnit : "");
+}
+
+
+/**
+ * Writes row with 64-bit value.
+ * @internal
+ */
+static void KPRF_NAME(HtmlWriteRowU64)(FILE *pOut, const char *pszName, uint64_t u64, const char *pszUnit)
+{
+    fprintf(pOut,
+            "  <tr>\n"
+            "    <th>%s</th>\n"
+            "    <td colspan=\"6\">% " KPRF_FMT_U64 " (0x%" KPRF_FMT_X64 ")%s%s</td>\n"
+            "  </tr>\n",
+            pszName,
+            u64, u64, pszUnit ? " " : "", pszUnit ? pszUnit : "");
+}
+
+
+/**
+ * Writes row with 64-bit hex value.
+ * @internal
+ */
+static void KPRF_NAME(HtmlWriteRowX64)(FILE *pOut, const char *pszName, uint64_t u64, const char *pszUnit)
+{
+    fprintf(pOut,
+            "  <tr>\n"
+            "    <th>%s</th>\n"
+            "    <td colspan=\"6\">0x%" KPRF_FMT_X64 "%s%s</td>\n"
+            "  </tr>\n",
+            pszName,
+            u64, pszUnit ? " " : "", pszUnit ? pszUnit : "");
+}
+
+
+/**
+ * Writes a ticks.
+ */
+static void KPRF_NAME(HtmlWriteParts)(FILE *pOut, uint64_t cTicks, uint64_t cTotalTicks)
+{
+    /** U+2030 PER MILLE SIGN */
+    static const uint8_t s_szPerMilleSignUtf8[4] = { 0xe2, 0x80, 0xb0, 0};
+
+    if (cTicks * 100 / cTotalTicks)
+    {
+        uint32_t u = (uint32_t)((cTicks * 1000) / cTotalTicks);
+        fprintf(pOut, "%u.%01u%%", u / 10, u %10);
+    }
+    else //if (cTicks * 100000 / cTotalTicks)
+    {
+        uint32_t u = (uint32_t)((cTicks * 100000) / cTotalTicks);
+        fprintf(pOut, "%u.%02u%s", u / 100, u % 100, s_szPerMilleSignUtf8);
+    }
+    /*
+    else if (cTicks * 1000000 / cTotalTicks)
+        fprintf(pOut, "%u ppm", (unsigned)((cTicks * 1000000) / cTotalTicks));
+    else
+        fprintf(pOut, "%u ppb", (unsigned)((cTicks * 1000000000) / cTotalTicks));
+    */
+}
+
+
+/**
+ * Writes a ticks.
+ */
+static void KPRF_NAME(HtmlWriteTicks)(FILE *pOut, uint64_t cTicks, uint64_t cTotalTicks)
+{
+    fprintf(pOut, "%" KPRF_FMT_U64 "", cTicks);
+    if (cTotalTicks)
+    {
+        fprintf(pOut, "</td><td class=\"PartsRow\">");
+        KPRF_NAME(HtmlWriteParts)(pOut, cTicks, cTotalTicks);
+    }
+}
+
+
+/**
+ * Writes row with ticks value.
+ *
+ * @param   pOut            Where to write.
+ * @aaran   pszName         The row name.
+ * @param   cTicks          The tick count.
+ * @param   cTotalTicks     If non-zero, this is used for cTicks / cTotalTicks.
+ * @internal
+ */
+static void KPRF_NAME(HtmlWriteRowTicks)(FILE *pOut, const char *pszName, uint64_t cTicks, uint64_t cTotalTicks)
+{
+    fprintf(pOut,
+            "  <tr>\n"
+            "    <th class=\"TicksRow\">%s</th>\n"
+            "    <td class=\"TicksRow\">",
+            pszName);
+    KPRF_NAME(HtmlWriteTicks)(pOut, cTicks, cTotalTicks);
+    fprintf(pOut,
+                     "</td><td colspan=\"%d\"/>\n"
+            "  </tr>\n",
+            cTotalTicks ? 4 : 5);
+}
+
+
+/**
+ * Writes row with a time stat value.
+ *
+ * @param   pOut            Where to write.
+ * @aaran   pszName         The row name.
+ * @param   cTicks          The tick count.
+ * @param   cTotalTicks     If non-zero, this is used for cTicks / cTotalTicks.
+ * @internal
+ */
+static void KPRF_NAME(HtmlWriteRowTimeStat)(FILE *pOut, const char *pszName, KPRF_TYPE(PC,TIMESTAT) pTimeStat, uint64_t cTotalTicks)
+{
+    fprintf(pOut,
+            "  <tr>\n"
+            "    <th class=\"TicksRow\">%s</th>\n"
+            "    <td class=\"TicksRow\">",
+            pszName);
+    KPRF_NAME(HtmlWriteTicks)(pOut, pTimeStat->SumTicks, cTotalTicks);
+    fprintf(pOut,    "</td>\n"
+            "    <td class=\"MinMaxTicksRow\">");
+    KPRF_NAME(HtmlWriteTicks)(pOut, pTimeStat->MinTicks, cTotalTicks);
+    fprintf(pOut,    "</td>\n"
+            "    <td class=\"MinMaxTicksRow\">");
+    KPRF_NAME(HtmlWriteTicks)(pOut, pTimeStat->MaxTicks, cTotalTicks);
+    fprintf(pOut,    "</td>\n"
+            "  </tr>\n");
+}
+
+
+/**
+ * Writes row with calls value.
+ *
+ * @param   pOut            Where to write.
+ * @aaran   pszName         The row name.
+ * @param   cCalls          The call count.
+ * @param   cTotalCalls     This is used for cCalls / cTotalCalls.
+ * @internal
+ */
+static void KPRF_NAME(HtmlWriteRowCalls)(FILE *pOut, const char *pszName, uint64_t cCalls, uint64_t cTotalCalls)
+{
+    fprintf(pOut,
+            "  <tr>\n"
+            "    <th class=\"CallsRow\">%s</th>\n"
+            "    <td class=\"CallsRow\">%" KPRF_FMT_U64"</td><td class=\"PartsRow\">",
+            pszName, cCalls);
+    KPRF_NAME(HtmlWriteParts)(pOut, cCalls, cTotalCalls);
+    fprintf(pOut, "</td><td colspan=4></td>"
+            "  </tr>\n");
+}
+
+
+/**
+ * Writes row with pointer value.
+ * @internal
+ */
+static void KPRF_NAME(HtmlWriteRowUPTR)(FILE *pOut, const char *pszName, KPRF_TYPE(,UPTR) uPtr, const char *pszUnit)
+{
+    fprintf(pOut,
+            "  <tr>\n"
+            "    <th>%s</th>\n"
+            "    <td colspan=\"6\">%" KPRF_FMT_UPTR "%s%s</td>\n"
+            "  </tr>\n",
+            pszName,
+            uPtr, pszUnit ? " " : "", pszUnit ? pszUnit : "");
+}
+
+
+/**
+ * Writes row with string value.
+ * @internal
+ */
+static void KPRF_NAME(HtmlWriteRowString)(FILE *pOut, const char *pszName, const char *pszClass, const char *pszFormat, ...)
+{
+    fprintf(pOut,
+             "  <tr>\n"
+             "    <th>%s</th>\n"
+             "    <td%s%s%s colspan=\"6\">",
+             pszName,
+             pszClass ? " class=\"" : "", pszClass ? pszClass : "", pszClass ? "\"" : "");
+    va_list va;
+    va_start(va, pszFormat);
+    vfprintf(pOut, pszFormat, va);
+    va_end(va);
+    fprintf(pOut,      "</td>\n"
+             "  </tr>\n");
+}
+
+
+/**
+ * The first column
+ */
+typedef enum KPRF_TYPE(,FIRSTCOLUMN)
+{
+    KPRF_TYPE(,FIRSTCOLUMN_ON_STACK) = 0,
+    KPRF_TYPE(,FIRSTCOLUMN_ON_TOP_OF_STACK),
+    KPRF_TYPE(,FIRSTCOLUMN_CALLS_TO),
+    KPRF_TYPE(,FIRSTCOLUMN_CALLS_FROM),
+    KPRF_TYPE(,FIRSTCOLUMN_MAX)
+} KPRF_TYPE(,FIRSTCOLUMN);
+
+
+/**
+ * Prints the table with the sorted functions.
+ * The tricky bit is that the sorted column should be to the left of the function name.
+ */
+static void KPRF_NAME(HtmlWriteSortedFunctions)(KPRF_TYPE(P,REPORT) pReport, FILE *pOut, const char *pszName,
+                                                const char *pszTitle, KPRF_TYPE(,FIRSTCOLUMN) enmFirst)
+{
+    fprintf(pOut,
+            "<h2><a name=\"%s\">%s</a></h2>\n"
+            "\n",
+            pszName, pszTitle);
+
+    fprintf(pOut,
+            "<table class=\"FunctionsSorted\">\n"
+            "  <tr>\n"
+            "    <th/>\n");
+    static const char *s_pszHeaders[KPRF_TYPE(,FIRSTCOLUMN_MAX) * 2] =
+    {
+        "    <th colspan=8><a href=\"#Functions-TimeOnStack\">Time On Stack</a> (ticks)</th>\n",
+        "    <th colspan=2><a href=\"#Functions-TimeOnStack\">Sum</a></th>\n"
+        "    <th colspan=2><a href=\"#Functions-TimeOnStack-Min\">Min</a></th>\n"
+        "    <th colspan=2><a href=\"#Functions-TimeOnStack-Avg\">Average</a></th>\n"
+        "    <th colspan=2><a href=\"#Functions-TimeOnStack-Max\">Max</a></th>\n",
+
+        "    <th colspan=8><a href=\"#Functions-TimeOnTopOfStack\">Time On To Top</a> (ticks)</th>\n",
+        "    <th colspan=2><a href=\"#Functions-TimeOnTopOfStack\">Sum</a></th>\n"
+        "    <th colspan=2><a href=\"#Functions-TimeOnTopOfStack-Min\">Min</a></th>\n"
+        "    <th colspan=2><a href=\"#Functions-TimeOnTopOfStack-Avg\">Average</a></th>\n"
+        "    <th colspan=2><a href=\"#Functions-TimeOnTopOfStack-Max\">Max</a></th>\n",
+
+        "    <th colspan=2><a href=\"#Functions-CallsTo\">Calls To</a></th>\n",
+        "    <th/><th/>\n",
+
+        "    <th colspan=2><a href=\"#Functions-CallsFrom\">Calls From</a></th>\n",
+        "    <th/><th/>\n",
+    };
+
+    fprintf(pOut, "%s",  s_pszHeaders[enmFirst * 2]);
+    fprintf(pOut, "    <th>Function</th>\n");
+    for (unsigned i = (enmFirst + 1) % KPRF_TYPE(,FIRSTCOLUMN_MAX); i != enmFirst; i = (i + 1) % KPRF_TYPE(,FIRSTCOLUMN_MAX))
+        fprintf(pOut, "%s",  s_pszHeaders[i * 2]);
+    fprintf(pOut,
+            "  </tr>\n"
+            "  <tr>\n"
+            "    <th/>\n");
+    fprintf(pOut, "%s",  s_pszHeaders[enmFirst * 2 + 1]);
+    fprintf(pOut, "    <th/>\n");
+    for (unsigned i = (enmFirst + 1) % KPRF_TYPE(,FIRSTCOLUMN_MAX); i != enmFirst; i = (i + 1) % KPRF_TYPE(,FIRSTCOLUMN_MAX))
+        fprintf(pOut, "%s",  s_pszHeaders[i * 2 + 1]);
+    fprintf(pOut,
+            "  </tr>\n");
+
+    for (uint32_t iFunc = 0; iFunc < pReport->pHdr->cFunctions; iFunc++)
+    {
+        KPRF_TYPE(P,REPORTFUNC) pReportFunc = pReport->papSortedFunctions[iFunc];
+        KPRF_TYPE(PC,FUNC) pFunc = pReportFunc->pFunc;
+        fprintf(pOut,
+                "  <tr>\n"
+                "    <td>%u</td>\n",
+               iFunc);
+
+        unsigned i = enmFirst;
+        do
+        {
+            switch (i)
+            {
+                case KPRF_TYPE(,FIRSTCOLUMN_ON_STACK):
+                    fprintf(pOut,
+                            "    <td class=\"Ticks\">%" KPRF_FMT_U64 "</td><td class=\"Parts\">",
+                            pFunc->OnStack.SumTicks);
+                    KPRF_NAME(HtmlWriteParts)(pOut, pFunc->OnStack.SumTicks, pReport->ProfiledTicks);
+                    fprintf(pOut,       "</td>\n"
+                            "    <td class=\"MinMaxTicks\">%" KPRF_FMT_U64 "</td><td class=\"Parts\">",
+                            pFunc->OnStack.MinTicks);
+                    KPRF_NAME(HtmlWriteParts)(pOut, pFunc->OnStack.MinTicks, pReport->ProfiledTicks);
+                    fprintf(pOut,       "</td>\n"
+                            "    <td class=\"MinMaxTicks\">%" KPRF_FMT_U64 "</td><td class=\"Parts\">",
+                            pFunc->OnStack.SumTicks / pFunc->cOnStack);
+                    KPRF_NAME(HtmlWriteParts)(pOut, pFunc->OnStack.MinTicks, pReport->ProfiledTicks);
+                    fprintf(pOut,       "</td>\n"
+                            "    <td class=\"MinMaxTicks\">%" KPRF_FMT_U64 "</td><td class=\"Parts\">",
+                            pFunc->OnStack.MaxTicks);
+                    KPRF_NAME(HtmlWriteParts)(pOut, pFunc->OnStack.MaxTicks, pReport->ProfiledTicks);
+                    fprintf(pOut,       "</td>\n");
+                    break;
+
+                case KPRF_TYPE(,FIRSTCOLUMN_ON_TOP_OF_STACK):
+                    fprintf(pOut,
+                            "    <td class=\"Ticks\">%" KPRF_FMT_U64 "</td><td class=\"Parts\">",
+                            pFunc->OnTopOfStack.SumTicks);
+                    KPRF_NAME(HtmlWriteParts)(pOut, pFunc->OnTopOfStack.SumTicks, pReport->ProfiledTicks);
+                    fprintf(pOut,       "</td>\n"
+                            "    <td class=\"MinMaxTicks\">%" KPRF_FMT_U64 "</td><td class=\"Parts\">",
+                            pFunc->OnTopOfStack.MinTicks);
+                    KPRF_NAME(HtmlWriteParts)(pOut, pFunc->OnTopOfStack.MinTicks, pReport->ProfiledTicks);
+                    fprintf(pOut,       "</td>\n"
+                            "    <td class=\"MinMaxTicks\">%" KPRF_FMT_U64 "</td><td class=\"Parts\">",
+                            pFunc->OnTopOfStack.SumTicks / pFunc->cOnStack);
+                    KPRF_NAME(HtmlWriteParts)(pOut, pFunc->OnTopOfStack.MinTicks, pReport->ProfiledTicks);
+                    fprintf(pOut,       "</td>\n"
+                            "    <td class=\"MinMaxTicks\">%" KPRF_FMT_U64 "</td><td class=\"Parts\">",
+                            pFunc->OnTopOfStack.MaxTicks);
+                    KPRF_NAME(HtmlWriteParts)(pOut, pFunc->OnTopOfStack.MaxTicks, pReport->ProfiledTicks);
+                    fprintf(pOut,       "</td>\n");
+                    break;
+
+                case KPRF_TYPE(,FIRSTCOLUMN_CALLS_TO):
+                    fprintf(pOut,
+                            "    <td class=\"Calls\">%" KPRF_FMT_U64 "</td><td Class=\"Parts\">",
+                            pFunc->cOnStack);
+                    KPRF_NAME(HtmlWriteParts)(pOut, pFunc->cOnStack, pReport->cCalls);
+                    fprintf(pOut,       "</td>\n");
+                    break;
+
+                case KPRF_TYPE(,FIRSTCOLUMN_CALLS_FROM):
+                    fprintf(pOut,
+                            "    <td class=\"Calls\">%" KPRF_FMT_U64 "</td><td Class=\"Parts\">",
+                            pFunc->cCalls);
+                    KPRF_NAME(HtmlWriteParts)(pOut, pFunc->cOnStack, pReport->cCalls);
+                    fprintf(pOut,       "</td>\n");
+                    break;
+
+                default:
+                    break;
+            }
+
+            /* inject the function column */
+            if (i == enmFirst)
+            {
+                fprintf(pOut,
+                        "    <td><a href=\"#Func-%u\">",
+                        pReportFunc - pReport->paFunctions);
+                if (pReportFunc->pSym)
+                    fprintf(pOut, "%s</a></td>\n", pReportFunc->pSym->szName);
+                else
+                    fprintf(pOut, "%" KPRF_FMT_UPTR "</a></td>\n", pFunc->uEntryPtr);
+            }
+
+            /* next */
+            i = (i + 1) % KPRF_TYPE(,FIRSTCOLUMN_MAX);
+        } while (i != enmFirst);
+
+        fprintf(pOut,
+                "  </tr>\n");
+    }
+    fprintf(pOut,
+            "</table>\n"
+            "\n");
+
+}
+
+
+/**
+ * Writes an HTML report.
+ *
+ * @returns 0 on success.
+ * @returns -1 on failure.
+ * @param   pReport     The report to put into HTML.
+ * @param   pOut        The file stream to write the HTML to.
+ */
+static int KPRF_NAME(WriteHtmlReport)(KPRF_TYPE(P,REPORT) pReport, FILE *pOut)
+{
+    KPRF_TYPE(PC,HDR) pHdr = pReport->pHdr;
+
+    /*
+     * Write the standard html.
+     */
+    fprintf(pOut,
+            "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n"
+            "<html>\n"
+            "<head>\n"
+            "  <meta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\">\n"
+            "  <title>kProfile 2 - %s</title>\n"
+            "</head>\n"
+            "<style>\n"
+            "table\n"
+            "{\n"
+//            "  width: 90%%;\n"
+            "  background: #999999;\n"
+//            "  margin-top: .6em;\n"
+//            "  margin-bottom: .3em;\n"
+            "}\n"
+            "th\n"
+            "{\n"
+            "  padding: 1px 4px;\n"
+            "  background: #cccccc;\n"
+//            "  text-align: left;\n"
+            "  font-size: 90%%;\n"
+            //"  width: 30%%;\n"
+            "}\n"
+            "td\n"
+            "{\n"
+            "  padding: 1px 4px;\n"
+            "  background: #ffffff;\n"
+            "  font-size: 90%%;\n"
+            "}\n"
+            "td.Ticks\n"
+            "{\n"
+            "  text-align: right;\n"
+            "}\n"
+            "td.TicksRow\n"
+            "{\n"
+            "  text-align: right;\n"
+            "}\n"
+            "td.MinMaxTicks\n"
+            "{\n"
+            "  text-align: right;\n"
+            "}\n"
+            "td.MinMaxTicksRow\n"
+            "{\n"
+            "  text-align: right;\n"
+            "}\n"
+            "td.Parts\n"
+            "{\n"
+            "  text-align: right;\n"
+            "}\n"
+            "td.PartsRow\n"
+            "{\n"
+            "  text-align: left;\n"
+            "}\n"
+            "td.Calls\n"
+            "{\n"
+            "  text-align: right;\n"
+            "}\n"
+            "td.CallsRow\n"
+            "{\n"
+            "  text-align: right;\n"
+            "}\n"
+            "td.BlankRow\n"
+            "{\n"
+            "  background: #e0e0e0;\n"
+            "}\n"
+            "td.Name\n"
+            "{\n"
+            "  font-weight: bold;\n"
+            "}\n"
+            "table.Summary th\n"
+            "{\n"
+            "  width:200px;\n"
+            "}\n"
+            "table.Thread\n"
+            "{\n"
+            "  min-width:60%%\n"
+            "}\n"
+            "table.Thread th\n"
+            "{\n"
+            "  width:200px;\n"
+            "}\n"
+            "table.Functions\n"
+            "{\n"
+            "  width:60%%;\n"
+            "}\n"
+            "table.Functions th\n"
+            "{\n"
+            "  width:200px;\n"
+            "}\n"
+            "table.Modules\n"
+            "{\n"
+            "  width:60%%;\n"
+            "}\n"
+            "table.Modules th\n"
+            "{\n"
+            "  width:200px;\n"
+            "}\n"
+            "table.FunctionsSorted\n"
+            "{\n"
+            "}\n"
+            "</style>\n"
+            "<body topmargin=\"0\">\n"
+            ,
+            pHdr->offCommandLine
+                ? (const char *)KPRF_OFF2PTR(P,FUNC, pHdr->offCommandLine, pHdr)
+                : ""
+            );
+
+    /*
+     * Table of contents.
+     */
+    fprintf(pOut,
+            "<h2>Table of Contents</h2>\n"
+            "\n"
+            "<ul>\n"
+            "  <li><a href=\"#Summary\"  >1.0 Summary</a></li>\n"
+            "  <li><a href=\"#Functions\">2.0 Functions</a></li>\n"
+            "      <ul>\n"
+            "        <li><a href=\"#Functions-TimeOnStack\"     >2.1 Time On Stack</a></li>\n"
+            "          <ul>\n"
+            "            <li><a href=\"#Functions-TimeOnStack-Avg\"     >2.2.1 Time On Stack - Average</a></li>\n"
+            "            <li><a href=\"#Functions-TimeOnStack-Min\"     >2.2.1 Time On Stack - Min</a></li>\n"
+            "            <li><a href=\"#Functions-TimeOnStack-Max\"     >2.2.2 Time On Stack - Max</a></li>\n"
+            "          </ul>\n"
+            "        <li><a href=\"#Functions-TimeOnTopOfStack\">2.3 Time On Top Of Stack</a></li>\n"
+            "          <ul>\n"
+            "            <li><a href=\"#Functions-TimeOnTopOfStack-Avg\">2.3.1 Time On Top Of Stack - Average</a></li>\n"
+            "            <li><a href=\"#Functions-TimeOnTopOfStack-Min\">2.3.2 Time On Top Of Stack - Min</a></li>\n"
+            "            <li><a href=\"#Functions-TimeOnTopOfStack-Max\">2.3.3 Time On Top Of Stack - Max</a></li>\n"
+            "          </ul>\n"
+            "        <li><a href=\"#Functions-CallsTo\"         >2.3 Calls To</a></li>\n"
+            "        <li><a href=\"#Functions-CallsFrom\"       >2.4 Calls From</a></li>\n"
+            "        <li><a href=\"#Function-Details\"          >2.5 Function Details</a></li>\n"
+            "      </ul>\n"
+            "  <li><a href=\"#Threads\"  >3.0 Threads</a></li>\n"
+            "  <li><a href=\"#Modules\"  >4.0 Modules</a></li>\n"
+            "</ul>\n"
+            "\n"
+            "\n");
+
+    /*
+     * Summary.
+     */
+    fprintf(pOut,
+            "<h2><a name=\"Summary\">1.0 Summary</a></h2>\n"
+            "\n"
+            "<p>\n"
+            "<table class=\"Summary\">\n");
+    if (pHdr->offCommandLine)
+        KPRF_NAME(HtmlWriteRowString)(pOut, "Command Line", NULL, "%s", (const char *)KPRF_OFF2PTR(P,FUNC, pHdr->offCommandLine, pHdr));
+    KPRF_NAME(HtmlWriteRowU32X32)(pOut, "Threads", pHdr->cThreads, NULL);
+    KPRF_NAME(HtmlWriteRowU32X32)(pOut, "Modules", pReport->cMods, NULL);
+    KPRF_NAME(HtmlWriteRowU32X32)(pOut, "Functions", pHdr->cFunctions, NULL);
+    KPRF_NAME(HtmlWriteRowTicks)(pOut, "Profiled", pReport->ProfiledTicks, pReport->ProfiledTicks);
+    KPRF_NAME(HtmlWriteRowTicks)(pOut, "Sleep", pReport->SleepTicks, pReport->ProfiledTicks);
+    KPRF_NAME(HtmlWriteRowTicks)(pOut, "Overhead", pReport->OverheadTicks, pReport->ProfiledTicks + pReport->OverheadTicks);
+    KPRF_NAME(HtmlWriteRowCalls)(pOut, "Recorded Calls", pReport->cCalls, pReport->cCalls);
+    fprintf(pOut, "<tr><td class=\"BlankRow\" colspan=7>&nbsp;</td></tr>\n");
+    KPRF_NAME(HtmlWriteRowString)(pOut, "kProfile Version ", NULL, "Mark 2 Alpha 1");
+    KPRF_NAME(HtmlWriteRowString)(pOut, "kProfile Build Time ", NULL, __DATE__ " " __TIME__);
+    fprintf(pOut,
+            "</table>\n"
+            "</p>\n"
+            "\n"
+            "\n");
+
+    /*
+     * Functions.
+     */
+    fprintf(pOut,
+            "<h2><a name=\"Functions\">2.0 Functions</a></h2>\n"
+            "\n");
+
+    qsort(&pReport->papSortedFunctions[0], pHdr->cFunctions, sizeof(pReport->papSortedFunctions[0]), KPRF_NAME(FuncCompareOnStack));
+    KPRF_NAME(HtmlWriteSortedFunctions)(pReport, pOut, "Functions-TimeOnStack",     "2.1 Time On Stack", KPRF_TYPE(,FIRSTCOLUMN_ON_STACK));
+    qsort(&pReport->papSortedFunctions[0], pHdr->cFunctions, sizeof(pReport->papSortedFunctions[0]), KPRF_NAME(FuncCompareOnStackAvg));
+    KPRF_NAME(HtmlWriteSortedFunctions)(pReport, pOut, "Functions-TimeOnStack-Avg",     "2.2.1 Time On Stack - Average", KPRF_TYPE(,FIRSTCOLUMN_ON_STACK));
+    qsort(&pReport->papSortedFunctions[0], pHdr->cFunctions, sizeof(pReport->papSortedFunctions[0]), KPRF_NAME(FuncCompareOnStackMin));
+    KPRF_NAME(HtmlWriteSortedFunctions)(pReport, pOut, "Functions-TimeOnStack-Min",     "2.2.2 Time On Stack - Min", KPRF_TYPE(,FIRSTCOLUMN_ON_STACK));
+    qsort(&pReport->papSortedFunctions[0], pHdr->cFunctions, sizeof(pReport->papSortedFunctions[0]), KPRF_NAME(FuncCompareOnStackMax));
+    KPRF_NAME(HtmlWriteSortedFunctions)(pReport, pOut, "Functions-TimeOnStack-Max",     "2.2.3 Time On Stack - Max", KPRF_TYPE(,FIRSTCOLUMN_ON_STACK));
+
+    qsort(&pReport->papSortedFunctions[0], pHdr->cFunctions, sizeof(pReport->papSortedFunctions[0]), KPRF_NAME(FuncCompareOnTopOfStack));
+    KPRF_NAME(HtmlWriteSortedFunctions)(pReport, pOut, "Functions-TimeOnTopOfStack",    "2.2 Time On Top Of Stack", KPRF_TYPE(,FIRSTCOLUMN_ON_TOP_OF_STACK));
+    qsort(&pReport->papSortedFunctions[0], pHdr->cFunctions, sizeof(pReport->papSortedFunctions[0]), KPRF_NAME(FuncCompareOnTopOfStackAvg));
+    KPRF_NAME(HtmlWriteSortedFunctions)(pReport, pOut, "Functions-TimeOnTopOfStack-Avg","2.2.1 Time On Top Of Stack - Average", KPRF_TYPE(,FIRSTCOLUMN_ON_TOP_OF_STACK));
+    qsort(&pReport->papSortedFunctions[0], pHdr->cFunctions, sizeof(pReport->papSortedFunctions[0]), KPRF_NAME(FuncCompareOnTopOfStackMin));
+    KPRF_NAME(HtmlWriteSortedFunctions)(pReport, pOut, "Functions-TimeOnTopOfStack-Min","2.2.2 Time On Top Of Stack - Min", KPRF_TYPE(,FIRSTCOLUMN_ON_TOP_OF_STACK));
+    qsort(&pReport->papSortedFunctions[0], pHdr->cFunctions, sizeof(pReport->papSortedFunctions[0]), KPRF_NAME(FuncCompareOnTopOfStackMax));
+    KPRF_NAME(HtmlWriteSortedFunctions)(pReport, pOut, "Functions-TimeOnTopOfStack-Max","2.2.3 Time On Top Of Stack - Max", KPRF_TYPE(,FIRSTCOLUMN_ON_TOP_OF_STACK));
+
+    qsort(&pReport->papSortedFunctions[0], pHdr->cFunctions, sizeof(pReport->papSortedFunctions[0]), KPRF_NAME(FuncCompareCallsTo));
+    KPRF_NAME(HtmlWriteSortedFunctions)(pReport, pOut, "Functions-CallsTo",         "2.4 Calls To", KPRF_TYPE(,FIRSTCOLUMN_CALLS_TO));
+
+    qsort(&pReport->papSortedFunctions[0], pHdr->cFunctions, sizeof(pReport->papSortedFunctions[0]), KPRF_NAME(FuncCompareCallsFrom));
+    KPRF_NAME(HtmlWriteSortedFunctions)(pReport, pOut, "Functions-CallsFrom",       "2.5 Calls From", KPRF_TYPE(,FIRSTCOLUMN_CALLS_FROM));
+
+    fprintf(pOut,
+            "<h2><a name=\"Function-Details\">2.5 Function Details</a></h2>\n"
+            "\n"
+            "<p>\n"
+            "<table class=\"Functions\">\n");
+    for (uint32_t iFunc = 0; iFunc < pHdr->cFunctions; iFunc++)
+    {
+        KPRF_TYPE(P,REPORTFUNC) pReportFunc = &pReport->paFunctions[iFunc];
+        KPRF_TYPE(PC,FUNC) pFunc = pReportFunc->pFunc;
+
+        fprintf(pOut,
+                "<tr><td class=\"BlankRow\" colspan=7><a name=\"Func-%u\">&nbsp;</a></td></tr>\n",
+                iFunc);
+        KPRF_NAME(HtmlWriteRowU32)(pOut, "Function No.", iFunc, NULL);
+        if (pReportFunc->pSym)
+            KPRF_NAME(HtmlWriteRowString)(pOut, "Name", "Name", "%s", pReportFunc->pSym->szName);
+        if (pReportFunc->pLine)
+            KPRF_NAME(HtmlWriteRowString)(pOut, "Location", NULL, "<a href=\"file:///%s\">%s</a> Line #%d",
+                                          pReportFunc->pLine->szFile, pReportFunc->pLine->szFile, pReportFunc->pLine->iLine);
+        if (pReportFunc->pModSeg)
+        {
+            KPRF_NAME(HtmlWriteRowString)(pOut, "Module", NULL, "<a href=\"#Mod-%u\">%s</a>",
+                                          pReportFunc->pModSeg->pMod->iMod, pReportFunc->pModSeg->pModSeg->szPath);
+            KPRF_NAME(HtmlWriteRowString)(pOut, "Segment:Offset", NULL, "%x:%" KPRF_FMT_UPTR,
+                                          pReportFunc->pModSeg->pModSeg->iSegment,
+                                          pFunc->uEntryPtr - pReportFunc->pModSeg->pModSeg->uBasePtr);
+        }
+        KPRF_NAME(HtmlWriteRowUPTR)(pOut, "Address", pFunc->uEntryPtr, NULL);
+
+        KPRF_NAME(HtmlWriteRowTimeStat)(pOut, "On Stack", &pFunc->OnStack, pReport->ProfiledTicks);
+        KPRF_NAME(HtmlWriteRowTimeStat)(pOut, "On Top Of Stack", &pFunc->OnTopOfStack, pReport->ProfiledTicks);
+        KPRF_NAME(HtmlWriteRowCalls)(pOut, "Calls To", pFunc->cOnStack, pReport->cCalls);
+        KPRF_NAME(HtmlWriteRowCalls)(pOut, "Calls From", pFunc->cCalls, pReport->cCalls);
+
+        fprintf(pOut,
+                "\n");
+    }
+    fprintf(pOut,
+            "</table>\n"
+            "</p>\n"
+            "\n");
+
+    /*
+     * Threads.
+     */
+    fprintf(pOut,
+            "<h2><a name=\"Threads\">3.0 Threads</a></h2>\n"
+            "\n"
+            "<p>\n"
+            "<table class=\"Threads\">\n");
+
+    for (uint32_t iThread = 0; iThread < pHdr->cThreads; iThread++)
+    {
+        KPRF_TYPE(PC,THREAD) pThread = pReport->paThreads[iThread].pThread;
+
+        fprintf(pOut,
+                "<tr><td class=\"BlankRow\" colspan=7><a name=\"Thread-%u\">&nbsp;</a></td></tr>\n",
+                iThread);
+        KPRF_NAME(HtmlWriteRowU32)(pOut, "Thread No.", iThread, NULL);
+        KPRF_NAME(HtmlWriteRowX64)(pOut, "Thread Id", pThread->ThreadId, NULL);
+        if (pThread->szName[0])
+            KPRF_NAME(HtmlWriteRowString)(pOut, "Name", "Name", "%s", pThread->szName);
+        KPRF_NAME(HtmlWriteRowUPTR)(pOut, "Stack Base Address", pThread->uStackBasePtr, NULL);
+        KPRF_NAME(HtmlWriteRowUPTR)(pOut, "Max Stack Depth", pThread->cbMaxStack, "bytes");
+        //KPRF_NAME(HtmlWriteRowUPTR)(pOut, "Max Stack Depth", pThread->cMaxFrames, "frames"); /** @todo max stack frames! */
+        KPRF_NAME(HtmlWriteRowTicks)(pOut, "Profiled", pThread->ProfiledTicks, pReport->ProfiledTicks);
+        KPRF_NAME(HtmlWriteRowTicks)(pOut, "Sleep", pThread->SleepTicks, pReport->ProfiledTicks);
+        KPRF_NAME(HtmlWriteRowTicks)(pOut, "Overhead", pThread->OverheadTicks, pReport->ProfiledTicks + pReport->OverheadTicks);
+        KPRF_NAME(HtmlWriteRowCalls)(pOut, "Recorded Calls",  pThread->cCalls, pReport->cCalls);
+        KPRF_NAME(HtmlWriteRowU64)(pOut, "Unwinds", pThread->cUnwinds, NULL);
+        KPRF_NAME(HtmlWriteRowU64)(pOut, "Profiler Stack Overflows", pThread->cOverflows, NULL);
+        KPRF_NAME(HtmlWriteRowU64)(pOut, "Profiler Stack Switch Rejects", pThread->cStackSwitchRejects, NULL);
+
+        fprintf(pOut,
+                "\n");
+    }
+    fprintf(pOut,
+            "</table>\n"
+            "</p>\n"
+            "\n");
+
+
+    /*
+     * Modules.
+     */
+    fprintf(pOut,
+            "<h2><a name=\"Modules\">4.0 Modules</a></h2>\n"
+            "\n"
+            "<p>\n"
+            "<table class=\"Modules\">\n");
+
+    KPRF_TYPE(P,REPORTMOD) pMod = pReport->pFirstMod;
+    uint32_t iMod = 0;
+    while (pMod)
+    {
+        fprintf(pOut,
+                "<a name=\"Mod-%u\">\n"
+                "<tr><td class=\"BlankRow\" colspan=7><a name=\"Module-%u\">&nbsp;</a></td></tr>\n",
+                iMod);
+        KPRF_NAME(HtmlWriteRowU32)(pOut, "Module No.", iMod, NULL);
+        KPRF_NAME(HtmlWriteRowString)(pOut, "Name", "Name", "%s", pMod->pFirstSeg->pModSeg->szPath);
+
+        for (KPRF_TYPE(P,REPORTMODSEG) pSeg = pMod->pFirstSeg; pSeg; pSeg = pSeg->pNext)
+        {
+            char szName[64];
+            sprintf(szName, "Segment No.%u - Base", pSeg->pModSeg->iSegment);
+            KPRF_NAME(HtmlWriteRowUPTR)(pOut, szName, pSeg->pModSeg->uBasePtr, NULL);
+            sprintf(szName, "Segment No.%u - Size", pSeg->pModSeg->iSegment);
+            KPRF_NAME(HtmlWriteRowUPTR)(pOut, szName,
+                                        pSeg->pModSeg->cbSegmentMinusOne + 1 > pSeg->pModSeg->cbSegmentMinusOne
+                                        ? pSeg->pModSeg->cbSegmentMinusOne + 1
+                                        : pSeg->pModSeg->cbSegmentMinusOne,
+                                        NULL);
+        }
+
+        KPRF_NAME(HtmlWriteRowTicks)(pOut, "On Stack", pMod->OnStackTicks, pReport->ProfiledTicks);
+        KPRF_NAME(HtmlWriteRowTicks)(pOut, "On Top Of Stack", pMod->OnTopOfStackTicks, pReport->ProfiledTicks);
+        KPRF_NAME(HtmlWriteRowU32)(pOut, "Functions", pMod->cFunctions, NULL);
+
+        fprintf(pOut,
+                "\n");
+
+        /* next */
+        iMod++;
+        pMod = pMod->pNext;
+    }
+    fprintf(pOut,
+            "</table>\n"
+            "</p>\n"
+            "\n");
+
+
+    /*
+     * The End.
+     */
+    fprintf(pOut,
+            "</body>\n"
+            "</html>\n");
+    return 0;
+}
Index: trunk/kProfile/prfx86msc.asm
===================================================================
--- trunk/kProfile/prfx86msc.asm	(revision 3524)
+++ trunk/kProfile/prfx86msc.asm	(revision 3524)
@@ -0,0 +1,392 @@
+; $Id: $
+;; @file
+;
+; kProfiler MK2 - Microsoft C/C++ Compiler Interaction.
+;
+;
+; Copyright (c) 2006 knut st. osmundsen <bird-src-spam@anduin.net.de>
+;
+;
+; This file is part of kLIBC.
+;
+; kLIBC is free software; you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation; either version 2 of the License, or
+; (at your option) any later version.
+;
+; kLIBC is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+; GNU General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with kLIBC; if not, write to the Free Software
+; Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+;
+;
+
+[section .data]
+;
+g_fCalibrated:
+        dd 0
+g_OverheadAdj:
+        dd 0
+
+[section .text]
+
+extern KPRF_ENTER
+extern KPRF_LEAVE
+
+global __penter
+global __pexit
+
+;ifdef  UNDEFINED
+global common_return_path
+global common_overhead
+global common_no_overhead
+global calibrate
+global outer_calibration_loop
+global inner_calibration_loop
+global calib_inner_update_minimum
+global calib_inner_next
+global calib_outer_dec
+global calib_outer_inc
+global calib_done
+global calib_nullproc
+;endif
+
+
+;;
+; On x86 the call to this function has been observed to be put before
+; creating the stack frame, as the very first instruction in the function.
+;
+; Thus the stack layout is as follows:
+;       24      return address of the calling function.
+;       20      our return address - the address of the calling function + 5.
+;       1c      eax
+;       18      edx
+;       14      eflags
+;       10      ecx
+;       c       tsc high       - param 3
+;       8       tsc low
+;       4       frame pointer  - param 2
+;       0       function ptr   - param 1
+;
+;
+align 16
+__penter:
+        ; save volatile register and get the time stamp.
+        push    eax
+        push    edx
+        rdtsc
+        pushfd
+        push    ecx
+
+        ; setting up the enter call frame (cdecl).
+        sub     esp, 4 + 4 + 8
+        mov     [esp + 0ch], edx        ; Param 3 - the timestamp
+        mov     [esp + 08h], eax
+        lea     edx, [esp + 24h]        ; Param 2 - frame pointer (pointer to the return address of the function calling us)
+        mov     [esp + 04h], edx
+        mov     eax, [esp + 20h]        ; Param 1 - The function address
+        sub     eax, 5                  ; call instruction
+        mov     [esp], eax
+
+        call    KPRF_ENTER
+        jmp     common_return_path
+
+
+;;
+; On x86 the call to this function has been observed to be put right before
+; return instruction. This fact matters since since we have to calc the same
+; stack address as in _penter.
+;
+; Thus the stack layout is as follows:
+;       24      return address of the calling function.
+;       20      our return address - the address of the calling function + 5.
+;       1c      eax
+;       18      edx
+;       14      eflags
+;       10      ecx
+;       c       tsc high       - param 3
+;       8       tsc low
+;       4       frame pointer  - param 2
+;       0       function ptr   - param 1
+;
+;
+align 16
+__pexit:
+        ; save volatile register and get the time stamp.
+        push    eax
+        push    edx
+        rdtsc
+        pushfd
+        push    ecx
+
+        ; setting up the leave call frame (cdecl).
+        sub     esp, 4 + 4 + 8
+        mov     [esp + 0ch], edx        ; Param 3 - the timestamp
+        mov     [esp + 08h], eax
+        lea     edx, [esp + 24h]        ; Param 2 - frame pointer (pointer to the return address of the function calling us)
+        mov     [esp + 04h], edx
+        mov     eax, [esp + 20h]        ; Param 1 - Some address in the function.
+        sub     eax, 5                  ; call instruction
+        mov     [esp], eax
+
+        call    KPRF_LEAVE
+        jmp common_return_path
+
+
+;;
+; This is the common return path for both the enter and exit hooks.
+; It's kept common because we can then use the same overhead adjustment
+; and save some calibration efforts. It also saves space :-)
+align 16
+common_return_path:
+        ; Update overhead
+        test    eax, eax
+        jz      common_no_overhead
+        cmp     byte [g_fCalibrated], 0
+        jnz     common_overhead
+        call    calibrate
+common_overhead:
+        mov     ecx, eax                ; ecx <- pointer to overhead counter.
+        mov     eax, [g_OverheadAdj]    ; apply the adjustment before reading tsc
+        sub     [esp + 08h], eax
+        sbb     dword [esp + 0ch], 0
+
+        rdtsc
+        sub     eax, [esp + 08h]
+        sbb     edx, [esp + 0ch]
+        add     [ecx], eax
+        adc     [ecx + 4], edx
+common_no_overhead:
+        add     esp, 4 + 4 + 8
+
+        ; restore volatile registers.
+        pop     ecx
+        popfd
+        pop     edx
+        pop     eax
+        ret
+
+;;
+; Data esi points to while we're calibrating.
+struc CALIBDATA
+    .OverheadLo resd 1
+    .OverheadHi resd 1
+    .ProfiledLo resd 1
+    .ProfiledHi resd 1
+    .EnterTSLo  resd 1
+    .EnterTSHi  resd 1
+    .MinLo      resd 1
+    .MinHi      resd 1
+endstruc
+
+
+
+align 16
+;;
+; Do necessary calibrations.
+;
+calibrate:
+        ; prolog
+        push    ebp
+        mov     ebp, esp
+        pushfd
+        pushad
+        sub     esp, CALIBDATA_size
+        mov     esi, esp                ; esi points to the CALIBDATA
+
+        ;
+        ; Indicate that we have finished calibrating.
+        ;
+        mov     eax, 1
+        xchg    dword [g_fCalibrated], eax
+
+        ;
+        ; The outer loop - find the right adjustment.
+        ;
+        mov     ebx, 200h               ; loop counter.
+calib_outer_loop:
+
+        ;
+        ; The inner loop - calls the function number of times to establish a
+        ;                  good minimum value
+        ;
+        mov     ecx, 200h
+        mov     dword [esi + CALIBDATA.MinLo], 0ffffffffh
+        mov     dword [esi + CALIBDATA.MinHi], 07fffffffh
+calib_inner_loop:
+
+        ; zero the overhead and profiled times.
+        xor     eax, eax
+        mov     [esi + CALIBDATA.OverheadLo], eax
+        mov     [esi + CALIBDATA.OverheadHi], eax
+        mov     [esi + CALIBDATA.ProfiledLo], eax
+        mov     [esi + CALIBDATA.ProfiledHi], eax
+        call    calib_nullproc
+
+        ; subtract the overhead
+        mov     eax, [esi + CALIBDATA.ProfiledLo]
+        mov     edx, [esi + CALIBDATA.ProfiledHi]
+        sub     eax, [esi + CALIBDATA.OverheadLo]
+        sbb     edx, [esi + CALIBDATA.OverheadHi]
+
+        ; update the minimum value.
+        test    edx, 080000000h
+        jnz near calib_outer_dec        ; if negative, just simplify and shortcut
+        cmp     edx, [esi + CALIBDATA.MinHi]
+        jg      calib_inner_next
+        jl      calib_inner_update_minimum
+        cmp     eax, [esi + CALIBDATA.MinLo]
+        jge     calib_inner_next
+calib_inner_update_minimum:
+        mov     [esi + CALIBDATA.MinLo], eax
+        mov     [esi + CALIBDATA.MinHi], edx
+calib_inner_next:
+        loop    calib_inner_loop
+
+        ; Is the minimum value acceptable?
+        test    dword [esi + CALIBDATA.MinHi], 80000000h
+        jnz     calib_outer_dec         ; simplify if negative.
+        cmp     dword [esi + CALIBDATA.MinHi], 0
+        jnz     calib_outer_inc         ; this shouldn't be possible
+        cmp     dword [esi + CALIBDATA.MinLo], 1fh
+        jbe     calib_outer_dec         ; too low - 2 ticks per pair is the minimum!
+        cmp     dword [esi + CALIBDATA.MinLo], 30h
+        jbe     calib_done              ; this is fine!
+calib_outer_inc:
+        inc     dword [g_OverheadAdj]
+        jmp     calib_outer_next
+calib_outer_dec:
+        cmp     dword [g_OverheadAdj], 1
+        je      calib_done
+        dec     dword [g_OverheadAdj]
+calib_outer_next:
+        dec     ebx
+        jnz     calib_outer_loop
+calib_done:
+
+        ; epilog
+        add     esp, CALIBDATA_size
+        popad
+        popfd
+        leave
+        ret
+
+
+
+
+;;
+; The calibration __penter - this must be identical to the real thing except for the KPRF call.
+align 16
+calib_penter:
+        ; This part must be identical
+        push    eax
+        push    edx
+        rdtsc
+        pushfd
+        push    ecx
+
+        ; store the entry
+        mov     [esi + CALIBDATA.EnterTSLo], eax
+        mov     [esi + CALIBDATA.EnterTSHi], edx
+
+        ; create the call frame
+        push    edx
+        push    eax
+        push    0
+        push    0
+
+        lea     eax, [esi + CALIBDATA.OverheadLo]
+        jmp     common_overhead
+
+
+;;
+; The calibration __pexit - this must be identical to the real thing except for the KPRF call.
+align 16
+calib_pexit:
+        ; This part must be identical
+        push    eax
+        push    edx
+        rdtsc
+        pushfd
+        push    ecx
+
+        ; update the time
+        push    eax
+        push    edx
+        sub     eax, [esi + CALIBDATA.EnterTSLo]
+        sbb     edx, [esi + CALIBDATA.EnterTSHi]
+        add     [esi + CALIBDATA.ProfiledLo], eax
+        adc     [esi + CALIBDATA.ProfiledHi], edx
+        pop     edx
+        pop     eax
+
+        ; create the call frame
+        push    edx
+        push    eax
+        push    0
+        push    0
+
+        lea     eax, [esi + CALIBDATA.EnterTSLo]
+        jmp     common_overhead
+
+
+;;
+; The 'function' we're profiling.
+; The general idea is that each pair should take something like 2-10 ticks.
+;
+; (Btw. If we don't use multiple pairs here, we end up with the wrong result.)
+align 16
+calib_nullproc:
+        call    calib_penter ;0
+        call    calib_pexit
+
+        call    calib_penter ;1
+        call    calib_pexit
+
+        call    calib_penter ;2
+        call    calib_pexit
+
+        call    calib_penter ;3
+        call    calib_pexit
+
+        call    calib_penter ;4
+        call    calib_pexit
+
+        call    calib_penter ;5
+        call    calib_pexit
+
+        call    calib_penter ;6
+        call    calib_pexit
+
+        call    calib_penter ;7
+        call    calib_pexit
+
+        call    calib_penter ;8
+        call    calib_pexit
+
+        call    calib_penter ;9
+        call    calib_pexit
+
+        call    calib_penter ;a
+        call    calib_pexit
+
+        call    calib_penter ;b
+        call    calib_pexit
+
+        call    calib_penter ;c
+        call    calib_pexit
+
+        call    calib_penter ;d
+        call    calib_pexit
+
+        call    calib_penter ;e
+        call    calib_pexit
+
+        call    calib_penter ;f
+        call    calib_pexit
+        ret
+
