summaryrefslogtreecommitdiffstats
path: root/src/lib/kStuff/kProfiler2/kProfileR3.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/kStuff/kProfiler2/kProfileR3.cpp')
-rw-r--r--src/lib/kStuff/kProfiler2/kProfileR3.cpp1666
1 files changed, 1666 insertions, 0 deletions
diff --git a/src/lib/kStuff/kProfiler2/kProfileR3.cpp b/src/lib/kStuff/kProfiler2/kProfileR3.cpp
new file mode 100644
index 0000000..9e19ee6
--- /dev/null
+++ b/src/lib/kStuff/kProfiler2/kProfileR3.cpp
@@ -0,0 +1,1666 @@
+/* $Id: kProfileR3.cpp 29 2009-07-01 20:30:29Z bird $ */
+/** @file
+ * kProfiler Mark 2 - The Ring-3 Implementation.
+ */
+
+/*
+ * Copyright (c) 2006-2007 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
+#include <k/kDefs.h>
+#if K_OS == K_OS_WINDOWS
+# include <windows.h>
+# include <psapi.h>
+# include <malloc.h>
+# if _MSC_VER >= 1400
+# include <intrin.h>
+# define HAVE_INTRIN
+# endif
+
+#elif K_OS == K_OS_LINUX || K_OS == K_OS_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 K_OS == K_OS_OS2
+# define INCL_BASE
+# include <os2.h>
+# include <stdint.h>
+# include <sys/fmutex.h>
+
+#else
+# error "not ported to this OS..."
+#endif
+
+#include <k/kDefs.h>
+#include <k/kTypes.h>
+
+/*
+ * Instantiate the header.
+ */
+#define KPRF_NAME(Suffix) KPrf##Suffix
+#define KPRF_TYPE(Prefix,Suffix) Prefix##KPRF##Suffix
+#if K_OS == K_OS_WINDOWS || K_OS == K_OS_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)) { __debugbreak(); } } 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 K_OS == K_OS_WINDOWS
+typedef CRITICAL_SECTION KPRF_TYPE(,MUTEX);
+#elif K_OS == K_OS_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 K_OS == K_OS_WINDOWS || K_OS == K_OS_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 KU32 *)&(pRWLock)->enmState, (KU32)(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. */
+ KU32 cReaders;
+ /** The number of readers waiting. */
+ KU32 cReadersWaiting;
+ /** The current number of waiting writers. */
+ KU32 cWritersWaiting;
+# if K_OS == K_OS_WINDOWS
+ /** 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 K_OS == K_OS_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 K_OS == K_OS_WINDOWS
+static DWORD g_dwThreadTLS = TLS_OUT_OF_INDEXES;
+
+#elif defined(KPRF_USE_PTHREAD)
+static pthread_key_t g_ThreadKey = (pthread_key_t)-1;
+
+#elif K_OS == K_OS_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 K_OS == K_OS_WINDOWS
+ 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 K_OS == K_OS_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 KUPTR kPrfGetThreadId(void)
+{
+/* Win32/64 */
+#if K_OS == K_OS_WINDOWS
+ KUPTR ThreadId = (KUPTR)GetCurrentThreadId();
+
+/* Posix Threads */
+#elif defined(KPRF_USE_PTHREAD)
+ KUPTR ThreadId = (KUPTR)pthread_self();
+
+#elif K_OS == K_OS_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 KUPTR kPrfGetProcessId(void)
+{
+/* Win32/64 */
+#if K_OS == K_OS_WINDOWS
+ KUPTR ThreadId = (KUPTR)GetProcessId(GetCurrentProcess());
+
+#elif K_OS == K_OS_OS2
+ PTIB pTib;
+ PPIB pPib;
+ DosGetInfoBlocks(&pTib, &pPib);
+ ThreadId = pPib->pib_pid;
+
+#else
+ KUPTR ThreadId = (KUPTR)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 K_OS == K_OS_WINDOWS
+ BOOL fRc = TlsSetValue(g_dwThreadTLS, pThread);
+
+/* Posix Threads */
+#elif defined(KPRF_USE_PTHREAD)
+ int rc = pthread_setspecific(g_ThreadKey, pThread);
+
+#elif K_OS == K_OS_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 KU64 kPrfNow(void)
+{
+#if defined(HAVE_INTRIN)
+ return __rdtsc();
+# else
+ union
+ {
+ KU64 u64;
+ struct
+ {
+ KU32 u32Lo;
+ KU32 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 KU32 *pu32, const KU32 u32)
+{
+#if defined(HAVE_INTRIN)
+ _InterlockedExchange((long volatile *)pu32, (const long)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 KU64 *pu64, KU64 u64)
+{
+#if defined(HAVE_INTRIN) && KPRF_BITS == 64
+ _InterlockedExchange64((KI64 *)pu64, (const KI64)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" ( (KU32)u64 ),
+ "c" ( (KU32)(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 KU32 *pu32, const KU32 u32)
+{
+#if defined(HAVE_INTRIN)
+ _InterlockedExchangeAdd((volatile long *)pu32, (const long)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, (KU32)-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 KU64 *pu64, const KU64 u64)
+{
+#if defined(HAVE_INTRIN) && KPRF_BITS == 64
+ _InterlockedExchangeAdd64((volatile KI64 *)pu64, (const KI64)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 KU32 *)pu64),
+ "=m" (*((volatile KU32 *)pu64 + 1))
+ : "r" ((KU32)u64),
+ "r" ((KU32)(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 K_OS == K_OS_WINDOWS
+ InitializeCriticalSection(pMutex);
+ return 0;
+
+#elif K_OS == K_OS_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 K_OS == K_OS_WINDOWS
+ DeleteCriticalSection(pMutex);
+
+#elif K_OS == K_OS_OS2
+ _fmutex_close(pMutex);
+#endif
+}
+
+/**
+ * Locks a mutex.
+ * @param pMutex The mutex lock.
+ */
+static inline void kPrfMutexAcquire(KPRF_TYPE(P,MUTEX) pMutex)
+{
+#if K_OS == K_OS_WINDOWS
+ EnterCriticalSection(pMutex);
+
+#elif defined(KPRF_USE_PTHREAD)
+ pthread_mutex_lock(pMutex);
+
+#elif K_OS == K_OS_OS2
+ fmutex_request(pMutex);
+#endif
+}
+
+
+/**
+ * Unlocks a mutex.
+ * @param pMutex The mutex lock.
+ */
+static inline void kPrfMutexRelease(KPRF_TYPE(P,MUTEX) pMutex)
+{
+#if K_OS == K_OS_WINDOWS
+ LeaveCriticalSection(pMutex);
+
+#elif defined(KPRF_USE_PTHREAD)
+ pthread_mutex_lock(pMutex);
+
+#elif K_OS == K_OS_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 K_OS == K_OS_WINDOWS || K_OS == K_OS_OS2
+ if (kPrfMutexInit(&pRWLock->Mutex))
+ return -1;
+ pRWLock->cReaders = 0;
+ pRWLock->cReadersWaiting = 0;
+ pRWLock->cWritersWaiting = 0;
+ pRWLock->enmState = RWLOCK_STATE_SHARED;
+# if K_OS == K_OS_WINDOWS
+ 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 K_OS == K_OS_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 K_OS == K_OS_WINDOWS || K_OS == K_OS_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 K_OS == K_OS_WINDOWS
+ CloseHandle(pRWLock->hevReaders);
+ pRWLock->hevReaders = INVALID_HANDLE_VALUE;
+ CloseHandle(pRWLock->hevWriters);
+ pRWLock->hevWriters = INVALID_HANDLE_VALUE;
+
+# elif K_OS == K_OS_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 K_OS == K_OS_WINDOWS || K_OS == K_OS_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 K_OS == K_OS_WINDOWS
+ HANDLE hev = pRWLock->hevReaders;
+ ResetEvent(hev);
+
+# elif K_OS == K_OS_OS2
+ HEV hev = pRWLock->hevReaders;
+ ULONG cIgnored;
+ DosResetEventSem(hev, &cIgnored);
+
+# endif
+ kPrfMutexRelease(&pRWLock->Mutex);
+
+# if K_OS == K_OS_WINDOWS
+ switch (WaitForSingleObject(hev, INFINITE))
+ {
+ case WAIT_IO_COMPLETION:
+ case WAIT_TIMEOUT:
+ case WAIT_OBJECT_0:
+ break;
+ case WAIT_ABANDONED:
+ default:
+ return;
+ }
+
+# elif K_OS == K_OS_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 K_OS == K_OS_WINDOWS || K_OS == K_OS_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 K_OS == K_OS_WINDOWS
+ SetEvent(pRWLock->hevWriters);
+# elif K_OS == K_OS_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 K_OS == K_OS_WINDOWS || K_OS == K_OS_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 K_OS == K_OS_WINDOWS
+ HANDLE hev = pRWLock->hevWriters;
+# elif K_OS == K_OS_OS2
+ HEV hev = pRWLock->hevWriters;
+# endif
+ kPrfMutexRelease(&pRWLock->Mutex);
+# if K_OS == K_OS_WINDOWS
+ 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 K_OS == K_OS_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 K_OS == K_OS_WINDOWS || K_OS == K_OS_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 K_OS == K_OS_WINDOWS
+ HANDLE hev = INVALID_HANDLE_VALUE;
+# elif K_OS == K_OS_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 K_OS == K_OS_WINDOWS
+ SetEvent(hev);
+# elif K_OS == K_OS_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, KU32 cchPath, KU32 *piSegment,
+ KPRF_TYPE(P,UPTR) puBasePtr, KPRF_TYPE(P,UPTR) pcbSegmentMinusOne)
+{
+#if K_OS == K_OS_WINDOWS
+ /*
+ * 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 KUPTR uImageBase = (KUPTR)pahModules[i];
+ union
+ {
+ KU8 *pu8;
+ PIMAGE_DOS_HEADER pDos;
+ PIMAGE_NT_HEADERS pNt;
+ PIMAGE_NT_HEADERS32 pNt32;
+ PIMAGE_NT_HEADERS64 pNt64;
+ KUPTR 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). */
+ KU32 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 */
+ KUPTR uRVA = uAddress - (KUPTR)pahModules[i];
+ if (uRVA >= cbImage)
+ continue;
+
+ /*
+ * Iterate the section headers and figure which section we're in.
+ * (segment == section + 1)
+ */
+ const KU32 cSHs = u.pNt->FileHeader.NumberOfSections;
+ if (uRVA < paSHs[0].VirtualAddress)
+ {
+ /* the implicit header section */
+ *puBasePtr = uImageBase;
+ *pcbSegmentMinusOne = paSHs[0].VirtualAddress - 1;
+ *piSegment = 0;
+ }
+ else
+ {
+ KU32 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 K_OS == K_OS_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 &= ~(KUPTR)0xfff;
+ rc = DosQueryMem((PVOID)(uAddress, &cb, &fFlags);
+ if (!rc)
+ {
+ *pcbSegmentMinusOne = (offObj & ~(KUPTR)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)
+{
+ KUPTR 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 K_OS == K_OS_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 = (KUPTR)pTib->tib_pstack < (KUPTR)pTib->tib_pstack_limit
+ ? (KUPTR)pTib->tib_pstack
+ : (KUPTR)pTib->tib_pstack_limit;
+
+#else
+ /* the default is top of the current stack page (assuming a page to be 4KB) */
+ uStackBasePtr = (KUPTR)&uStackBasePtr;
+ uStackBasePtr = (uStackBasePtr + 0xfff) & ~(KUPTR)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, KU32 cchValue, const char *pszDefault)
+{
+#if K_OS == K_OS_WINDOWS
+ if (GetEnvironmentVariable(pszVar, pszValue, cchValue))
+ return pszValue;
+
+#elif K_OS == K_OS_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 KU32 kPrfGetEnvValue(const char *pszVar, KU32 uDefault)
+{
+#if K_OS == K_OS_WINDOWS
+ char szBuf[128];
+ const char *pszValue = szBuf;
+ if (!GetEnvironmentVariable(pszVar, szBuf, sizeof(szBuf)))
+ pszValue = NULL;
+
+#elif K_OS == K_OS_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;
+ KU32 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;
+ psz++;
+ }
+
+ /* 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(KU32 cb)
+{
+#if K_OS == K_OS_WINDOWS
+ 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 K_OS == K_OS_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 K_OS == K_OS_WINDOWS
+ VirtualFree(pv, 0, MEM_RELEASE);
+
+#elif defined(KPRF_USE_MMAN)
+ munmap(pv, 0); /** @todo check if 0 is allowed here.. */
+
+#elif K_OS == K_OS_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, KU32 cbData)
+{
+#if K_OS == K_OS_WINDOWS
+ 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 K_OS == K_OS_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.
+ */
+ KU32 cbModSegs = kPrfGetEnvValue("KPRF2_CBMODSEGS", 128*1024);
+ KU32 cFunctions = kPrfGetEnvValue("KPRF2_CFUNCTIONS", 8192);
+ KU32 cThreads = kPrfGetEnvValue("KPRF2_CTHREADS", 256);
+ KU32 cStacks = kPrfGetEnvValue("KPRF2_CSTACKS", 48);
+ KU32 cFrames = kPrfGetEnvValue("KPRF2_CFRAMES", 448);
+ KU32 fAffinity = kPrfGetEnvValue("KPRF2_AFFINITY", 0);
+
+ KU32 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 K_OS == K_OS_WINDOWS
+ 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 K_OS == K_OS_OS2
+ int rc = DosAllocThreadLocalMemory(sizeof(void *), (PULONG*)&g_ppThread); /** @todo check if this is a count or a size. */
+ if (!rc)
+
+#endif
+ {
+ /*
+ * Apply the affinity mask, if specified.
+ */
+ if (fAffinity)
+ {
+#if K_OS == K_OS_WINDOWS
+ SetProcessAffinityMask(GetCurrentProcess(), fAffinity);
+#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 K_OS == K_OS_WINDOWS
+ Sleep(10);
+#elif K_OS == K_OS_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 K_OS == K_OS_WINDOWS
+ /* all is one single string */
+ const char *pszCommandLine = GetCommandLine();
+ if (pszCommandLine)
+ KPRF_NAME(SetCommandLine)(pHdr, 1, &pszCommandLine);
+
+#elif K_OS == K_OS_OS2 || K_OS == K_OS_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 */
+ KUPTR pid = kPrfGetProcessId();
+ char *psz = szName;
+ while (*psz)
+ psz++;
+
+ static char s_szDigits[0x11] = "0123456789abcdef";
+ KU32 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 K_OS == K_OS_WINDOWS
+ 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 K_OS == K_OS_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
+