Index: trunk/src/odinprof/initterm.cpp
===================================================================
--- trunk/src/odinprof/initterm.cpp	(revision 7421)
+++ trunk/src/odinprof/initterm.cpp	(revision 7421)
@@ -0,0 +1,155 @@
+/*
+ * DLL entry point
+ *
+ * Copyright 1998 Sander van Leeuwen
+ * Copyright 1998 Peter Fitzsimmons
+ *
+ *
+ * Project Odin Software License can be found in LICENSE.TXT
+ *
+ */
+
+/*-------------------------------------------------------------*/
+/* INITERM.C -- Source for a custom dynamic link library       */
+/*              initialization and termination (_DLL_InitTerm) */
+/*              function.                                      */
+/*                                                             */
+/* When called to perform initialization, this sample function */
+/* gets storage for an array of integers, and initializes its  */
+/* elements with random integers.  At termination time, it     */
+/* frees the array.  Substitute your own special processing.   */
+/*-------------------------------------------------------------*/
+
+
+/* Include files */
+#define  INCL_DOSMODULEMGR
+#define  INCL_DOSPROCESS
+#define  INCL_DOSERRORS
+#include <os2wrap.h>    //Odin32 OS/2 api wrappers
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <odin.h>
+#include <misc.h>       /*PLF Wed  98-03-18 23:18:15*/
+#include <exitlist.h>
+#include <initdll.h>
+
+#ifdef __IBMCPP__
+extern "C" {
+
+/*-------------------------------------------------------------------*/
+/* A clean up routine registered with DosExitList must be used if    */
+/* runtime calls are required and the runtime is dynamically linked. */
+/* This will guarantee that this clean up routine is run before the  */
+/* library DLL is terminated.                                        */
+/*-------------------------------------------------------------------*/
+static void APIENTRY cleanup(ULONG reason);
+}
+
+/****************************************************************************/
+/* _DLL_InitTerm is the function that gets called by the operating system   */
+/* loader when it loads and frees this DLL for each process that accesses   */
+/* this DLL.  However, it only gets called the first time the DLL is loaded */
+/* and the last time it is freed for a particular process.  The system      */
+/* linkage convention MUST be used because the operating system loader is   */
+/* calling this function.                                                   */
+/****************************************************************************/
+unsigned long SYSTEM _DLL_InitTerm(unsigned long hModule, unsigned long
+                                   ulFlag)
+{
+   APIRET rc;
+
+   /*-------------------------------------------------------------------------*/
+   /* If ulFlag is zero then the DLL is being loaded so initialization should */
+   /* be performed.  If ulFlag is 1 then the DLL is being freed so            */
+   /* termination should be performed.                                        */
+   /*-------------------------------------------------------------------------*/
+
+   switch (ulFlag) {
+      case 0 :
+
+         /*******************************************************************/
+         /* The C run-time environment initialization function must be      */
+         /* called before any calls to C run-time functions that are not    */
+         /* inlined.                                                        */
+         /*******************************************************************/
+
+         if (_CRT_init() == -1)
+            return 0UL;
+         ctordtorInit();
+
+         /*******************************************************************/
+         /* A DosExitList routine must be used to clean up if runtime calls */
+         /* are required and the runtime is dynamically linked.             */
+         /*******************************************************************/
+
+         rc = DosExitList(EXITLIST_ODINPROF|EXLST_ADD, cleanup);
+         if(rc)
+                return 0UL;
+         #if 1 /*
+                * Experimental console hack. Sets apptype to PM anyhow.
+                * First Dll to be initiated should now allways be OdinCrt!
+                * So include odincrt first!
+                */
+            PPIB pPIB;
+            PTIB pTIB;
+            rc = DosGetInfoBlocks(&pTIB, &pPIB);
+            if (rc != NO_ERROR)
+                return 0UL;
+            pPIB->pib_ultype = 3;
+         #endif
+         break;
+      case 1 :
+         break;
+      default  :
+         return 0UL;
+   }
+
+   /***********************************************************/
+   /* A non-zero value must be returned to indicate success.  */
+   /***********************************************************/
+   return 1UL;
+}
+
+
+static void APIENTRY cleanup(ULONG ulReason)
+{
+   ctordtorTerm();
+   _CRT_term();
+   DosExitList(EXLST_EXIT, cleanup);
+   return ;
+}
+
+#elif defined(__WATCOM_CPLUSPLUS__)
+
+/*
+ * Watcom dll init and term routines.
+ */
+
+int __dll_initialize(unsigned long hModule, unsigned long ulFlag)
+{
+    APIRET rc;
+    #if 1 /*
+          * Experimental console hack. Sets apptype to PM anyhow.
+          * First Dll to be initiated should now allways be OdinCrt!
+          * So include odincrt first!
+          */
+    PPIB pPIB;
+    PTIB pTIB;
+    rc = DosGetInfoBlocks(&pTIB, &pPIB);
+    if (rc != NO_ERROR)
+        return 0UL;
+    pPIB->pib_ultype = 3;
+    #endif
+    return 1;
+}
+
+int __dll_terminate(unsigned long hModule, unsigned long ulFlag)
+{
+    return 1;
+}
+
+#else
+#error message("compiler is not supported");
+#endif
+
Index: trunk/src/odinprof/makefile
===================================================================
--- trunk/src/odinprof/makefile	(revision 7421)
+++ trunk/src/odinprof/makefile	(revision 7421)
@@ -0,0 +1,60 @@
+# $Id: makefile,v 1.1 2001-11-22 10:43:59 phaller Exp $
+
+#
+# Odin32 API
+#
+#       odinprof.dll makefile
+#
+
+
+#
+# Tell the buildenvironment not to
+#
+NOTEXPDEF   = 1
+
+
+#
+# Compiler, tools, and interference rules.
+#
+!include ../../makefile.inc
+
+
+#
+# Overrides.
+#
+CXXFLAGS    = $(CXXFLAGS_ODINCRT)
+LD2FLAGS    = $(LD2FLAGS_ODINCRT)
+CDEFINES    = $(CDEFINES_ODINCRT)
+IMPLIBFLAGS = $(IMPLIBFLAGS) /NOIgnoreCase
+DEFFILE     = odinprof.def
+
+
+#
+# Object files. All objects should be prefixed with $(OBJDIR)!
+#
+OBJS = \
+$(OBJDIR)\odinprof.obj \
+$(OBJDIR)\initterm.obj \
+$(OBJDIR)\profiler.obj \
+$(OBJDIR)\profasm.obj \
+$(OBJDIR)\profcollection.obj \
+$(OBJDIR)\symfile.obj
+
+
+#
+# Libraries. One space before the '\'.
+#
+
+LIBS = demangl.lib
+
+#
+# Target name - name of the dll without extention and path.
+#
+TARGET = odinprof
+
+
+#
+# Includes the common rules.
+#
+!include $(ODIN32_POST_INC)
+
Index: trunk/src/odinprof/odinprof.asm
===================================================================
--- trunk/src/odinprof/odinprof.asm	(revision 7421)
+++ trunk/src/odinprof/odinprof.asm	(revision 7421)
@@ -0,0 +1,51 @@
+; $Id: odinprof.asm,v 1.1 2001-11-22 10:43:59 phaller Exp $
+
+;/*
+; * Project Odin Software License can be found in LICENSE.TXT
+; * Win32 Exception handling + misc functions for OS/2
+; *
+; * Copyright 1998 Sander van Leeuwen
+; *
+; */
+
+.386p
+                NAME    odinfs
+
+CODE32          SEGMENT DWORD PUBLIC USE32 'CODE'
+                ASSUME  DS:FLAT, SS:FLAT
+
+        PUBLIC GetFS
+GetFS   proc near
+        mov     eax, fs
+        ret
+GetFS   endp
+
+
+        PUBLIC SetFS
+SetFS   proc near
+        mov     eax, [esp+4]
+        mov     fs, eax
+        ret
+SetFS   endp
+
+
+        PUBLIC SetReturnFS
+SetReturnFS proc near
+        push    fs
+        mov     eax, [esp+8]
+        mov     fs, eax
+        pop     eax
+        ret
+SetReturnFS endp
+
+
+        PUBLIC RestoreOS2FS
+RestoreOS2FS proc near
+        push    150bh           ; @@@PH that's NOT the clean way ! :)
+        mov     ax, fs
+        pop     fs
+        ret
+RestoreOS2FS endp
+
+CODE32          ENDS
+                END
Index: trunk/src/odinprof/odinprof.def
===================================================================
--- trunk/src/odinprof/odinprof.def	(revision 7421)
+++ trunk/src/odinprof/odinprof.def	(revision 7421)
@@ -0,0 +1,14 @@
+; $Id: odinprof.def,v 1.1 2001-11-22 10:43:59 phaller Exp $
+; Odin VAC++ 3.08 profiler library
+LIBRARY ODINPROF INITINSTANCE TERMINSTANCE
+DESCRIPTION 'Odin32 System DLL - VAC++ 3.08 Profiler'
+DATA MULTIPLE NONSHARED READWRITE LOADONCALL
+CODE LOADONCALL
+
+EXPORTS
+    ProfilerIsEnabled     @1 
+    ProfilerEnable        @2 
+    ProfilerInitialize    @3 
+    ProfilerWrite         @4 
+    ProfilerTerminate     @5 
+    odin_ProfileHook32    @6 
Index: trunk/src/odinprof/profasm.asm
===================================================================
--- trunk/src/odinprof/profasm.asm	(revision 7421)
+++ trunk/src/odinprof/profasm.asm	(revision 7421)
@@ -0,0 +1,119 @@
+; $Id: profasm.asm,v 1.1 2001-11-22 10:43:59 phaller Exp $
+
+;/*
+; * Project Odin Software License can be found in LICENSE.TXT
+; * Execution Trace Profiler
+; *
+; * Copyright 2001 Patrick Haller <patrick.haller@innotek.de>
+; *
+; */
+
+.586p
+                NAME    profasm
+
+extrn           C_ProfileHook32Enter: proc
+extrn           C_ProfileHook32Exit: proc
+
+CODE32          SEGMENT DWORD PUBLIC USE32 'CODE'
+                ASSUME  DS:FLAT, SS:FLAT
+
+		public odin_ProfileHook32
+odin_ProfileHook32	proc near
+
+                ; --------
+                ; top half
+                ; --------
+
+                pushfd
+                push eax  ; save altered registers
+                push edx
+                push ecx
+
+                rdtsc     ; push the machine timestamp
+                          ; (assuming at least Pentium CPU is available)
+                push eax
+                push edx
+                mov  edx, dword ptr [esp+1Ch]
+                push edx     ; push return address for bottom half
+                mov  edx, dword ptr [esp+1Ch]
+                push edx     ; push entry address of caller function
+
+                ; ret_addr _System C_ProfilerHook32Enter(ULONG eip_function, ULONG ret, ULONGLONG tsc)
+                call C_ProfileHook32Enter
+                add esp,  10h
+
+                ; restore the stack as it was before
+                ; and replace the (saved) caller's address by the
+                ; hook's bottom half address
+                mov dword ptr [esp + 14h], eax
+
+                pop ecx
+                pop edx
+                pop eax
+                popfd
+
+                ret
+odin_ProfileHook32 endp
+
+
+		public odin_ProfileHook32Bottom
+odin_ProfileHook32Bottom proc near
+
+                ; -----------
+                ; bottom half
+                ; -----------
+
+                push 0    ; make room for virtual jump
+
+                pushfd
+                push eax  ; save altered registers
+                push edx
+                push ecx
+
+                rdtsc     ; push the machine timestamp
+                          ; (assuming at least Pentium CPU is available)
+                push eax
+                push edx
+
+                ; ret_addr _System C_ProfilerHook32Exit(ULONGLONG tsc)
+                call C_ProfileHook32Exit
+                add esp,  08h
+
+                ; restore the stack as it was before
+                ; and virtually JUMP BACK to the caller's caller
+                ; eax now contains the return address
+                mov dword ptr [esp + 10h], eax
+
+                pop  ecx
+                pop  edx   ; restore altered registers
+                pop  eax
+                popfd
+
+                ; this is a virtual jump!
+                ret
+odin_ProfileHook32Bottom	endp
+
+		public _ProfileGetTimestamp
+_ProfileGetTimestamp proc near
+
+                push eax  ; save altered registers
+                push edx
+                push edi
+
+                rdtsc     ; push the machine timestamp
+                          ; (assuming at least Pentium CPU is available)
+                mov edi, dword ptr [esp+14h]
+                mov dword ptr [edi], eax
+                mov edi, dword ptr [esp+10h]
+                mov dword ptr [edi], edx
+
+                pop edi
+                pop edx
+                pop eax
+
+                ret
+_ProfileGetTimestamp	endp
+
+
+CODE32          ENDS
+                END
Index: trunk/src/odinprof/profcollection.cpp
===================================================================
--- trunk/src/odinprof/profcollection.cpp	(revision 7421)
+++ trunk/src/odinprof/profcollection.cpp	(revision 7421)
@@ -0,0 +1,909 @@
+/* $Id: profcollection.cpp,v 1.1 2001-11-22 10:43:59 phaller Exp $ */
+
+/*
+ * Collection class:
+ *   provides very fast object lookup by index number
+ *
+ * Copyright 2001 Patrick Haller <patrick.haller@innotek.de>
+ *
+ *
+ * Project Odin Software License can be found in LICENSE.TXT
+ *
+ *
+ */
+
+#include <ccollection.h>
+
+
+
+static inline unsigned long _Optlink hashcode(int iRing, char *pszName)
+{
+    unsigned long h;
+    unsigned char *t = (unsigned char*)pszName;
+
+    for (h = 0;
+         *t;
+         t++)
+    {
+        h = (h << 6);
+        h += *t;
+        h %= iRing;
+    }
+
+    return h;
+}
+
+
+
+CCollection::CCollection(int iInitialSize)
+{
+    iSize = iInitialSize;
+}
+
+CCollection::~CCollection()
+{
+}
+
+
+CIndexLookup::CIndexLookup(int iInitialSize)
+{
+    // iSize is expected to be set by parent constructor
+
+    // allocate array with given size
+    if (iInitialSize > 0)
+    {
+        pEntries = (PINDEXLOOKUPENTRY)malloc(iInitialSize * sizeof(INDEXLOOKUPENTRY));
+        memset(pEntries,
+               0,
+               iInitialSize * sizeof(INDEXLOOKUPENTRY));
+    }
+    else
+        // no initial array provided
+        pEntries = NULL;
+
+    iOffset = INT_MAX;
+    iHigh = INT_MIN;
+    iUsedOffset = INT_MAX;
+    iUsedHigh = INT_MIN;
+}
+
+
+CIndexLookup::~CIndexLookup()
+{
+    free(pEntries);
+}
+
+
+int CIndexLookup::isValidIndex(int iIndex)
+{
+    /* determine if the given index is within bounds */
+    return ( (iIndex >= iOffset) &&
+             (iIndex <= iHigh) );
+}
+
+
+int CIndexLookup::shrink()
+{
+    // shrink the index array to the absolutely necessary size only
+
+    if (iInitialized == 0)
+    {
+        // if no elements were allocated, simple remove all data memory
+        if (pEntries)
+        {
+            free(pEntries);
+            pEntries = NULL;
+        }
+
+        return 1;
+    }
+
+    // verify if shrink can do anything good
+    if ( (iOffset < iUsedOffset) ||
+         (iHigh   > iUsedHigh) )
+    {
+        // OK, reallocate the beast
+        int iRequiredSize = iUsedHigh - iUsedOffset + 1;
+
+        PINDEXLOOKUPENTRY pNewData = (PINDEXLOOKUPENTRY)malloc(iRequiredSize * sizeof(INDEXLOOKUPENTRY));
+        if (NULL == pNewData)
+        {
+            /* signal failure */
+            return 0;
+        }
+
+        // copy old data and write new boundary info
+        if (NULL != pEntries)
+        {
+            int iRelative = iUsedOffset - iOffset;
+
+            // Note: due to C++ pointer arithmetic,
+            // (pEntries + iRelative) should automatically
+            // increase pEntries by iRelative-times the
+            // sizeof(INDEXLOOKUPENTRY) byte positions!
+            memmove(pNewData,
+                    pEntries + iRelative,
+                    iRequiredSize * sizeof(INDEXLOOKUPENTRY));
+    
+            // deferred delete ensures we've always got a valid array
+            PINDEXLOOKUPENTRY pTemp = pEntries;
+            pEntries = pNewData;
+            free(pTemp);
+        }
+        else
+        {
+            // set new pointer and we're done
+            memset(pNewData,
+                   0,
+                   iRequiredSize * sizeof(INDEXLOOKUPENTRY));
+
+            pEntries = pNewData;
+        }
+
+        iSize = iRequiredSize;
+        iOffset = iUsedOffset;
+        iHigh = iUsedHigh;
+
+        return 1; // shrunk
+    }
+    else
+        return 0; // didn't do anything
+}
+
+
+PINDEXLOOKUPENTRY CIndexLookup::reallocateData(int iShift, int iRequiredSize)
+{
+    // Note: assumption is iRequiredSize is > iSize!
+    // if (iRequiredSize < iSize)
+    //   return NULL;
+
+    PINDEXLOOKUPENTRY pNewData = (PINDEXLOOKUPENTRY)malloc(iRequiredSize * sizeof(INDEXLOOKUPENTRY));
+    if (NULL == pNewData)
+    {
+        /* signal failure */
+        return NULL;
+    }
+
+    // copy current data
+    if (pEntries != NULL)
+    {
+        // copy the data, array cannot overlap
+        memmove(pNewData + (iShift * sizeof(INDEXLOOKUPENTRY)),
+                pEntries,
+                iSize * sizeof(INDEXLOOKUPENTRY));
+
+        // zero out at the beginning or at the end
+        if (iShift == 0)
+        {
+            // clear trailing
+            memset(pNewData + sizeof(INDEXLOOKUPENTRY) * iSize,
+                   0,
+                   sizeof(INDEXLOOKUPENTRY) * (iRequiredSize - iSize));
+        }
+        else
+        {
+            // clear leading
+            memset(pNewData,
+                   0,
+                   sizeof(INDEXLOOKUPENTRY) * (iShift));
+        }
+
+    }
+    else
+        // clear brand-new array
+        memset(pNewData,
+               0,
+               iRequiredSize * sizeof(INDEXLOOKUPENTRY));
+
+
+    return pNewData;
+}
+
+
+int CIndexLookup::ensureCapacity(int iIndex)
+{
+    // we need to resize if the index is not valid
+    if (!isValidIndex(iIndex))
+    {
+        // calculate new array bounds,
+        // copy original table,
+        // swap table pointers
+        // and free original pointer
+
+        // check if new base offset is lower
+        if (iIndex < iOffset)
+        {
+            // insert space at the front
+
+            // calculate new size
+            int iRequiredSize = iHigh - iIndex + 1;
+
+            // reallocate the array
+            int iShift = iOffset - iIndex; // for how many elements to shift left
+
+            PINDEXLOOKUPENTRY pNewData = reallocateData(iShift, iRequiredSize);
+            if (NULL == pNewData)
+                // signal failure
+                return 0;
+
+            // deferred delete ensures we've always got a valid array
+            PINDEXLOOKUPENTRY pTemp = pEntries;
+            pEntries = pNewData;
+            if (pTemp)
+                free(pTemp);
+
+            iSize = iRequiredSize;
+            iOffset = iIndex;
+        }
+        else
+            if (iIndex > iHigh)
+            {
+                // lengthen the array
+
+                // calculate new size
+                int iRequiredSize = iIndex - iOffset + 1;
+
+                // reallocate the array
+                PINDEXLOOKUPENTRY pNewData = reallocateData(0, iRequiredSize);
+                if (NULL == pNewData)
+                    // signal failure
+                    return 0;
+
+                // deferred delete ensures we've always got a valid array
+                PINDEXLOOKUPENTRY pTemp = pEntries;
+                pEntries = pNewData;
+                if (pTemp)
+                    free(pTemp);
+                
+                iSize = iRequiredSize;
+                iHigh = iIndex;
+            }
+            else
+                // internal logic flaw!
+                return 0;
+    }
+
+    return 1; /* OK */
+}
+
+
+void* CIndexLookup::addElement(int iIndex, void* pObject)
+{
+    // ensure the array can hold the entry
+    if (ensureCapacity(iIndex))
+    {
+        // update the used low and high marks
+        if (iIndex < iUsedOffset)
+            iUsedOffset = iIndex;
+
+        if (iIndex > iUsedHigh)
+            iUsedHigh = iIndex;
+
+        // used limits are not initialized
+        iInitialized = 1;
+
+        // insert entry and quit
+        pEntries[iIndex - iOffset].pObject = pObject;
+        return pObject;
+    }
+    else
+    {
+        // signal failure
+        return NULL;
+    }
+}
+
+
+void* CIndexLookup::removeElement(int iIndex)
+{
+    // check if index is within table
+    if (isValidIndex(iIndex))
+    {
+        // remove specified element from the array
+        void *pOld = pEntries[iIndex - iOffset].pObject;
+        pEntries[iIndex - iOffset].pObject = NULL;
+
+        return pOld;
+    }
+    else
+    {
+        // signal failure
+        return NULL;
+    }
+}
+
+
+void CIndexLookup::clear(void)
+{
+    free(pEntries);
+    pEntries = NULL;
+    iOffset = INT_MAX;
+    iHigh = INT_MIN;
+    iSize = 0;
+    iUsedOffset = INT_MAX;
+    iUsedHigh = INT_MIN;
+    iInitialized = 0;
+}
+
+
+void* CIndexLookup::getElement(int iIndex)
+{
+    if (isValidIndex(iIndex))
+    {
+        // OK, return entry
+        return pEntries[iIndex - iOffset].pObject;
+    }
+    else
+    {
+        // signal: not found
+        return NULL;
+    }
+}
+
+
+CIndexLookupLimit::CIndexLookupLimit(int iHardLimitLow,
+                                     int iHardLimitHigh)
+  : CIndexLookup(0)
+{
+    // iSize is expected to be set by parent constructor
+
+    // enable hard limits
+    iLimitLow = min(iHardLimitLow, iHardLimitHigh);
+    iLimitHigh = max (iHardLimitLow, iHardLimitHigh);
+
+    // size the array
+    iOffset = iLimitLow;
+    iHigh = iLimitHigh;
+    iSize = iHigh - iOffset + 1;
+    pEntries = reallocateData(0, iSize);
+}
+
+
+CIndexLookupLimit::~CIndexLookupLimit()
+{
+}
+
+
+int CIndexLookupLimit::ensureCapacity(int iIndex)
+{
+    // verify against hard limits if enabled
+    // prevent growing under the low limit
+    if (iIndex < iLimitLow)
+        return 0;
+
+    // prevent growing above the high limit
+    if (iIndex > iLimitHigh)
+        return 0;
+
+
+    return CIndexLookup::ensureCapacity(iIndex);
+}
+
+
+CLinearList::CLinearList()
+  : CCollection(0)
+{
+    pFirst = NULL;
+    pLast  = NULL;
+}
+
+
+CLinearList::~CLinearList()
+{
+    clear();
+}
+
+
+PLINEARLISTENTRY CLinearList::addFirst(void* pObject)
+{
+    if (pFirst)
+        return addBefore(pFirst, pObject);
+    else
+        return add0(pObject);
+}
+
+
+PLINEARLISTENTRY CLinearList::addLast(void* pObject)
+{
+    if (pLast)
+        return addAfter(pLast, pObject);
+    else
+        return add0(pObject);
+}
+
+
+// special internal entry to add the 1st element
+PLINEARLISTENTRY CLinearList::add0 (void *pObject)
+{
+    // allocate new entry
+    PLINEARLISTENTRY pLLENew = (PLINEARLISTENTRY)malloc(sizeof(LINEARLISTENTRY));
+    if (NULL == pLLENew)
+        // signal failure
+        return NULL;
+
+    // setup object
+    pLLENew->pObject = pObject;
+    pLLENew->pPrev = NULL;
+    pLLENew->pNext = NULL;
+
+    pFirst = pLLENew;
+    pLast  = pLLENew;
+
+    // count items
+    iSize = 1;
+
+    return pLLENew;
+}
+
+
+
+PLINEARLISTENTRY CLinearList::addBefore (PLINEARLISTENTRY pLLE, void *pObject)
+{
+    // allocate new entry
+    PLINEARLISTENTRY pLLENew = (PLINEARLISTENTRY)malloc(sizeof(LINEARLISTENTRY));
+    if (NULL == pLLENew)
+        // signal failure
+        return NULL;
+
+    // setup object
+    pLLENew->pObject = pObject;
+    pLLENew->pPrev = pLLE->pPrev;
+    pLLENew->pNext = pLLE;
+    pLLE->pPrev = pLLENew;
+
+    // establish linking, append to end of list
+    if (pFirst == pLLE)
+        pFirst = pLLENew;
+
+    // count items
+    iSize++;
+
+    return pLLENew;
+}
+
+
+PLINEARLISTENTRY CLinearList::addAfter(PLINEARLISTENTRY pLLE, void *pObject)
+{
+    // allocate new entry
+    PLINEARLISTENTRY pLLENew = (PLINEARLISTENTRY)malloc(sizeof(LINEARLISTENTRY));
+    if (NULL == pLLENew)
+        // signal failure
+        return NULL;
+
+    // setup object
+    pLLENew->pObject = pObject;
+    pLLENew->pNext = pLLE->pNext;
+    pLLENew->pPrev = pLLE;
+    pLLE->pNext = pLLENew;
+
+    // establish linking, append to end of list
+    if (pLast  == pLLE)
+        pLast  = pLLENew;
+
+    // count items
+    iSize++;
+
+    return pLLENew;
+}
+
+
+
+PLINEARLISTENTRY CLinearList::findForward(void *pObject)
+{
+    return findForward(pFirst, pObject);
+}
+
+
+PLINEARLISTENTRY CLinearList::findBackward(void *pObject)
+{
+    return findBackward(pLast, pObject);
+}
+
+
+PLINEARLISTENTRY CLinearList::findForward(PLINEARLISTENTRY pLLECurrent,
+                                          void *pObject)
+{
+    while(pLLECurrent)
+    {
+        // check current item
+        if (pLLECurrent->pObject == pObject)
+            return pLLECurrent;
+
+        pLLECurrent = pLLECurrent->pNext;
+    }
+
+    // signal not found
+    return NULL;
+}
+
+
+PLINEARLISTENTRY CLinearList::findBackward(PLINEARLISTENTRY pLLECurrent,
+                                           void *pObject)
+{
+    while(pLLECurrent)
+    {
+        // check current item
+        if (pLLECurrent->pObject == pObject)
+            return pLLECurrent;
+
+        pLLECurrent = pLLECurrent->pPrev;
+    }
+
+    // signal not found
+    return NULL;
+}
+
+
+void CLinearList::removeElement(PLINEARLISTENTRY pLLE)
+{
+    // check if we're removing from the head
+    if (pLLE == pFirst)
+        pFirst = pLLE->pNext;
+
+    // check if we're removing from the tail
+    if (pLLE == pLast)
+        pLast = pLLE->pPrev;
+
+    // remove from chain
+    if (pLLE->pPrev != NULL)
+        pLLE->pPrev->pNext = pLLE->pNext;
+
+    if (pLLE->pNext != NULL)
+        pLLE->pNext->pPrev = pLLE->pPrev;
+
+    // free memory of the pLLE
+    free(pLLE);
+
+    // count elements
+    iSize--;
+}
+
+
+int CLinearList::removeElement(void* pObject)
+{
+    PLINEARLISTENTRY pLLE = findForward(pObject);
+    if (pLLE == NULL)
+        // signal not found
+        return 0;
+    else
+    {
+        removeElement(pLLE);
+        return 1;
+    }
+}
+
+
+void CLinearList::clear()
+{
+    PLINEARLISTENTRY pLLE = pFirst;
+    PLINEARLISTENTRY pLLENext;
+
+    // Establish new head and tail pointers very quickly,
+    // since new items can be added already while the old ones
+    // are still being deleted without causing interference.
+    pFirst = NULL;
+    pLast = NULL;
+
+    while (pLLE)
+    {
+        pLLENext = pLLE->pNext;
+        free (pLLE);
+        pLLE = pLLENext;
+    }
+}
+
+
+CHashtableLookup::CHashtableLookup(int iInitialSize)
+  : CCollection(iInitialSize)
+{
+    // the parent constructor is expected to set iSize
+    // to iInitialSize
+
+    parrLists = new CLinearList* [iSize];
+    memset(parrLists,
+           0,
+           iSize * sizeof(CLinearList*) );
+
+    iElements = 0;
+}
+
+CHashtableLookup::~CHashtableLookup()
+{
+    // kill all the slot lists
+    for (int i = 0;
+         i < iSize;
+         i++)
+    {
+        // check if slot was occupied
+        if (parrLists[i] != NULL)
+        {
+            delete parrLists[i];
+            parrLists[i] = NULL;
+        }
+    }
+
+    iSize = 0;
+    iElements = 0;
+
+    delete [] parrLists;
+}
+
+
+PHASHTABLEENTRY CHashtableLookup::addElement(char *pszName, void *pObject)
+{
+    // get slot number
+    unsigned long ulHash = hashcode(iSize, pszName);
+
+    // create entry
+    PHASHTABLEENTRY pHTE = (PHASHTABLEENTRY)malloc(sizeof(HASHTABLEENTRY));
+    pHTE->pszName = pszName;
+    pHTE->pObject = pObject;
+
+    // check if slot has a list object already
+    if (parrLists[ulHash] == NULL)
+        parrLists[ulHash] = new CLinearList();
+
+    parrLists[ulHash]->addLast(pHTE);
+
+    // count allocated elements
+    iElements++;
+
+    return pHTE;
+}
+
+
+void* CHashtableLookup::removeElement(char *pszName)
+{
+    // get slot number
+    unsigned long ulHash = hashcode(iSize, pszName);
+
+    // check if slot is occupied
+    if (parrLists[ulHash] == NULL)
+        // signal not found
+        return NULL;
+
+    // search the list
+    PLINEARLISTENTRY pLLE = parrLists[ulHash]->getFirst();
+    if (pLLE == NULL)
+        // signal not found
+        return NULL;
+
+    // iterate over the list
+    while(pLLE)
+    {
+        PHASHTABLEENTRY pHTE = (PHASHTABLEENTRY)pLLE->pObject;
+
+        // quickly compare 1st character for equality
+        // before doing the strcmp call
+        if (*pHTE->pszName == *pszName)
+            if (strcmp(pHTE->pszName, pszName) == 0)
+            {
+                // save old object pointer
+                void* pTemp = pHTE->pObject;
+                free(pHTE);
+
+                // found the correct entry
+                parrLists[ulHash]->removeElement(pLLE);
+
+                // count allocated elements
+                iElements--;
+
+                // return old object pointer to signal success
+                return pTemp;
+            }
+
+        pLLE = pLLE->pNext;
+    }
+
+    // failed
+    return NULL;
+}
+
+
+void* CHashtableLookup::getElement(char *pszName)
+{
+    // get slot number
+    unsigned long ulHash = hashcode(iSize, pszName);
+
+    CLinearList *pLL = parrLists[ulHash];
+
+    // check if slot is occupied
+    if (pLL == NULL)
+        // signal not found
+        return NULL;
+
+    // search the list
+    PLINEARLISTENTRY pLLE = pLL->getFirst();
+    if (pLLE == NULL)
+        // signal not found
+        return NULL;
+
+    // even if this is the only element on the list,
+    // we cannot save the strcmp, since the pszName-key
+    // might still be different! (mismatch)
+
+    // iterate over the list
+    while(pLLE)
+    {
+        PHASHTABLEENTRY pHTE = (PHASHTABLEENTRY)pLLE->pObject;
+
+        // quickly compare 1st character for equality
+        // before doing the strcmp call
+        if (*pHTE->pszName == *pszName)
+            if (strcmp(pHTE->pszName, pszName) == 0)
+            {
+                // return result
+                return pHTE->pObject;
+            }
+
+        pLLE = pLLE->pNext;
+    }
+
+    // failed
+    return NULL;
+}
+
+
+void  CHashtableLookup::clear()
+{
+    // clear all the slot lists
+    for (int i = 0;
+         i < iSize;
+         i++)
+    {
+        // check if slot was occupied
+        if (parrLists[i] != NULL)
+            parrLists[i]->clear();
+    }
+
+    iSize = 0;
+    iElements = 0;
+}
+
+
+void  CHashtableLookup::rehash()
+{
+    // if there are less slots than elements,
+    // the blocking factor is expected to be high
+    setSize(iElements);
+}
+
+
+void  CHashtableLookup::setSize(int iNewSize)
+{
+    // determine number of allocated elements
+    // actually, we need the prime next to
+    // the given number.
+    if (iSize < iNewSize)
+        setSize0(nextPrime(iNewSize));
+}
+
+
+int CHashtableLookup::getElementMap(PHASHTABLEENTRY pBuffer)
+{
+  int iIndex = 0;
+  
+  // iterate over all registered entries and dump them to the buffer
+  // giving the caller direct access to the hashtable internals.
+  for (int i = 0;
+       i < iSize;
+       i++)
+  {
+    // check if slot was occupied
+    if (parrLists[i] != NULL)
+    {
+      // walk along any entry in that linear list
+      PLINEARLISTENTRY pLLE = parrLists[i]->getFirst();
+      
+      while (pLLE)
+      {
+        PHASHTABLEENTRY pHTE = (PHASHTABLEENTRY)pLLE->pObject;
+        memcpy(&pBuffer[iIndex], pHTE, sizeof( HASHTABLEENTRY ) );
+        iIndex++;
+        
+        pLLE = parrLists[i]->getNext(pLLE);
+      }
+    }
+  }
+  
+  // return number of elements copied
+  return iIndex;
+}
+
+
+void  CHashtableLookup::setSize0(int iNewSize)
+{
+    // check if rehashing is necessary at all
+    if (iSize == iNewSize)
+        return;
+
+    // save old array, allocate new array
+    CLinearList** parrNew = new CLinearList* [iNewSize];
+    memset(parrNew,
+           0,
+           sizeof(CLinearList*) * iNewSize);
+
+    // convert all the slot lists
+    for (int i = 0;
+         i < iSize;
+         i++)
+    {
+        // check if slot was occupied
+        if (parrLists[i] != NULL)
+        {
+            // iterate over the slot
+            PLINEARLISTENTRY pLLE = parrLists[i]->getFirst();
+            PHASHTABLEENTRY pHTE;
+            unsigned long ulHash;
+
+            while(pLLE)
+            {
+                // calculate new hashcode for the entry
+                pHTE = (PHASHTABLEENTRY)pLLE->pObject;
+                ulHash = hashcode(iNewSize, pHTE->pszName);
+
+                // reinsert the pHTE into new slot
+                if (parrNew[ulHash] == NULL)
+                    parrNew[ulHash] = new CLinearList();
+
+                parrNew[ulHash]->addLast( (void*)pHTE );
+
+                pLLE=pLLE->pNext;
+            }
+
+            // kill the slot
+            delete parrLists[i];
+            parrLists[i] = NULL;
+        }
+    }
+
+    // swap the tables
+    iSize = iNewSize;
+    delete [] parrLists;
+
+    parrLists = parrNew;
+}
+
+
+unsigned long CHashtableLookup::nextPrime(unsigned long x)
+{
+   // Return next prime number after x, unless x is a prime in which case
+   // x is returned.
+
+   if (x < 2)
+       return 2;
+
+   if (x == 3)
+      return 3;
+
+   if (x % 2 == 0)
+      x++;
+
+   unsigned long sqr = (unsigned long) sqrt((double)x) + 1;
+
+   for (;;)
+   {
+       unsigned long n;
+
+       for (n = 3;
+            (n <= sqr) && ((x % n) != 0);
+            n += 2)
+           ;
+
+      if (n > sqr)
+          return x;
+
+      x += 2;
+   }
+}
+
+
+/*
+ * The following code provides classes for special purposes, whereas
+ * the above code is meant for general purposes. The above classes provide
+ * excellent performance when looking up entries, whereas they're rather
+ * slow when adding new elements to the collections.
+ *
+ * For the PELDR, it's more critical to have exceptionally fast adding
+ * of the exports to the lists as this outweighs resolving imports.
+ */
Index: trunk/src/odinprof/profcollection.h
===================================================================
--- trunk/src/odinprof/profcollection.h	(revision 7421)
+++ trunk/src/odinprof/profcollection.h	(revision 7421)
@@ -0,0 +1,179 @@
+/* $Id: profcollection.h,v 1.1 2001-11-22 10:43:59 phaller Exp $ */
+
+/*
+ * Collection class:
+ *   provides very fast object lookup by index number
+ *
+ * Copyright 2001 Patrick Haller <patrick.haller@innotek.de>
+ *
+ *
+ * Project Odin Software License can be found in LICENSE.TXT
+ *
+ *
+ */
+
+
+#ifndef _CCOLLECTION_H_
+#define _CCOLLECTION_H_
+
+
+#include <string.h>
+#include <malloc.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <math.h>
+#include <odin.h>                       /* Watcom needs max/min... */
+
+class CCollection
+{
+    protected:
+        int iSize; /* current size of the structure in elements */
+
+    public:
+        CCollection(int iInitialSize = 0);
+        ~CCollection();
+
+        virtual void clear() = 0; // remove all elements from the table
+                int  getSize() { return iSize; } // query size of structure
+};
+
+
+typedef struct
+{
+    void* pObject;
+} INDEXLOOKUPENTRY, *PINDEXLOOKUPENTRY;
+
+
+class CIndexLookup : public CCollection
+{
+    public:
+        CIndexLookup(int iInitialSize = 0);
+        ~CIndexLookup();
+
+        void* addElement(int iIndex, void *pObject);
+        void* removeElement(int iIndex);
+        void* getElement(int iIndex);
+        int   isValidIndex(int iIndex);
+        void  clear();
+        int   shrink();
+
+    protected:
+
+        /* ensure the specified index can be held in the table */
+        virtual int ensureCapacity(int iIndex);
+        PINDEXLOOKUPENTRY reallocateData(int iShift, int iRequiredSize);
+
+        /* pointer to array with data entries */
+        PINDEXLOOKUPENTRY pEntries;
+
+        /* offset is subtracted from any index into the table */
+        /* this also is the lowest valid index in the table */
+        int iOffset;
+        int iUsedOffset;
+
+        /* this is the highest valid index in the table */
+        int iHigh;
+        int iUsedHigh;
+
+        /* are the iUsedOffset and iUserHigh initialized? */
+        int iInitialized;
+};
+
+
+class CIndexLookupLimit : public CIndexLookup
+{
+    public:
+        CIndexLookupLimit(int iHardLimitLow = 0,
+                          int iHardLimitHigh = 0);
+        ~CIndexLookupLimit();
+
+
+    protected:
+
+        /* ensure the specified index can be held in the table */
+        int ensureCapacity(int iIndex);
+
+        /* hard limits for the array */
+        int iLimitLow;
+        int iLimitHigh;
+};
+
+
+typedef struct tagLinearListEntry
+{
+    struct tagLinearListEntry *pNext;
+    struct tagLinearListEntry *pPrev;
+
+    void *pObject;
+} LINEARLISTENTRY, *PLINEARLISTENTRY;
+
+
+class CLinearList : public CCollection
+{
+    public:
+        CLinearList();
+        ~CLinearList();
+
+        PLINEARLISTENTRY addFirst  (void *pObject);
+        PLINEARLISTENTRY addLast   (void *pObject);
+        PLINEARLISTENTRY addBefore (PLINEARLISTENTRY pLLE, void *pObject);
+        PLINEARLISTENTRY addAfter  (PLINEARLISTENTRY pLLE, void *pObject);
+        void             removeElement(PLINEARLISTENTRY pLLE);
+        int              removeElement(void *pObject);
+        PLINEARLISTENTRY findForward(void *pObject);
+        PLINEARLISTENTRY findForward(PLINEARLISTENTRY pLLECurrent,
+                                     void *pObject);
+        PLINEARLISTENTRY findBackward(void *pObject);
+        PLINEARLISTENTRY findBackward(PLINEARLISTENTRY pLLECurrent,
+                                      void *pObject);
+        PLINEARLISTENTRY getFirst(void) { return pFirst; }
+        PLINEARLISTENTRY getLast(void)  { return pLast;  }
+        PLINEARLISTENTRY getNext(PLINEARLISTENTRY pCurrent)
+                                        { return pCurrent->pNext; }
+        PLINEARLISTENTRY getPrevious(PLINEARLISTENTRY pCurrent)
+                                        { return pCurrent->pPrev; }
+        void             clear();
+
+    protected:
+        PLINEARLISTENTRY add0      (void *pObject);
+
+        PLINEARLISTENTRY pFirst;
+        PLINEARLISTENTRY pLast;
+};
+
+
+typedef struct
+{
+    char* pszName;
+    void* pObject;
+} HASHTABLEENTRY, *PHASHTABLEENTRY;
+
+
+class CHashtableLookup : public CCollection
+{
+    public:
+        CHashtableLookup(int iInitialSize = 23);
+        ~CHashtableLookup();
+
+        PHASHTABLEENTRY addElement(char *pszName, void *pObject);
+        void* removeElement(char *pszName);
+        void* getElement(char *pszName);
+        void  clear();
+        void  rehash();
+        void  setSize(int iNewSize);
+        int   getElementMap(PHASHTABLEENTRY pBuffer);
+        int   getNumberOfElements() { return iElements; }
+  
+    protected:
+        void          setSize0(int iNewSize);
+        unsigned long nextPrime(unsigned long x);
+
+        // the array of linear lists
+        CLinearList** parrLists;
+
+        // actual number of allocated elements
+        int iElements;
+};
+
+
+#endif /* _CCOLLECTION_H_ */
Index: trunk/src/odinprof/profiler.cpp
===================================================================
--- trunk/src/odinprof/profiler.cpp	(revision 7421)
+++ trunk/src/odinprof/profiler.cpp	(revision 7421)
@@ -0,0 +1,818 @@
+/* $Id: profiler.cpp,v 1.1 2001-11-22 10:43:59 phaller Exp $ */
+/*
+ * Project Odin Software License can be found in LICENSE.TXT
+ * Execution Trace Profiler
+ *
+ * Copyright 2001 Patrick Haller <patrick.haller@innotek.de>
+ *
+ */
+
+
+/****************************************************************************
+ * To Do
+ ****************************************************************************
+ 
+ - write result in java.prof style
+ - find tool to nicely work on the output files
+ - fix wrong timestamps (use 64-bit arithmetic and recalculate to ms)
+ */
+
+
+/****************************************************************************
+ * includes
+ ****************************************************************************/
+
+#define INCL_DOSERRORS
+#define INCL_DOSPROCESS
+#include <os2.h>
+
+#include <os2sel.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+#include <direct.h>
+
+#include "symfile.h"
+
+
+/****************************************************************************
+ * local class definitions
+ ****************************************************************************/
+
+
+typedef struct
+{
+    ULONG ulID;
+    void* pObject;
+} HASHTABLEENTRYID, *PHASHTABLEENTRYID;
+
+
+class CProfilerHashtableLookup : public CHashtableLookup
+{
+  public:
+        CProfilerHashtableLookup(int iInitialSize);
+        PHASHTABLEENTRYID addElement(ULONG ulID, void *pObject);
+        void* removeElement(ULONG ulID);
+        void* getElement(ULONG ulID);
+        int   getElementMap(PHASHTABLEENTRYID pBuffer);
+
+  protected:
+        void  setSize0(int iNewSize);
+};
+
+
+/****************************************************************************
+ * definitions
+ ****************************************************************************/
+
+typedef struct tagThreadProfileEntry
+{
+  unsigned long      EIP;          /* currently only 32-bit segments are traced */
+  char*              pszModule;                   /* name of the current module */
+                        /* name of the current function or (caller;callee pair) */
+  char*              pszFunction;
+  unsigned long      ulCalls;
+  unsigned long      ulTimeMinimum;
+  unsigned long      ulTimeMaximum;
+  unsigned long      ulTimeTotal;
+} PROFILEENTRY, *PPROFILEENTRY;
+
+
+typedef struct tagThreadProfilerDataBlock
+{
+  unsigned long ulCalldepth;         // current call depth
+  unsigned long ret[384];            // last saved return address
+  unsigned long callStack[384];      // call stack (CS:) EIP records
+  unsigned long enterTimeHi[384];    // machine timestamps of function entry
+  unsigned long enterTimeLo[384];
+  
+  // Hashtable to record the PROFILEENTRY records
+  CProfilerHashtableLookup* pHashtable;
+  
+} PROFILERBLOCK, *PPROFILERBLOCK;
+
+
+/****************************************************************************
+ * module local variables
+ ****************************************************************************/
+
+static SymbolFilePool*           pSymPool    = new SymbolFilePool();
+static CProfilerHashtableLookup* pProfileMap = new CProfilerHashtableLookup(1021);
+static PPROFILERBLOCK* ppTLS              = 0;      /* thread local storage */
+static BOOL            fIsProfilerEnabled = FALSE;  /* enable / disable hook*/
+static CHAR            szWorkingDirectory[260] = ".";
+
+extern void _System odin_ProfileHook32Bottom();
+extern void _System _ProfileGetTimestamp(PULONG hi, PULONG lo);
+
+
+/****************************************************************************
+ * local class definitions
+ ****************************************************************************/
+
+static inline unsigned long _Optlink hashcodeEIP(int iRing, ULONG eip)
+{
+  return eip % iRing;
+}
+
+
+CProfilerHashtableLookup::CProfilerHashtableLookup(int iInitialSize)
+  : CHashtableLookup(iInitialSize)
+{
+}
+
+
+PHASHTABLEENTRYID CProfilerHashtableLookup::addElement(ULONG ulID, void *pObject)
+{
+    // get slot number
+    unsigned long ulHash = hashcodeEIP(iSize, ulID);
+
+    // create entry
+    PHASHTABLEENTRYID pHTE = (PHASHTABLEENTRYID)malloc(sizeof(HASHTABLEENTRYID));
+    pHTE->ulID    = ulID;
+    pHTE->pObject = pObject;
+
+    // check if slot has a list object already
+    if (parrLists[ulHash] == NULL)
+        parrLists[ulHash] = new CLinearList();
+
+    parrLists[ulHash]->addLast(pHTE);
+
+    // count allocated elements
+    iElements++;
+
+    return pHTE;
+}
+
+
+void* CProfilerHashtableLookup::removeElement(ULONG ulID)
+{
+    // get slot number
+    unsigned long ulHash = hashcodeEIP(iSize, ulID);
+
+    // check if slot is occupied
+    if (parrLists[ulHash] == NULL)
+        // signal not found
+        return NULL;
+
+    // search the list
+    PLINEARLISTENTRY pLLE = parrLists[ulHash]->getFirst();
+    if (pLLE == NULL)
+        // signal not found
+        return NULL;
+
+    // iterate over the list
+    while(pLLE)
+    {
+        PHASHTABLEENTRYID pHTE = (PHASHTABLEENTRYID)pLLE->pObject;
+
+        if (pHTE->ulID == ulID)
+        {
+            // save old object pointer
+            void* pTemp = pHTE->pObject;
+            free(pHTE);
+
+            // found the correct entry
+            parrLists[ulHash]->removeElement(pLLE);
+
+            // count allocated elements
+            iElements--;
+
+            // return old object pointer to signal success
+            return pTemp;
+        }
+
+        pLLE = pLLE->pNext;
+    }
+
+    // failed
+    return NULL;
+}
+
+
+void* CProfilerHashtableLookup::getElement(ULONG ulID)
+{
+    // get slot number
+    unsigned long ulHash = hashcodeEIP(iSize, ulID);
+
+    CLinearList *pLL = parrLists[ulHash];
+
+    // check if slot is occupied
+    if (pLL == NULL)
+        // signal not found
+        return NULL;
+
+    // search the list
+    PLINEARLISTENTRY pLLE = pLL->getFirst();
+    if (pLLE == NULL)
+        // signal not found
+        return NULL;
+
+    // iterate over the list
+    while(pLLE)
+    {
+        PHASHTABLEENTRYID pHTE = (PHASHTABLEENTRYID)pLLE->pObject;
+
+        // quickly compare 1st character for equality
+        // before doing the strcmp call
+        if (pHTE->ulID == ulID)
+        {
+            // return result
+            return pHTE->pObject;
+        }
+
+        pLLE = pLLE->pNext;
+    }
+
+    // failed
+    return NULL;
+}
+
+
+int CProfilerHashtableLookup::getElementMap(PHASHTABLEENTRYID pBuffer)
+{
+  int iIndex = 0;
+  
+  // iterate over all registered entries and dump them to the buffer
+  // giving the caller direct access to the hashtable internals.
+  for (int i = 0;
+       i < iSize;
+       i++)
+  {
+    // check if slot was occupied
+    if (parrLists[i] != NULL)
+    {
+      // walk along any entry in that linear list
+      PLINEARLISTENTRY pLLE = parrLists[i]->getFirst();
+      
+      while (pLLE)
+      {
+        PHASHTABLEENTRYID pHTE = (PHASHTABLEENTRYID)pLLE->pObject;
+        memcpy(&pBuffer[iIndex], pHTE, sizeof( HASHTABLEENTRYID ) );
+        iIndex++;
+        
+        pLLE = parrLists[i]->getNext(pLLE);
+      }
+    }
+  }
+  
+  // return number of elements copied
+  return iIndex;
+}
+
+
+void  CProfilerHashtableLookup::setSize0(int iNewSize)
+{
+    // check if rehashing is necessary at all
+    if (iSize == iNewSize)
+        return;
+
+    // save old array, allocate new array
+    CLinearList** parrNew = new CLinearList* [iNewSize];
+    memset(parrNew,
+           0,
+           sizeof(CLinearList*) * iNewSize);
+
+    // convert all the slot lists
+    for (int i = 0;
+         i < iSize;
+         i++)
+    {
+        // check if slot was occupied
+        if (parrLists[i] != NULL)
+        {
+            // iterate over the slot
+            PLINEARLISTENTRY pLLE = parrLists[i]->getFirst();
+            PHASHTABLEENTRYID pHTE;
+            unsigned long ulHash;
+
+            while(pLLE)
+            {
+                // calculate new hashcode for the entry
+                pHTE = (PHASHTABLEENTRYID)pLLE->pObject;
+                ulHash = hashcodeEIP(iNewSize, pHTE->ulID);
+
+                // reinsert the pHTE into new slot
+                if (parrNew[ulHash] == NULL)
+                    parrNew[ulHash] = new CLinearList();
+
+                parrNew[ulHash]->addLast( (void*)pHTE );
+
+                pLLE=pLLE->pNext;
+            }
+
+            // kill the slot
+            delete parrLists[i];
+            parrLists[i] = NULL;
+        }
+    }
+
+    // swap the tables
+    iSize = iNewSize;
+    delete [] parrLists;
+
+    parrLists = parrNew;
+}
+
+
+/****************************************************************************
+ * implementation
+ ****************************************************************************/
+ 
+static PPROFILERBLOCK inline getProfilerBlock(void)
+{
+  if ( ( (ULONG)ppTLS != 0) && ( (ULONG)ppTLS != 0xffffffff) )
+    return *ppTLS;
+  
+  // is TLS already available?
+  if (ppTLS == NULL)
+  {
+    // allocate thread local storage
+    USHORT sel = RestoreOS2FS();
+    APIRET rc = DosAllocThreadLocalMemory(1, (PULONG*)&ppTLS);
+    SetFS(sel);
+    
+    if (rc != NO_ERROR)
+      ppTLS = (PPROFILERBLOCK*)0xffffffff; // mark as invalid
+  }
+  
+  if ((ULONG)ppTLS == 0xffffffff)
+    // profiling is not available
+    return NULL;
+
+  // allocate data structure
+  {
+    USHORT sel = RestoreOS2FS();
+    
+    // Note: must be DosAllocMem because the CRT would automatically close
+    // all heap objects on termination
+    //    *ppTLS = (PPROFILERBLOCK)malloc( sizeof(PROFILERBLOCK) );
+    int iSize = 4096 * (1 + sizeof(PROFILERBLOCK) / 4096);
+    APIRET rc = DosAllocMem((PPVOID)ppTLS, iSize, PAG_COMMIT | PAG_READ | PAG_WRITE);
+    SetFS(sel);
+    
+    if (rc == NO_ERROR)
+    {
+      memset(*ppTLS, 0, sizeof(PROFILERBLOCK) );
+      
+      PPROFILERBLOCK pPB = (PPROFILERBLOCK)*ppTLS;
+      
+      // experiment: thread-local profile
+      // pPB->pHashtable = new CProfilerHashtableLookup(1021);
+      pPB->pHashtable = pProfileMap;
+    }
+  }
+  
+  return (PPROFILERBLOCK)*ppTLS;
+}
+
+
+
+/***********************************************************************
+* Name      : APIRET QueryModuleFromEIP
+* Funktion  : Determine which module resides at 0:EIP
+* Parameter :
+* Variablen :
+* Ergebnis  : Rckgabewert ans Betriebssystem
+* Bemerkung :
+*
+* Autor     : Patrick Haller [Samstag, 28.10.1995 14.55.13]
+***********************************************************************/
+
+APIRET APIENTRY DosQueryModFromEIP (PULONG pulModule,
+                                    PULONG pulObject,
+                                    ULONG  ulBufferLength,
+                                    PSZ    pszBuffer,
+                                    PULONG pulOffset,
+                                    ULONG  ulEIP);
+
+static void i_ProfileQuerySymbol(ULONG eip_function,
+                                 char **ppszModule,
+                                 char **ppszFunction)
+{
+  APIRET rc;                                               /* API returncode */
+  ULONG  ulModule;                                          /* module number */
+  ULONG  ulObject;                        /* object number within the module */
+  CHAR   szModule[260];                        /* buffer for the module name */
+  ULONG  ulOffset;             /* offset within the object within the module */
+  
+  rc = DosQueryModFromEIP(&ulModule,
+                          &ulObject,
+                          sizeof(szModule),
+                          szModule,
+                          &ulOffset,
+                          eip_function);
+  if (rc == NO_ERROR)
+  {
+    CHAR szFunction[128];
+    *ppszModule = strdup(szModule);
+    
+    // @@@PH replace by symbol file lookup
+    if (FALSE == pSymPool->getSymbolName(szModule,
+                                         ulObject,
+                                         ulOffset,
+                                         szFunction,
+                                         sizeof(szFunction)) )
+    {
+      sprintf(szFunction,
+              "func_#%08xh",
+              eip_function);
+    }
+    *ppszFunction = strdup(szFunction);
+  }
+  else
+  {
+    // neither module name nor function could be determined, so
+    // we can just provide these values here.
+    CHAR szFunction[40];
+    *ppszModule = "<unknown module>";
+    sprintf(szFunction,
+            "func_#%08xh",
+            eip_function);
+    *ppszFunction = strdup(szFunction);
+  }
+}
+
+
+
+//
+// @@@PH
+// Note: prevent compiler from generating hooks in these functions here!
+// -> is there any pragma to prevent this?
+// -> alternative: place it in the makefile accordingly w/ "/Gh-"
+
+ULONG _System C_ProfileHook32Enter(ULONG eip_function, ULONG ret, ULONG time_hi, ULONG time_lo)
+{
+  if (!fIsProfilerEnabled)
+    // tell low level procedure not to replace the return address
+    // so bottom half will never be called
+    return ret;
+  
+  // "call _ProfileHook32" has 5 bytes, adjust here
+  eip_function -= 5;
+  
+  // 1 - get profiler per-thread-data-block
+  PPROFILERBLOCK pPB = getProfilerBlock();
+  if (NULL == pPB)
+    // tell low level procedure not to replace the return address
+    // so bottom half will never be called
+    return ret;
+  
+  // 2 - save return address and call stack entry
+  int i               = pPB->ulCalldepth;
+  pPB->ret[i]         = ret;
+  pPB->callStack[i]   = eip_function;
+  
+  // 3 - check if the database has an entry with the current EIP
+  // @@@PH need to synchronize access!
+  PPROFILEENTRY pPE = (PPROFILEENTRY) pPB->pHashtable->getElement(eip_function);
+  
+  // 3.1 - if not, create new entry
+  if (NULL == pPE)
+  {
+    USHORT sel = RestoreOS2FS();
+    
+    pPE = (PPROFILEENTRY)malloc( sizeof(PROFILEENTRY) );
+    
+    i_ProfileQuerySymbol(eip_function,
+                         &pPE->pszModule,
+                         &pPE->pszFunction);
+    
+    pPE->EIP           = eip_function;
+    // pPE->pszModule     = NULL; // @@@PH
+    // pPE->pszFunction   = NULL; // @@@PH
+    pPE->ulCalls       = 0;
+    pPE->ulTimeMaximum = 0;
+    pPE->ulTimeMinimum = 0xffffffff;
+    pPE->ulTimeTotal   = 0;
+    
+    // add to the hashtable
+    // @@@PH need to synchronize access!
+    pPB->pHashtable->addElement(eip_function, pPE);
+    
+    SetFS(sel);
+  }
+  
+  // update available statistical data
+  pPE->ulCalls++;
+  
+  // register call and skip to the next empty slot
+  pPB->ulCalldepth++;
+  
+  // get "compensated" timestamp
+  // -> keep the time between entering the function and
+  // exiting it as low as possible, yet the error will definately
+  // accumulate for the "higher" callers
+  _ProfileGetTimestamp(&pPB->enterTimeHi[i],
+                       &pPB->enterTimeLo[i]);
+  
+  // tell low level procedure to replace the return address
+  return (ULONG)&odin_ProfileHook32Bottom;
+}
+
+
+ULONG _System C_ProfileHook32Exit(ULONG time_hi, ULONG time_lo)
+{
+  // 1 - get profiler per-thread-data-block
+  PPROFILERBLOCK pPB = getProfilerBlock();
+  if (NULL == pPB)
+    // Oops, we're now in deep trouble! The return address will not be
+    // available, so we're definately gonna crash!
+    return NULL;
+  
+  // register call exit
+  pPB->ulCalldepth--;
+  
+  // 3.2 - add gathered statistical values to the entry
+  int   i = pPB->ulCalldepth;
+  ULONG eip_function = pPB->callStack[i];
+  PPROFILEENTRY pPE = (PPROFILEENTRY) pPB->pHashtable->getElement(eip_function);
+  if (pPE)
+  {
+    // Note:
+    // we don't take the values for the 1st call since all the lookup values
+    // are virtually non-deterministic and cannot be compensated for.
+    if (pPE->ulCalls > 1)
+    {
+      ULONG ulDiffHi = time_hi - pPB->enterTimeHi[i];
+      ULONG ulDiffLo = time_lo - pPB->enterTimeLo[i];
+      
+      // test for wrap around
+      if (ulDiffHi)
+        ulDiffLo = ~ulDiffLo;
+      
+      // timestamps are in "cpu cycles"
+      // ULONG ulTime = (ulDiffHi >> 4) | (ulDiffLo << 28);
+      ULONG ulTime = ulDiffLo;
+      
+      if (pPE->ulTimeMinimum > ulTime)
+        pPE->ulTimeMinimum = ulTime;
+      
+      if (pPE->ulTimeMaximum < ulTime)
+        pPE->ulTimeMaximum = ulTime;
+      
+      pPE->ulTimeTotal += ulTime;
+    }
+  }
+
+  
+  // 2 - return saved return address
+  return pPB->ret[pPB->ulCalldepth];
+}
+
+
+// query if the profiler is enabled
+BOOL _System ProfilerIsEnabled(void)
+{
+  return fIsProfilerEnabled;
+}
+
+
+// enable / disable the profiler hook
+void _System ProfilerEnable(BOOL fState)
+{
+  fIsProfilerEnabled = fState;
+}
+
+
+// initialize the profiler (i. e. working directory)
+void _System ProfilerInitialize(void)
+{
+  USHORT sel = RestoreOS2FS();
+  
+  // determine where to write the profile data to
+  // (get the current working directory)
+  getcwd(szWorkingDirectory, sizeof(szWorkingDirectory) );
+  
+  // @@@PH
+  // try to determine the measurement overhead
+  // (difficult due to non-deterministic behaviour
+  // of the hashtable lookups and dynamic symbol loading, etc.)
+  // We'd have to split the hook function into three parts:
+  // - lookup and prepare the call
+  // - enter call
+  // - exit call
+  // This however, still leaves us with wrong timing for the parent functions.
+  
+  SetFS(sel);
+}
+
+
+// terminate the profiler
+void _System ProfilerTerminate(void)
+{
+  USHORT sel = RestoreOS2FS();
+  
+  // disable the profiler
+  ProfilerEnable(FALSE);
+  
+  SetFS(sel);
+}
+
+
+int _Optlink sortHashtableEntries0(const void *arg1,const void *arg2)
+{
+  PHASHTABLEENTRYID pHTE1 = (PHASHTABLEENTRYID)arg1;
+  PHASHTABLEENTRYID pHTE2 = (PHASHTABLEENTRYID)arg2;
+  
+  PPROFILEENTRY p1 = (PPROFILEENTRY)pHTE1->pObject;
+  PPROFILEENTRY p2 = (PPROFILEENTRY)pHTE2->pObject;
+  
+  return strcmp(p1->pszFunction, p2->pszFunction);
+}
+
+int _Optlink sortHashtableEntries1(const void *arg1,const void *arg2)
+{
+  PHASHTABLEENTRYID pHTE1 = (PHASHTABLEENTRYID)arg1;
+  PHASHTABLEENTRYID pHTE2 = (PHASHTABLEENTRYID)arg2;
+  
+  PPROFILEENTRY p1 = (PPROFILEENTRY)pHTE1->pObject;
+  PPROFILEENTRY p2 = (PPROFILEENTRY)pHTE2->pObject;
+  
+  return p1->ulTimeTotal - p2->ulTimeTotal;
+}
+
+int _Optlink sortHashtableEntries2(const void *arg1,const void *arg2)
+{
+  PHASHTABLEENTRYID pHTE1 = (PHASHTABLEENTRYID)arg1;
+  PHASHTABLEENTRYID pHTE2 = (PHASHTABLEENTRYID)arg2;
+  
+  PPROFILEENTRY p1 = (PPROFILEENTRY)pHTE1->pObject;
+  PPROFILEENTRY p2 = (PPROFILEENTRY)pHTE2->pObject;
+  
+  return p1->ulCalls - p2->ulCalls;
+}
+
+int _Optlink sortHashtableEntries3(const void *arg1,const void *arg2)
+{
+  PHASHTABLEENTRYID pHTE1 = (PHASHTABLEENTRYID)arg1;
+  PHASHTABLEENTRYID pHTE2 = (PHASHTABLEENTRYID)arg2;
+  
+  PPROFILEENTRY p1 = (PPROFILEENTRY)pHTE1->pObject;
+  PPROFILEENTRY p2 = (PPROFILEENTRY)pHTE2->pObject;
+  
+  unsigned long int iAvg1 = p1->ulTimeTotal / p1->ulCalls;
+  unsigned long int iAvg2 = p2->ulTimeTotal / p2->ulCalls;
+  
+  return iAvg1 - iAvg2;
+}
+
+
+// dump the collected profile to the specified file
+void _Optlink Profiler_DumpProfile(FILE *file)
+{
+  fprintf(file,
+          "ODIN Performance Analysis\n");
+  
+  BOOL flagLock = ProfilerIsEnabled();  // lock recording
+  ProfilerEnable(FALSE);
+  
+  int iEntries = pProfileMap->getNumberOfElements();
+  
+  // get a list of all entries of the hashtable
+  PHASHTABLEENTRYID arrEntries = (PHASHTABLEENTRYID)malloc( iEntries * sizeof(HASHTABLEENTRYID) );
+  iEntries = pProfileMap->getElementMap(arrEntries);
+  
+  fprintf(file,
+          "%d entries available.\n\n",
+          iEntries);
+  
+  // sort the list by function name
+  qsort(arrEntries,
+        iEntries,
+        sizeof( HASHTABLEENTRYID ),
+        sortHashtableEntries0);
+  
+  // write to file
+  fprintf(file,
+          "Sorted by function name\n"
+          "Ticks ---- Called --- Average -- Minimum -- Maximum -- Function -----------\n");
+  for(int i = 0;
+      i < iEntries;
+      i++)
+  {
+    PPROFILEENTRY p = (PPROFILEENTRY)arrEntries[i].pObject;
+    if (p->ulCalls > 1)
+    fprintf(file,
+            "%10d %10d %10d %10d %10d %s(%s)\n",
+            p->ulTimeTotal,
+            p->ulCalls,
+            p->ulTimeTotal / p->ulCalls,
+            p->ulTimeMinimum,
+            p->ulTimeMaximum,
+            p->pszFunction,
+            p->pszModule);
+  }
+  
+  
+  // sort the list by ulTimeTotal
+  qsort(arrEntries,
+        iEntries,
+        sizeof( HASHTABLEENTRYID ),
+        sortHashtableEntries1);
+  
+  // write to file
+  fprintf(file,
+          "\nSorted by total call time\n"
+          "Ticks ---- Called --- Average -- Function ---------------------------------\n");
+  for(i = 0;
+      i < iEntries;
+      i++)
+  {
+    PPROFILEENTRY p = (PPROFILEENTRY)arrEntries[i].pObject;
+    if (p->ulCalls > 1)
+    fprintf(file,
+            "%10d %10d %10d %s(%s)\n",
+            p->ulTimeTotal,
+            p->ulCalls,
+            p->ulTimeTotal / p->ulCalls,
+            p->pszFunction,
+            p->pszModule);
+  }
+  
+  
+  // sort the list by ulCalls
+  qsort(arrEntries,
+        iEntries,
+        sizeof( HASHTABLEENTRYID ),
+        sortHashtableEntries2);
+  
+  // write to file
+  fprintf(file,
+          "\nSorted by total calls\n"
+          "Called --- Ticks ---- Average -- Function ---------------------------------\n");
+  for(i = 0;
+      i < iEntries;
+      i++)
+  {
+    PPROFILEENTRY p = (PPROFILEENTRY)arrEntries[i].pObject;
+    if (p->ulCalls > 1)
+    fprintf(file,
+            "%10d %10d %10d %s(%s)\n",
+            p->ulCalls,
+            p->ulTimeTotal,
+            p->ulTimeTotal / p->ulCalls,
+            p->pszFunction,
+            p->pszModule);
+  }
+  
+  
+  // sort the list by average call time
+  qsort(arrEntries,
+        iEntries,
+        sizeof( HASHTABLEENTRYID ),
+        sortHashtableEntries3);
+  
+  // write to file
+  fprintf(file,
+          "\nSorted by average call time\n"
+          "Average -- Calls ---- Ticks ---- Function ---------------------------------\n");
+  for(i = 0;
+      i < iEntries;
+      i++)
+  {
+    PPROFILEENTRY p = (PPROFILEENTRY)arrEntries[i].pObject;
+    if (p->ulCalls > 1)
+    fprintf(file,
+            "%10d %10d %10d %s(%s)\n",
+            p->ulTimeTotal / p->ulCalls,
+            p->ulCalls,
+            p->ulTimeTotal,
+            p->pszFunction,
+            p->pszModule);
+  }
+  
+  
+  ProfilerEnable(flagLock);
+}
+
+
+void _System ProfilerWrite()
+{
+  FILE* _privateLogFile;
+  char szFilename[260];
+  USHORT sel = RestoreOS2FS();
+
+  sprintf(szFilename, "%s\\perf.log", szWorkingDirectory);
+  _privateLogFile = fopen(szFilename, "w");
+  
+  if(_privateLogFile == NULL) 
+  {
+    DosBeep(500,500);
+  }
+  else
+  {
+    // dump the gathered data
+    Profiler_DumpProfile(_privateLogFile);
+  
+    if(_privateLogFile)
+    {
+      fclose(_privateLogFile);
+      _privateLogFile = NULL;
+    }
+  }
+  
+  SetFS(sel);
+}
+
Index: trunk/src/odinprof/symfile.cpp
===================================================================
--- trunk/src/odinprof/symfile.cpp	(revision 7421)
+++ trunk/src/odinprof/symfile.cpp	(revision 7421)
@@ -0,0 +1,445 @@
+/* $Id: symfile.cpp,v 1.1 2001-11-22 10:44:00 phaller Exp $ */
+/*
+ * Project Odin Software License can be found in LICENSE.TXT
+ * Execution Trace Profiler
+ *
+ * Copyright 2001 Patrick Haller <patrick.haller@innotek.de>
+ *
+ */
+
+
+/****************************************************************************
+ * To Do
+ ****************************************************************************
+ 
+ - find and load symbolic debug information file
+   (SYM2IDA)
+   => verify against kernel32\exceptionstack.cpp
+   (KERNEL32.SYM is not interpreted correctly)
+ - try to demangle C++ names
+ - fix weird duplicate lookups of same symbol (i. e. SetBkColor)
+ */
+
+
+/****************************************************************************
+ * includes
+ ****************************************************************************/
+
+#define INCL_DOSERRORS
+#define INCL_DOSPROCESS
+#define INCL_DOSMISC
+#define INCL_DOSFILEMGR
+#include <os2.h>
+
+#include <os2sel.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <demangle.h>
+
+#include "profcollection.h"
+#include "symfile.h"
+
+
+/****************************************************************************
+ * implementation
+ ****************************************************************************/
+
+/*****************************************************************************
+ * Name      : APIRET ReadFileToBuffer
+ * Funktion  : Reads a single file completely into a memory buffer
+ * Parameter : PSZ pszFile, PPVOID ppBuffer, PULONG pulBuffer
+ * Variablen :
+ * Ergebnis  : API-Returncode
+ * Bemerkung :
+ *
+ * Autor     : Patrick Haller [Samstag, 07.10.1995 03.22.44]
+ *****************************************************************************/
+
+APIRET ToolsReadFileToBuffer (PSZ    pszFile,
+                              PPVOID ppBuffer,
+                              PULONG pulBuffer)
+{
+  APIRET      rc;                                          /* API-Returncode */
+  FILESTATUS3 fs3Status;                               /* Dateiinformationen */
+  HFILE       hFileInput;                               /* input file handle */
+  ULONG       ulAction;                       /* dummy parameter for DosOpen */
+  ULONG       ulReadSize;                /* number of bytes to read per call */
+  ULONG       ulReadTotal = 0;                 /* total number of bytes read */
+
+  if ( (pszFile   == NULL) ||                            /* check parameters */
+       (ppBuffer  == NULL) ||
+       (pulBuffer == NULL) )
+    return (ERROR_INVALID_PARAMETER);                        /* signal error */
+
+
+  rc = DosQueryPathInfo (pszFile,                          /* Infos einholen */
+                         FIL_STANDARD,
+                         &fs3Status,
+                         sizeof(fs3Status));
+  if (rc != NO_ERROR)                           /* check if an error occured */
+    return (rc);                                             /* signal error */
+
+                                         /* allocate buffer RAM for the file */
+  *pulBuffer = fs3Status.cbFile;              /* return the size of the file */
+  rc = DosAllocMem(ppBuffer,                          /* allocate the buffer */
+                   fs3Status.cbFile,
+                   PAG_WRITE |
+                   PAG_READ  |
+                   PAG_COMMIT);
+  if (rc != NO_ERROR)                      /* check if the allocation failed */
+    return (rc);                                             /* signal error */
+
+  ulReadSize = fs3Status.cbFile;
+
+  rc = DosOpen(pszFile,                                    /* File path name */
+               &hFileInput,                                   /* File handle */
+               &ulAction,                                    /* Action taken */
+               0L,                                /* File primary allocation */
+               FILE_ARCHIVED |
+               FILE_NORMAL,                                /* File attribute */
+               OPEN_ACTION_FAIL_IF_NEW |
+               OPEN_ACTION_OPEN_IF_EXISTS,             /* Open function type */
+               OPEN_FLAGS_NOINHERIT |
+               OPEN_FLAGS_SEQUENTIAL|
+               OPEN_SHARE_DENYNONE  |
+               OPEN_ACCESS_READONLY,                /* Open mode of the file */
+               0L);                                 /* No extended attribute */
+  if (rc == NO_ERROR)                                    /* check for errors */
+  {
+    ULONG ulRead;                                    /* number of read bytes */
+
+    rc = DosRead (hFileInput,
+                  *ppBuffer,
+                  ulReadSize,
+                  &ulRead);
+
+    DosClose (hFileInput);               /* close the filehandle in any case */
+  }
+
+  if (rc != NO_ERROR)                                    /* check for errors */
+  {
+    DosFreeMem (*ppBuffer);                     /* free the allocated memory */
+    *ppBuffer  = NULL;                       /* reset the passed back values */
+    *pulBuffer = 0;
+  }
+
+  return (rc);                                                /* return code */
+}
+
+
+
+
+LXSymbolFile::LXSymbolFile(PSZ _pszName, PSZ _pszFileName)
+{
+  pszName         = strdup(_pszName); // copy lookup name
+  pszFileName     = strdup(_pszFileName);
+  pszErrorMessage = NULL;
+}
+
+
+LXSymbolFile::~LXSymbolFile()
+{
+  if (pszName)
+    free(pszName);
+  
+  if (pszErrorMessage)
+    free(pszErrorMessage);
+  
+  if (pszFileName)
+    free(pszFileName);
+  
+  if (pSymbolRawData)
+    DosFreeMem (pSymbolRawData);
+}
+
+
+void LXSymbolFile::setErrorMessage(PSZ _pszErrorMessage)
+{
+  if (pszErrorMessage)
+    free(pszErrorMessage);
+  
+  if (NULL != _pszErrorMessage)
+    pszErrorMessage = strdup(_pszErrorMessage);
+  else
+    pszErrorMessage = NULL;
+}
+
+
+APIRET LXSymbolFile::parseFile()
+{
+  APIRET rc;
+  ULONG  ulBufferSize;
+  
+  // 1 - read file to buffer
+  rc = ToolsReadFileToBuffer(pszFileName,
+                             (PPVOID)&pSymbolRawData,
+                             &ulBufferSize);
+  if (NO_ERROR != rc)
+  {
+    setErrorMessage("unable to read file");
+    return rc;
+  }
+  
+  return NO_ERROR;
+}
+
+
+BOOL   LXSymbolFile::isAvailable()
+{
+  return pszErrorMessage == NULL;
+}
+
+
+PSZ    LXSymbolFile::getErrorMessage()
+{
+  return pszErrorMessage;
+}
+
+
+PSZ    LXSymbolFile::getFileName()
+{
+  return pszFileName;
+}
+
+
+BOOL   LXSymbolFile::getSymbolName(ULONG objNr,
+                                   ULONG offset,
+                                   PSZ   pszNameBuffer,
+                                   ULONG ulNameBufferLength)
+{
+  PUCHAR    p = (PUCHAR)pSymbolRawData;
+  ULONG     ulMapDefs;
+  PMAPDEF   pMapDef;
+  ULONG     SegOffset;
+  ULONG     SymOffset;
+  ULONG     ulSegNum;
+  ULONG     ulSymNum;
+  SEGDEF*   pSegDef;
+  SYMDEF32* pSymDef32;
+  
+  // scan through the maps
+  pMapDef = (PMAPDEF)p;
+  for (ulMapDefs = 0;
+       pMapDef->ppNextMap != 0;
+       ulMapDefs++)
+  {
+    SegOffset = pMapDef->ppSegDef * 16;
+    
+    for (ulSegNum = 0;
+         ulSegNum < pMapDef->cSegs;
+         ulSegNum++)
+    {
+      pSegDef = (PSEGDEF)(p + SegOffset);
+      
+      // check for the segment number
+      if (ulSegNum == objNr)
+      {
+        PSYMDEF32 pSymClosest = NULL;
+        
+        // lookup the symbol
+        for (ulSymNum = 0;
+             ulSymNum < pSegDef->cSymbols;
+             ulSymNum++)
+        {
+          /* calculate symbol offset */
+          PUCHAR pucTemp = ( (PUCHAR)pSegDef + 
+                            pSegDef->pSymDef +
+                            ulSymNum * sizeof(USHORT) );
+          SymOffset = *(PUSHORT)pucTemp;
+          pucTemp = (PUCHAR)pSegDef + SymOffset;
+          
+          if (pSegDef->bFlags & 0x01)
+          {
+            // 32-bit segment
+            pSymDef32 = (PSYMDEF32)pucTemp;
+            
+            if (NULL == pSymClosest)
+              pSymClosest = pSymDef32;
+            else
+            {
+              int iClosest = offset - pSymClosest->wSymVal;
+              int iCurrent = offset - pSymDef32->wSymVal;
+              
+              // find nearest symbol to the given offset
+              if (iCurrent > 0)
+                if (iClosest < iCurrent)
+                  pSymClosest = pSymDef32;
+            }
+            
+            // check if we've found it (or get the closest symbol)
+            if (offset == pSymDef32->wSymVal)
+            {
+              int iLen = min(pSymDef32->cbSymName, ulNameBufferLength);
+              memcpy(pszNameBuffer,
+                     pSymDef32->achSymName,
+                     iLen);
+              
+              // terminate the string in any case
+              pszNameBuffer[iLen] = 0;
+              
+              // OK, found
+              return TRUE;
+            }
+          }
+          else
+          {
+            // 16-bit segment
+            // @@@PH not supported
+          }
+        } /* symbol lookup */
+        
+        if (NULL != pSymClosest)
+        {
+          CHAR szBuf[128];
+          CHAR szBuf2[128];
+          int  iLen;
+          
+          // proper symbol name found?
+          if ( (pSymClosest->achSymName[0] > 32) &&
+               (pSymClosest->cbSymName < 255) )
+          {
+            memcpy(szBuf, pSymClosest->achSymName, sizeof(szBuf));
+            szBuf[ sizeof(szBuf) ] = 0;
+            sprintf(szBuf2, "%s+%0xh", szBuf, offset - pSymClosest->wSymVal);
+            iLen = min(pSymClosest->cbSymName, ulNameBufferLength);
+          }
+          else
+          {
+            sprintf(szBuf2, "%s:obj%d:%08xh", getName(), objNr, offset);
+            iLen = min(strlen(szBuf2), ulNameBufferLength);
+          }
+
+          memcpy(pszNameBuffer, szBuf2, iLen);
+          pszNameBuffer[iLen] = 0; // terminate the string in any case
+          return TRUE;            // at least something usable has been found!
+        }
+        else
+        {
+          // nothing usable was found
+          return FALSE;
+        }
+      }
+      
+      SegOffset = (pSegDef->ppNextSeg * 16);
+    } /* segment loop */
+    
+    p += (pMapDef->ppNextMap * 16);
+    pMapDef = (PMAPDEF)p;
+  } /* map loop */
+  
+  // not found
+  return FALSE;
+}
+  
+
+
+
+
+SymbolFilePool::SymbolFilePool()
+{
+  pHashModules = new CHashtableLookup(67);
+}
+
+
+SymbolFilePool::~SymbolFilePool()
+{
+  if (pHashModules)
+    delete pHashModules;
+}
+
+
+APIRET SymbolFilePool::searchModule(PSZ pszModule, PBYTE pBuffer, ULONG ulBufferLength)
+{
+  // 1 - build symbol filename
+  CHAR szSymFile[260];
+  
+  strcpy(szSymFile, pszModule);
+  PSZ pszDot = strrchr(szSymFile, '.');
+  if (NULL == pszDot)
+    pszDot = szSymFile + strlen(szSymFile);
+  
+  strcpy(pszDot, ".sym");
+  
+  // 2 - search path for symbol files
+  //     and copy result to pszFilename
+  APIRET rc = DosSearchPath(SEARCH_IGNORENETERRS |
+                            SEARCH_ENVIRONMENT |
+                            SEARCH_CUR_DIRECTORY,
+                            "PATH",
+                            szSymFile,
+                            pBuffer,
+                            ulBufferLength);
+  if ( (rc == ERROR_FILE_NOT_FOUND) ||
+       (rc == ERROR_ENVVAR_NOT_FOUND) )
+    rc = DosSearchPath(SEARCH_IGNORENETERRS |
+                       SEARCH_ENVIRONMENT |
+                       SEARCH_CUR_DIRECTORY,
+                       "DPATH",
+                       szSymFile,
+                       pBuffer,
+                       ulBufferLength);
+  
+  return rc;
+}
+
+
+  
+BOOL   SymbolFilePool::getSymbolName(PSZ   pszModule,
+                                     ULONG objNr,
+                                     ULONG offset,
+                                     PSZ   pszNameBuffer,
+                                     ULONG ulNameBufferLength)
+{
+  LXSymbolFile* pSym = (LXSymbolFile*)pHashModules->getElement(pszModule);
+  if (NULL == pSym)
+  {
+    CHAR szFilename[260];
+    
+    // 1 - locate the file
+    APIRET rc = searchModule(pszModule, szFilename, sizeof(szFilename));
+    
+    // create new entry
+    pSym = new LXSymbolFile(pszModule, szFilename);
+    
+    // parse the file
+    if (rc == NO_ERROR)
+      pSym->parseFile();
+    else
+      pSym->setErrorMessage("file not found");
+    
+    // add to the hashtable
+    pHashModules->addElement(pszModule, pSym);
+  }
+  
+  BOOL rc;
+  
+  // if the parsed symbol table is correct, lookup the symbol
+  if (pSym->isAvailable())
+  {
+    rc = pSym->getSymbolName(objNr,
+                             offset,
+                             pszNameBuffer,
+                             ulNameBufferLength);
+    
+    if (rc == TRUE)
+    {
+      // check if we can demangle a C++ name
+      char* rest;
+      Name* name = Demangle(pszNameBuffer, rest);
+      if (name != NULL)
+      {
+        strncpy(pszNameBuffer,
+                name->Text(),
+                ulNameBufferLength);
+      
+        delete name;
+      }
+    }
+  }
+  else
+    rc = FALSE;
+  
+  return rc;
+}
+
Index: trunk/src/odinprof/symfile.h
===================================================================
--- trunk/src/odinprof/symfile.h	(revision 7421)
+++ trunk/src/odinprof/symfile.h	(revision 7421)
@@ -0,0 +1,176 @@
+/* $Id: symfile.h,v 1.1 2001-11-22 10:44:00 phaller Exp $ */
+/*
+ * Project Odin Software License can be found in LICENSE.TXT
+ * Execution Trace Profiler
+ *
+ * Copyright 2001 Patrick Haller <patrick.haller@innotek.de>
+ *
+ */
+
+/****************************************************************************
+ * To Do
+ ****************************************************************************
+ 
+ - find and load symbolic debug information file
+   (SYM2IDA)
+ - resolve (relative address) to function name(s)
+ - write result in java.prof style
+ - find tool to nicely work on the output files
+ */
+
+
+#ifndef _SYMFILE_H_
+#define _SYMFILE_H_
+
+
+/****************************************************************************
+ * includes
+ ****************************************************************************/
+
+#include "profcollection.h"
+
+
+/****************************************************************************
+ * definitions
+ ****************************************************************************/
+
+/* Pointer means offset from beginning of file or beginning of struct */
+#pragma pack(1)
+typedef struct {
+  unsigned short int ppNextMap;     /* paragraph pointer to next map         */
+  unsigned char      bFlags;        /* symbol types                          */
+  unsigned char      bReserved1;    /* reserved                              */
+  unsigned short int pSegEntry;     /* segment entry point value             */
+  unsigned short int cConsts;       /* count of constants in map             */
+  unsigned short int pConstDef;     /* pointer to constant chain             */
+  unsigned short int cSegs;         /* count of segments in map              */
+  unsigned short int ppSegDef;      /* paragraph pointer to first segment    */
+  unsigned char      cbMaxSym;      /* maximum symbol-name length            */
+  unsigned char      cbModName;     /* length of module name                 */
+  char               achModName[1]; /* cbModName Bytes of module-name member */
+} MAPDEF, *PMAPDEF;
+
+typedef struct {
+  unsigned short int ppNextMap;     /* always zero                           */
+  unsigned char      release;       /* release number (minor version number) */
+  unsigned char      version;       /* major version number                  */
+} LAST_MAPDEF, *PLAST_MAPDEF;
+
+typedef struct {
+  unsigned short int ppNextSeg;     /* paragraph pointer to next segment     */
+  unsigned short int cSymbols;      /* count of symbols in list              */
+  unsigned short int pSymDef;       /* offset of symbol chain                */
+  unsigned short int wReserved1;    /* reserved                              */
+  unsigned short int wReserved2;    /* reserved                              */
+  unsigned short int wReserved3;    /* reserved                              */
+  unsigned short int wReserved4;    /* reserved                              */
+  unsigned char      bFlags;        /* symbol types                          */
+  unsigned char      bReserved1;    /* reserved                              */
+  unsigned short int ppLineDef;     /* offset of line number record          */
+  unsigned char      bReserved2;    /* reserved                              */
+  unsigned char      bReserved3;    /* reserved                              */
+  unsigned char      cbSegName;     /* length of segment name                */
+  char               achSegName[1]; /* cbSegName Bytes of segment-name member*/
+} SEGDEF, *PSEGDEF;
+
+typedef struct {
+  unsigned short int wSymVal;       /* symbol address or constant            */
+  unsigned char      cbSymName;     /* length of symbol name                 */
+  char               achSymName[1]; /* cbSymName Bytes of symbol-name member */
+} SYMDEF16, *PSEGDEF16;
+
+typedef struct {
+  unsigned       int wSymVal;       /* symbol address or constant            */
+  unsigned char      cbSymName;     /* length of symbol name                 */
+  char               achSymName[1]; /* cbSymName Bytes of symbol-name member */
+} SYMDEF32, *PSYMDEF32;
+
+typedef struct {
+  unsigned short int ppNextLine;    /* ptr to next linedef (0 if last)       */
+  unsigned short int wReserved1;    /* reserved                              */
+  unsigned short int pLines;        /* pointer to line numbers               */
+  unsigned short int cLines;        /* reserved                              */
+  unsigned char      cbFileName;    /* length of filename                    */
+  char               achFileName[1];/* cbFileName Bytes of filename          */
+} LINEDEF, *PLINEDEF;
+
+typedef struct {
+  unsigned short int wCodeOffset;   /* executable offset                     */
+  unsigned short int dwFileOffset;  /* source offset                         */
+} LINEINF, *PLINEINF;
+
+#define SEGDEFOFFSET(MapDef)     (MapDef.ppSegDef*16)
+#define NEXTSEGDEFOFFSET(SegDef)  (SegDef.ppNextSeg*16)
+
+#define ASYMPTROFFSET(SegDefOffset,Segdef) (SegDefOffset+SegDef.pSymDef)
+#define SYMDEFOFFSET(SegDefOffset,SegDef,n) (ASYMPTROFFSET(SegDefOffset,SegDef)+(n)*(sizeof(unsigned short int)))
+
+#define ACONSTPTROFFSET(MapDef) (MapDef.ppConstDef)
+#define CONSTDEFOFFSET(MapDef,n) ((MapDef.ppConstDef)+(n)*(sizeof(unsigned short int)))
+
+#define LINEDEFOFFSET(SegDef) (SegDef.ppLineDef*16))
+#define NEXTLINEDEFOFFSET(LineDef) (LineDef.ppNextLine*16)
+#define LINESOFFSET(LinedefOffset,LineDef) ((LinedefOffset)+LineDef.pLines)
+
+#pragma pack()
+
+
+typedef struct tagLXMap
+{
+  
+} LXMAP, *PLXMAP;
+
+
+class LXSymbolFile
+{
+  public:
+    LXSymbolFile(PSZ pszName, PSZ pszFileName);
+    ~LXSymbolFile();
+  
+    APIRET parseFile();
+    BOOL   isAvailable();
+  
+    void   setErrorMessage(PSZ _pszErrorMessage);
+    PSZ    getErrorMessage();
+    PSZ    getFileName();
+    PSZ    getName() { return pszName; }
+    BOOL   getSymbolName(ULONG objNr,
+                         ULONG offset,
+                         PSZ   pszNameBuffer,
+                         ULONG ulNameBufferLength);
+  
+  protected:
+    PSZ pszName;
+    PSZ pszFileName;
+    PSZ pszErrorMessage;
+  
+    // table: objnr:offset, symbolname
+    PBYTE pSymbolRawData;
+};
+
+
+class SymbolFilePool
+{
+  public:
+    SymbolFilePool();
+    ~SymbolFilePool();
+  
+    APIRET searchModule(PSZ pszModule, PBYTE pBuffer, ULONG ulBufferLength);
+    BOOL   getSymbolName(PSZ   pszModule,
+                         ULONG objNr,
+                         ULONG offset,
+                         PSZ   pszNameBuffer,
+                         ULONG ulNameBufferLength);
+
+  protected:
+    CHashtableLookup*  pHashModules;
+  
+    // - find module name in internal table
+    // - try to load and parse the module
+    // - if loading failed, cache negative result
+    //   so we won't try again (-> isAvailable)
+};
+
+
+#endif /* _SYMFILE_H_ */
+
