1 /* $Id: kProfileR3.cpp 29 2009-07-01 20:30:29Z bird $ */
2 /** @file
3  * kProfiler Mark 2 - The Ring-3 Implementation.
4  */
5 
6 /*
7  * Copyright (c) 2006-2007 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
8  *
9  * Permission is hereby granted, free of charge, to any person
10  * obtaining a copy of this software and associated documentation
11  * files (the "Software"), to deal in the Software without
12  * restriction, including without limitation the rights to use,
13  * copy, modify, merge, publish, distribute, sublicense, and/or sell
14  * copies of the Software, and to permit persons to whom the
15  * Software is furnished to do so, subject to the following
16  * conditions:
17  *
18  * The above copyright notice and this permission notice shall be
19  * included in all copies or substantial portions of the Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
23  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
25  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
26  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
28  * OTHER DEALINGS IN THE SOFTWARE.
29  */
30 
31 /*******************************************************************************
32 *   Header Files                                                               *
33 *******************************************************************************/
34 #include <k/kDefs.h>
35 #if K_OS == K_OS_WINDOWS
36 # include <windows.h>
37 # include <psapi.h>
38 # include <malloc.h>
39 # if _MSC_VER >= 1400
40 #  include <intrin.h>
41 #  define HAVE_INTRIN
42 # endif
43 
44 #elif K_OS == K_OS_LINUX || K_OS == K_OS_FREEBSD
45 # define KPRF_USE_PTHREAD
46 # include <pthread.h>
47 # include <stdint.h>
48 # define KPRF_USE_MMAN
49 # include <sys/mman.h>
50 # include <sys/fcntl.h>
51 # include <unistd.h>
52 # include <stdlib.h>
53 # ifndef O_BINARY
54 #  define O_BINARY 0
55 # endif
56 
57 #elif K_OS == K_OS_OS2
58 # define INCL_BASE
59 # include <os2.h>
60 # include <stdint.h>
61 # include <sys/fmutex.h>
62 
63 #else
64 # error "not ported to this OS..."
65 #endif
66 
67 #include <k/kDefs.h>
68 #include <k/kTypes.h>
69 
70 /*
71  * Instantiate the header.
72  */
73 #define KPRF_NAME(Suffix)               KPrf##Suffix
74 #define KPRF_TYPE(Prefix,Suffix)        Prefix##KPRF##Suffix
75 #if K_OS == K_OS_WINDOWS || K_OS == K_OS_OS2
76 # define KPRF_DECL_FUNC(type, name)     extern "C"  __declspec(dllexport) type __cdecl KPRF_NAME(name)
77 #else
78 # define KPRF_DECL_FUNC(type, name)     extern "C" type KPRF_NAME(name)
79 #endif
80 #if 1
81 # ifdef __GNUC__
82 #  define KPRF_ASSERT(expr) do { if (!(expr)) { __asm__ __volatile__("int3\n\tnop\n\t");} } while (0)
83 # else
84 #  define KPRF_ASSERT(expr) do { if (!(expr)) { __debugbreak(); } } while (0)
85 # endif
86 #else
87 # define KPRF_ASSERT(expr) do { } while (0)
88 #endif
89 
90 #include "prfcore.h.h"
91 
92 
93 
94 /*******************************************************************************
95 *   Structures and Typedefs                                                    *
96 *******************************************************************************/
97 /** Mutex lock type. */
98 #if defined(KPRF_USE_PTHREAD)
99 typedef pthread_mutex_t     KPRF_TYPE(,MUTEX);
100 #elif K_OS == K_OS_WINDOWS
101 typedef CRITICAL_SECTION    KPRF_TYPE(,MUTEX);
102 #elif K_OS == K_OS_OS2
103 typedef struct _fmutex      KPRF_TYPE(,MUTEX);
104 #endif
105 /** Pointer to a mutex lock. */
106 typedef KPRF_TYPE(,MUTEX)  *KPRF_TYPE(P,MUTEX);
107 
108 
109 #if defined(KPRF_USE_PTHREAD)
110 /** Read/Write lock type. */
111 typedef pthread_rwlock_t    KPRF_TYPE(,RWLOCK);
112 #elif K_OS == K_OS_WINDOWS || K_OS == K_OS_OS2
113 /** Read/Write lock state. */
114 typedef enum KPRF_TYPE(,RWLOCKSTATE)
115 {
116     RWLOCK_STATE_UNINITIALIZED = 0,
117     RWLOCK_STATE_SHARED,
118     RWLOCK_STATE_LOCKING,
119     RWLOCK_STATE_EXCLUSIVE,
120     RWLOCK_STATE_32BIT_HACK = 0x7fffffff
121 } KPRF_TYPE(,RWLOCKSTATE);
122 /** Update the state. */
123 #define KPRF_RWLOCK_SETSTATE(pRWLock, enmNewState) \
124     kPrfAtomicSet32((volatile KU32 *)&(pRWLock)->enmState, (KU32)(enmNewState))
125 
126 /** Read/Write lock type. */
127 typedef struct KPRF_TYPE(,RWLOCK)
128 {
129     /** This mutex serialize the access and updating of the members
130      * of this structure. */
131     KPRF_TYPE(,MUTEX)       Mutex;
132     /** The current number of readers. */
133     KU32                    cReaders;
134     /** The number of readers waiting. */
135     KU32                    cReadersWaiting;
136     /** The current number of waiting writers. */
137     KU32                    cWritersWaiting;
138 # if K_OS == K_OS_WINDOWS
139     /** The handle of the event object on which the waiting readers block. (manual reset). */
140     HANDLE                  hevReaders;
141     /** The handle of the event object on which the waiting writers block. (manual reset). */
142     HANDLE                  hevWriters;
143 # elif K_OS == K_OS_OS2
144     /** The handle of the event semaphore on which the waiting readers block. */
145     HEV                     hevReaders;
146     /** The handle of the event semaphore on which the waiting writers block. */
147     HEV                     hevWriters;
148 # endif
149     /** The current state of the read-write lock. */
150     KPRF_TYPE(,RWLOCKSTATE) enmState;
151 } KPRF_TYPE(,RWLOCK);
152 #endif
153 /** Pointer to a Read/Write lock. */
154 typedef KPRF_TYPE(,RWLOCK) *KPRF_TYPE(P,RWLOCK);
155 
156 
157 
158 /*******************************************************************************
159 *   Global Variables                                                           *
160 *******************************************************************************/
161 /** The TLS index / key. */
162 #if K_OS == K_OS_WINDOWS
163 static DWORD                g_dwThreadTLS = TLS_OUT_OF_INDEXES;
164 
165 #elif defined(KPRF_USE_PTHREAD)
166 static pthread_key_t        g_ThreadKey = (pthread_key_t)-1;
167 
168 #elif K_OS == K_OS_OS2
169 static KPRF_TYPE(P,THREAD) *g_ppThread = NULL;
170 
171 #else
172 # error "Not ported to your OS - or you're missing the OS define(s)."
173 #endif
174 
175 /** Pointer to the profiler header. */
176 static KPRF_TYPE(P,HDR)     g_pHdr = NULL;
177 #define KPRF_GET_HDR()                  g_pHdr
178 
179 /** Whether the profiler is enabled or not. */
180 static bool                 g_fEnabled = false;
181 #define KPRF_IS_ACTIVE()                g_fEnabled
182 
183 
184 /** The mutex protecting the threads in g_pHdr. */
185 static KPRF_TYPE(,MUTEX)   g_ThreadsMutex;
186 
187 /** The mutex protecting the module segments in g_pHdr. */
188 static KPRF_TYPE(,MUTEX)   g_ModSegsMutex;
189 
190 /** The read-write lock protecting the functions in g_pHdr. */
191 static KPRF_TYPE(,RWLOCK)  g_FunctionsRWLock;
192 
193 
194 
195 /*******************************************************************************
196 *   Internal Functions                                                         *
197 *******************************************************************************/
198 static KPRF_TYPE(P,THREAD) kPrfGetThreadAutoReg(void);
199 #ifdef KPRF_USE_PTHREAD
200 static void kPrfPThreadKeyDtor(void *pvThread);
201 #endif
202 
203 
204 /**
205  * Gets the pointer to the profiler data for the current thread.
206  *
207  * This implementation automatically adds unknown threads.
208  *
209  * @returns Pointer to the profiler thread data.
210  * @returns NULL if we're out of thread space.
211  */
KPRF_TYPE(P,THREAD)212 static inline KPRF_TYPE(P,THREAD) kPrfGetThread(void)
213 {
214     KPRF_TYPE(P,THREAD) pThread;
215 
216 /* Win32/64 */
217 #if K_OS == K_OS_WINDOWS
218     pThread = (KPRF_TYPE(P,THREAD))TlsGetValue(g_dwThreadTLS);
219 
220 /* Posix Threads */
221 #elif defined(KPRF_USE_PTHREAD)
222     pThread = (KPRF_TYPE(P,THREAD))pthread_getspecific(g_ThreadKey);
223 
224 #elif K_OS == K_OS_OS2
225     pThread = *g_ppThread;
226 
227 #else
228 # error not implemented
229 #endif
230     if (!pThread)
231         pThread = kPrfGetThreadAutoReg();
232     return pThread;
233 }
234 #define KPRF_GET_THREAD()               kPrfGetThread()
235 
236 
237 /**
238  * The the ID of the current thread.
239  *
240  * @returns The thread id.
241  */
kPrfGetThreadId(void)242 static inline KUPTR kPrfGetThreadId(void)
243 {
244 /* Win32/64 */
245 #if K_OS == K_OS_WINDOWS
246     KUPTR ThreadId = (KUPTR)GetCurrentThreadId();
247 
248 /* Posix Threads */
249 #elif defined(KPRF_USE_PTHREAD)
250     KUPTR ThreadId = (KUPTR)pthread_self();
251 
252 #elif K_OS == K_OS_OS2
253     PTIB pTib;
254     PPIB pPib;
255     DosGetInfoBlocks(&pTib, &pPib);
256     ThreadId = pTib->tib_ptib2->tib2_ultid;
257 
258 #else
259 # error not implemented
260 #endif
261 
262     return ThreadId;
263 }
264 #define KPRF_GET_THREADID()             kPrfGetThreadId()
265 
266 
267 /**
268  * The the ID of the current process.
269  *
270  * @returns The process id.
271  */
kPrfGetProcessId(void)272 static inline KUPTR kPrfGetProcessId(void)
273 {
274 /* Win32/64 */
275 #if K_OS == K_OS_WINDOWS
276     KUPTR ThreadId = (KUPTR)GetProcessId(GetCurrentProcess());
277 
278 #elif K_OS == K_OS_OS2
279     PTIB pTib;
280     PPIB pPib;
281     DosGetInfoBlocks(&pTib, &pPib);
282     ThreadId = pPib->pib_pid;
283 
284 #else
285     KUPTR ThreadId = (KUPTR)getpid();
286 #endif
287 
288     return ThreadId;
289 }
290 #define KPRF_GET_PROCESSID()            kPrfGetProcessId()
291 
292 
293 /**
294  * Sets the pointer to the profiler data for the current thread.
295  *
296  * We require fast access to the profiler thread data, so we store
297  * it in a TLS (thread local storage) item/key where the implementation
298  * allows that.
299  *
300  * @param   pThread         The pointer to the profiler thread data for the current thread.
301  */
kPrfSetThread(KPRF_TYPE (P,THREAD)pThread)302 static inline void kPrfSetThread(KPRF_TYPE(P,THREAD) pThread)
303 {
304 /* Win32/64 */
305 #if K_OS == K_OS_WINDOWS
306     BOOL fRc = TlsSetValue(g_dwThreadTLS, pThread);
307 
308 /* Posix Threads */
309 #elif defined(KPRF_USE_PTHREAD)
310     int rc = pthread_setspecific(g_ThreadKey, pThread);
311 
312 #elif K_OS == K_OS_OS2
313     *g_ppThread = pThread;
314 
315 #else
316 # error not implemented
317 #endif
318 }
319 #define KPRF_SET_THREAD(pThread)        kPrfSetThread(pThread)
320 
321 
322 /**
323  * Get the now timestamp.
324  * This must correspond to what the assembly code are doing.
325  */
kPrfNow(void)326 static inline KU64 kPrfNow(void)
327 {
328 #if defined(HAVE_INTRIN)
329     return __rdtsc();
330 # else
331     union
332     {
333         KU64 u64;
334         struct
335         {
336             KU32 u32Lo;
337             KU32 u32Hi;
338         } s;
339     } u;
340 # if defined(__GNUC__)
341     __asm__ __volatile__ ("rdtsc\n\t" : "=a" (u.s.u32Lo), "=d" (u.s.u32Hi));
342 # else
343     __asm
344     {
345         rdtsc
346         mov     [u.s.u32Lo], eax
347         mov     [u.s.u32Hi], edx
348     }
349 
350 # endif
351     return u.u64;
352 #endif
353 }
354 #define KPRF_NOW()                      kPrfNow()
355 
356 
357 /**
358  * Atomically set a 32-bit value.
359  */
kPrfAtomicSet32(volatile KU32 * pu32,const KU32 u32)360 static inline void kPrfAtomicSet32(volatile KU32 *pu32, const KU32 u32)
361 {
362 #if defined(HAVE_INTRIN)
363     _InterlockedExchange((long volatile *)pu32, (const long)u32);
364 
365 #elif defined(__GNUC__)
366     __asm__ __volatile__("xchgl %0, %1\n\t"
367                          : "=m" (*pu32)
368                          : "r" (u32));
369 
370 #elif _MSC_VER
371     __asm
372     {
373         mov     edx, [pu32]
374         mov     eax, [u32]
375         xchg    [edx], eax
376     }
377 
378 #else
379     *pu32 = u32;
380 #endif
381 }
382 #define KPRF_ATOMIC_SET32(a,b)          kPrfAtomicSet32(a, b)
383 
384 
385 
386 /**
387  * Atomically set a 64-bit value.
388  */
kPrfAtomicSet64(volatile KU64 * pu64,KU64 u64)389 static inline void kPrfAtomicSet64(volatile KU64 *pu64, KU64 u64)
390 {
391 #if defined(HAVE_INTRIN) && KPRF_BITS == 64
392     _InterlockedExchange64((KI64 *)pu64, (const KI64)u64);
393 
394 #elif defined(__GNUC__) && KPRF_BITS == 64
395     __asm__ __volatile__("xchgq %0, %1\n\t"
396                          : "=m" (*pu64)
397                          : "r" (u64));
398 
399 #elif defined(__GNUC__) && KPRF_BITS == 32
400     __asm__ __volatile__("1:\n\t"
401                          "lock; cmpxchg8b %1\n\t"
402                          "jnz 1b\n\t"
403                          : "=A" (u64),
404                            "=m" (*pu64)
405                          : "0" (*pu64),
406                            "b" ( (KU32)u64 ),
407                            "c" ( (KU32)(u64 >> 32) ));
408 
409 #elif _MSC_VER
410     __asm
411     {
412         mov     ebx, dword ptr [u64]
413         mov     ecx, dword ptr [u64 + 4]
414         mov     esi, pu64
415         mov     eax, dword ptr [esi]
416         mov     edx, dword ptr [esi + 4]
417     retry:
418         lock cmpxchg8b [esi]
419         jnz retry
420     }
421 #else
422     *pu64 = u64;
423 #endif
424 }
425 #define KPRF_ATOMIC_SET64(a,b)          kPrfAtomicSet64(a, b)
426 
427 
428 /**
429  * Atomically add a 32-bit integer to another.
430  */
kPrfAtomicAdd32(volatile KU32 * pu32,const KU32 u32)431 static inline void kPrfAtomicAdd32(volatile KU32 *pu32, const KU32 u32)
432 {
433 #if defined(HAVE_INTRIN)
434     _InterlockedExchangeAdd((volatile long *)pu32, (const long)u32);
435 
436 #elif defined(__GNUC__)
437     __asm__ __volatile__("lock; addl %0, %1\n\t"
438                          : "=m" (*pu32)
439                          : "r" (u32));
440 
441 #elif _MSC_VER
442     __asm
443     {
444         mov     edx, [pu32]
445         mov     eax, dword ptr [u32]
446         lock add [edx], eax
447     }
448 
449 #else
450     *pu32 += u32;
451 #endif
452 }
453 #define KPRF_ATOMIC_ADD32(a,b)          kPrfAtomicAdd32(a, b)
454 #define KPRF_ATOMIC_INC32(a)            kPrfAtomicAdd32(a, 1);
455 #define KPRF_ATOMIC_DEC32(a)            kPrfAtomicAdd32(a, (KU32)-1);
456 
457 
458 /**
459  * Atomically add a 64-bit integer to another.
460  * Atomically isn't quite required, just a non-corruptive manner, assuming all updates are adds.
461  */
kPrfAtomicAdd64(volatile KU64 * pu64,const KU64 u64)462 static inline void kPrfAtomicAdd64(volatile KU64 *pu64, const KU64 u64)
463 {
464 #if defined(HAVE_INTRIN) && KPRF_BITS == 64
465     _InterlockedExchangeAdd64((volatile KI64 *)pu64, (const KI64)u64);
466 
467 #elif defined(__GNUC__) && KPRF_BITS == 64
468     __asm__ __volatile__("lock; addq %0, %1\n\t"
469                          : "=m" (*pu64)
470                          : "r" (u64));
471 
472 #elif defined(__GNUC__) && KPRF_BITS == 32
473     __asm__ __volatile__("lock; addl %0, %2\n\t"
474                          "lock; adcl %1, %3\n\t"
475                          : "=m" (*(volatile KU32 *)pu64),
476                            "=m" (*((volatile KU32 *)pu64 + 1))
477                          : "r" ((KU32)u64),
478                            "r" ((KU32)(u64 >> 32)));
479 
480 #elif _MSC_VER
481     __asm
482     {
483         mov     edx, [pu64]
484         mov     eax, dword ptr [u64]
485         mov     ecx, dword ptr [u64 + 4]
486         lock add [edx], eax
487         lock adc [edx + 4], ecx
488     }
489 
490 #else
491     *pu64 += u64;
492 #endif
493 }
494 #define KPRF_ATOMIC_ADD64(a,b)          kPrfAtomicAdd64(a, b)
495 #define KPRF_ATOMIC_INC64(a)            kPrfAtomicAdd64(a, 1);
496 
497 
498 /**
499  * Initializes a mutex.
500  *
501  * @returns 0 on success.
502  * @returns -1 on failure.
503  * @param   pMutex      The mutex to init.
504  */
kPrfMutexInit(KPRF_TYPE (P,MUTEX)pMutex)505 static int kPrfMutexInit(KPRF_TYPE(P,MUTEX) pMutex)
506 {
507 #if defined(KPRF_USE_PTHREAD)
508     if (!pthread_mutex_init(pMutex, NULL));
509         return 0;
510     return -1;
511 
512 #elif K_OS == K_OS_WINDOWS
513     InitializeCriticalSection(pMutex);
514     return 0;
515 
516 #elif K_OS == K_OS_OS2
517     if (!_fmutex_create(pMutex, 0))
518         return 0;
519     return -1;
520 #endif
521 }
522 
523 /**
524  * Deletes a mutex.
525  *
526  * @param   pMutex      The mutex to delete.
527  */
kPrfMutexDelete(KPRF_TYPE (P,MUTEX)pMutex)528 static void kPrfMutexDelete(KPRF_TYPE(P,MUTEX) pMutex)
529 {
530 #if defined(KPRF_USE_PTHREAD)
531     pthread_mutex_destroy(pMutex);
532 
533 #elif K_OS == K_OS_WINDOWS
534     DeleteCriticalSection(pMutex);
535 
536 #elif K_OS == K_OS_OS2
537     _fmutex_close(pMutex);
538 #endif
539 }
540 
541 /**
542  * Locks a mutex.
543  * @param   pMutex      The mutex lock.
544  */
kPrfMutexAcquire(KPRF_TYPE (P,MUTEX)pMutex)545 static inline void kPrfMutexAcquire(KPRF_TYPE(P,MUTEX) pMutex)
546 {
547 #if K_OS == K_OS_WINDOWS
548     EnterCriticalSection(pMutex);
549 
550 #elif defined(KPRF_USE_PTHREAD)
551     pthread_mutex_lock(pMutex);
552 
553 #elif K_OS == K_OS_OS2
554     fmutex_request(pMutex);
555 #endif
556 }
557 
558 
559 /**
560  * Unlocks a mutex.
561  * @param   pMutex      The mutex lock.
562  */
kPrfMutexRelease(KPRF_TYPE (P,MUTEX)pMutex)563 static inline void kPrfMutexRelease(KPRF_TYPE(P,MUTEX) pMutex)
564 {
565 #if K_OS == K_OS_WINDOWS
566     LeaveCriticalSection(pMutex);
567 
568 #elif defined(KPRF_USE_PTHREAD)
569     pthread_mutex_lock(pMutex);
570 
571 #elif K_OS == K_OS_OS2
572     fmutex_request(pMutex);
573 #endif
574 }
575 
576 
577 #define KPRF_THREADS_LOCK()             kPrfMutexAcquire(&g_ThreadsMutex)
578 #define KPRF_THREADS_UNLOCK()           kPrfMutexRelease(&g_ThreadsMutex)
579 
580 #define KPRF_MODSEGS_LOCK()             kPrfMutexAcquire(&g_ModSegsMutex)
581 #define KPRF_MODSEGS_UNLOCK()           kPrfMutexRelease(&g_ModSegsMutex)
582 
583 
584 /**
585  * Initializes a read-write lock.
586  *
587  * @returns 0 on success.
588  * @returns -1 on failure.
589  * @param   pRWLock     The read-write lock to initialize.
590  */
kPrfRWLockInit(KPRF_TYPE (P,RWLOCK)pRWLock)591 static inline int kPrfRWLockInit(KPRF_TYPE(P,RWLOCK) pRWLock)
592 {
593 #if defined(KPRF_USE_PTHREAD)
594     if (!pthread_rwlock_init(pRWLock, NULL))
595         return 0;
596     return -1;
597 
598 #elif K_OS == K_OS_WINDOWS || K_OS == K_OS_OS2
599     if (kPrfMutexInit(&pRWLock->Mutex))
600         return -1;
601     pRWLock->cReaders = 0;
602     pRWLock->cReadersWaiting = 0;
603     pRWLock->cWritersWaiting = 0;
604     pRWLock->enmState = RWLOCK_STATE_SHARED;
605 # if K_OS == K_OS_WINDOWS
606     pRWLock->hevReaders = CreateEvent(NULL, TRUE, TRUE, NULL);
607     pRWLock->hevWriters = CreateEvent(NULL, FALSE, FALSE, NULL);
608     if (    pRWLock->hevReaders != INVALID_HANDLE_VALUE
609         &&  pRWLock->hevWriters != INVALID_HANDLE_VALUE)
610         return 0;
611     CloseHandle(pRWLock->hevReaders);
612     CloseHandle(pRWLock->hevWriters);
613 
614 # elif K_OS == K_OS_OS2
615     APIRET rc = DosCreateEventSem(NULL, &pRWLock->hevReaders, 0, TRUE);
616     if (!rc)
617     {
618         rc = DosCreateEventSem(NULL, &pRWLock->hevWriters, 0, TRUE);
619         if (!rc)
620             return 0;
621         pRWLock->hevWriters = NULLHANDLE;
622         DosCloseEventSem(pRWLock->hevReaders);
623     }
624     pRWLock->hevReaders = NULLHANDLE;
625 # endif
626 
627     pRWLock->enmState = RWLOCK_STATE_UNINITIALIZED;
628     kPrfMutexDelete(&pRWLock->Mutex);
629     return -1;
630 #endif
631 }
632 
633 
634 /**
635  * Deleters a read-write lock.
636  *
637  * @param   pRWLock     The read-write lock to delete.
638  */
kPrfRWLockDelete(KPRF_TYPE (P,RWLOCK)pRWLock)639 static inline void kPrfRWLockDelete(KPRF_TYPE(P,RWLOCK) pRWLock)
640 {
641 #if defined(KPRF_USE_PTHREAD)
642     pthread_rwlock_destroy(pRWLock);
643 
644 #elif K_OS == K_OS_WINDOWS || K_OS == K_OS_OS2
645     if (pRWLock->enmState == RWLOCK_STATE_UNINITIALIZED)
646         return;
647 
648     pRWLock->enmState = RWLOCK_STATE_UNINITIALIZED;
649     kPrfMutexDelete(&pRWLock->Mutex);
650     pRWLock->cReaders = 0;
651     pRWLock->cReadersWaiting = 0;
652     pRWLock->cWritersWaiting = 0;
653 # if K_OS == K_OS_WINDOWS
654     CloseHandle(pRWLock->hevReaders);
655     pRWLock->hevReaders = INVALID_HANDLE_VALUE;
656     CloseHandle(pRWLock->hevWriters);
657     pRWLock->hevWriters = INVALID_HANDLE_VALUE;
658 
659 # elif K_OS == K_OS_OS2
660     DosCloseEventSem(pRWLock->hevReaders);
661     pRWLock->hevReaders = NULLHANDLE;
662     DosCloseEventSem(pRWLock->hevWriters);
663     pRWLock->hevWriters = NULLHANDLE;
664 # endif
665 #endif
666 }
667 
668 
669 /**
670  * Acquires read access to the read-write lock.
671  * @param   pRWLock     The read-write lock.
672  */
kPrfRWLockAcquireRead(KPRF_TYPE (P,RWLOCK)pRWLock)673 static inline void kPrfRWLockAcquireRead(KPRF_TYPE(P,RWLOCK) pRWLock)
674 {
675 #if defined(KPRF_USE_PTHREAD)
676     pthread_rwlock_rdlock(pRWLock);
677 
678 #elif K_OS == K_OS_WINDOWS || K_OS == K_OS_OS2
679     if (pRWLock->enmState == RWLOCK_STATE_UNINITIALIZED)
680         return;
681 
682     kPrfMutexAcquire(&pRWLock->Mutex);
683     if (pRWLock->enmState == RWLOCK_STATE_SHARED)
684     {
685         KPRF_ATOMIC_INC32(&pRWLock->cReaders);
686         kPrfMutexRelease(&pRWLock->Mutex);
687         return;
688     }
689 
690     for (;;)
691     {
692         /* have to wait */
693         KPRF_ATOMIC_INC32(&pRWLock->cReadersWaiting);
694 # if K_OS == K_OS_WINDOWS
695         HANDLE hev = pRWLock->hevReaders;
696         ResetEvent(hev);
697 
698 # elif K_OS == K_OS_OS2
699         HEV    hev = pRWLock->hevReaders;
700         ULONG cIgnored;
701         DosResetEventSem(hev, &cIgnored);
702 
703 # endif
704         kPrfMutexRelease(&pRWLock->Mutex);
705 
706 # if K_OS == K_OS_WINDOWS
707         switch (WaitForSingleObject(hev, INFINITE))
708         {
709             case WAIT_IO_COMPLETION:
710             case WAIT_TIMEOUT:
711             case WAIT_OBJECT_0:
712                 break;
713             case WAIT_ABANDONED:
714             default:
715                 return;
716         }
717 
718 # elif K_OS == K_OS_OS2
719         switch (DosWaitEventSem(hev, SEM_INDEFINITE_WAIT))
720         {
721             case NO_ERROR:
722             case ERROR_SEM_TIMEOUT:
723             case ERROR_TIMEOUT:
724             case ERROR_INTERRUPT:
725                 break;
726             default:
727                 return;
728         }
729 # endif
730 
731         kPrfMutexAcquire(&pRWLock->Mutex);
732         if (pRWLock->enmState == RWLOCK_STATE_SHARED)
733         {
734             KPRF_ATOMIC_INC32(&pRWLock->cReaders);
735             KPRF_ATOMIC_DEC32(&pRWLock->cReadersWaiting);
736             kPrfMutexRelease(&pRWLock->Mutex);
737             return;
738         }
739     }
740 #endif
741 }
742 
743 
744 /**
745  * Releases read access to the read-write lock.
746  * @param   pRWLock     The read-write lock.
747  */
kPrfRWLockReleaseRead(KPRF_TYPE (P,RWLOCK)pRWLock)748 static inline void kPrfRWLockReleaseRead(KPRF_TYPE(P,RWLOCK) pRWLock)
749 {
750 #if defined(KPRF_USE_PTHREAD)
751     pthread_rwlock_unlock(pRWLock);
752 
753 #elif K_OS == K_OS_WINDOWS || K_OS == K_OS_OS2
754     if (pRWLock->enmState == RWLOCK_STATE_UNINITIALIZED)
755         return;
756 
757     /*
758      * If we're still in the shared state, or if there
759      * are more readers out there, or if there are no
760      * waiting writers, all we have to do is decrement an leave.
761      *
762      * That's the most frequent, thing and should be fast.
763      */
764     kPrfMutexAcquire(&pRWLock->Mutex);
765     KPRF_ATOMIC_DEC32(&pRWLock->cReaders);
766     if (    pRWLock->enmState == RWLOCK_STATE_SHARED
767         ||  pRWLock->cReaders
768         ||  !pRWLock->cWritersWaiting)
769     {
770         kPrfMutexRelease(&pRWLock->Mutex);
771         return;
772     }
773 
774     /*
775      * Wake up one (or more on OS/2) waiting writers.
776      */
777 # if K_OS == K_OS_WINDOWS
778     SetEvent(pRWLock->hevWriters);
779 # elif K_OS == K_OS_OS2
780     DosPostEvent(pRWLock->hevwriters);
781 # endif
782     kPrfMutexRelease(&pRWLock->Mutex);
783 
784 #endif
785 }
786 
787 
788 /**
789  * Acquires write access to the read-write lock.
790  * @param   pRWLock     The read-write lock.
791  */
kPrfRWLockAcquireWrite(KPRF_TYPE (P,RWLOCK)pRWLock)792 static inline void kPrfRWLockAcquireWrite(KPRF_TYPE(P,RWLOCK) pRWLock)
793 {
794 #if defined(KPRF_USE_PTHREAD)
795     pthread_rwlock_wrlock(pRWLock);
796 
797 #elif K_OS == K_OS_WINDOWS || K_OS == K_OS_OS2
798     if (pRWLock->enmState == RWLOCK_STATE_UNINITIALIZED)
799         return;
800 
801     kPrfMutexAcquire(&pRWLock->Mutex);
802     if (    !pRWLock->cReaders
803         &&  (   pRWLock->enmState == RWLOCK_STATE_SHARED
804              || pRWLock->enmState == RWLOCK_STATE_LOCKING)
805         )
806     {
807         KPRF_RWLOCK_SETSTATE(pRWLock, RWLOCK_STATE_EXCLUSIVE);
808         kPrfMutexRelease(&pRWLock->Mutex);
809         return;
810     }
811 
812     /*
813      * We'll have to wait.
814      */
815     if (pRWLock->enmState == RWLOCK_STATE_SHARED)
816         KPRF_RWLOCK_SETSTATE(pRWLock, RWLOCK_STATE_LOCKING);
817     KPRF_ATOMIC_INC32(&pRWLock->cWritersWaiting);
818     for (;;)
819     {
820 # if K_OS == K_OS_WINDOWS
821         HANDLE hev = pRWLock->hevWriters;
822 # elif K_OS == K_OS_OS2
823         HEV    hev = pRWLock->hevWriters;
824 # endif
825         kPrfMutexRelease(&pRWLock->Mutex);
826 # if K_OS == K_OS_WINDOWS
827         switch (WaitForSingleObject(hev, INFINITE))
828         {
829             case WAIT_IO_COMPLETION:
830             case WAIT_TIMEOUT:
831             case WAIT_OBJECT_0:
832                 break;
833             case WAIT_ABANDONED:
834             default:
835                 KPRF_ATOMIC_DEC32(&pRWLock->cWritersWaiting);
836                 return;
837         }
838 
839 # elif K_OS == K_OS_OS2
840         switch (DosWaitEventSem(hev, SEM_INDEFINITE_WAIT))
841         {
842             case NO_ERROR:
843             case ERROR_SEM_TIMEOUT:
844             case ERROR_TIMEOUT:
845             case ERROR_INTERRUPT:
846                 break;
847             default:
848                 KPRF_ATOMIC_DEC32(&pRWLock->cWritersWaiting);
849                 return;
850         }
851         ULONG cIgnored;
852         DosResetEventSem(hev, &cIgnored);
853 # endif
854 
855         /*
856          * Try acquire the lock.
857          */
858         kPrfMutexAcquire(&pRWLock->Mutex);
859         if (    !pRWLock->cReaders
860             &&  (   pRWLock->enmState == RWLOCK_STATE_SHARED
861                  || pRWLock->enmState == RWLOCK_STATE_LOCKING)
862             )
863         {
864             KPRF_RWLOCK_SETSTATE(pRWLock, RWLOCK_STATE_EXCLUSIVE);
865             KPRF_ATOMIC_DEC32(&pRWLock->cWritersWaiting);
866             kPrfMutexRelease(&pRWLock->Mutex);
867             return;
868         }
869     }
870 #endif
871 }
872 
873 
874 /**
875  * Releases write access to the read-write lock.
876  * @param   pRWLock     The read-write lock.
877  */
kPrfRWLockReleaseWrite(KPRF_TYPE (P,RWLOCK)pRWLock)878 static inline void kPrfRWLockReleaseWrite(KPRF_TYPE(P,RWLOCK) pRWLock)
879 {
880 #if defined(KPRF_USE_PTHREAD)
881     pthread_rwlock_unlock(pRWLock);
882 
883 #elif K_OS == K_OS_WINDOWS || K_OS == K_OS_OS2
884     if (pRWLock->enmState == RWLOCK_STATE_UNINITIALIZED)
885         return;
886 
887     /*
888      * The common thing is that there are noone waiting.
889      * But, before that usual paranoia.
890      */
891     kPrfMutexAcquire(&pRWLock->Mutex);
892     if (pRWLock->enmState != RWLOCK_STATE_EXCLUSIVE)
893     {
894         kPrfMutexRelease(&pRWLock->Mutex);
895         return;
896     }
897     if (    !pRWLock->cReadersWaiting
898         &&  !pRWLock->cWritersWaiting)
899     {
900         KPRF_RWLOCK_SETSTATE(pRWLock, RWLOCK_STATE_SHARED);
901         kPrfMutexRelease(&pRWLock->Mutex);
902         return;
903     }
904 
905     /*
906      * Someone is waiting, wake them up as we change the state.
907      */
908 # if K_OS == K_OS_WINDOWS
909     HANDLE hev = INVALID_HANDLE_VALUE;
910 # elif K_OS == K_OS_OS2
911     HEV    hev = NULLHANDLE;
912 # endif
913 
914     if (pRWLock->cWritersWaiting)
915     {
916         KPRF_RWLOCK_SETSTATE(pRWLock, RWLOCK_STATE_LOCKING);
917         hev = pRWLock->hevWriters;
918     }
919     else
920     {
921         KPRF_RWLOCK_SETSTATE(pRWLock, RWLOCK_STATE_SHARED);
922         hev = pRWLock->hevReaders;
923     }
924 # if K_OS == K_OS_WINDOWS
925     SetEvent(hev);
926 # elif K_OS == K_OS_OS2
927     DosPostEvent(pRWLock->hevwriters);
928 # endif
929     kPrfMutexRelease(&pRWLock->Mutex);
930 
931 #endif
932 }
933 
934 #define KPRF_FUNCS_WRITE_LOCK()         kPrfRWLockAcquireWrite(&g_FunctionsRWLock)
935 #define KPRF_FUNCS_WRITE_UNLOCK()       kPrfRWLockReleaseWrite(&g_FunctionsRWLock)
936 #define KPRF_FUNCS_READ_LOCK()          kPrfRWLockAcquireRead(&g_FunctionsRWLock)
937 #define KPRF_FUNCS_READ_UNLOCK()        kPrfRWLockReleaseRead(&g_FunctionsRWLock)
938 
939 
940 
941 
942 /**
943  * Finds the module segment which the address belongs to.
944  *
945  */
946 static int kPrfGetModSeg(KPRF_TYPE(,UPTR) uAddress, char *pszPath, KU32 cchPath, KU32 *piSegment,
947                          KPRF_TYPE(P,UPTR) puBasePtr, KPRF_TYPE(P,UPTR) pcbSegmentMinusOne)
948 {
949 #if K_OS == K_OS_WINDOWS
950     /*
951      * Enumerate the module handles.
952      */
953     HANDLE      hProcess = GetCurrentProcess();
954     DWORD       cbNeeded = 0;
955     HMODULE     hModIgnored;
956     if (    !EnumProcessModules(hProcess, &hModIgnored, sizeof(hModIgnored), &cbNeeded)
957         &&  GetLastError() != ERROR_BUFFER_OVERFLOW) /** figure out what this actually returns */
958         cbNeeded = 256 * sizeof(HMODULE);
959 
960     cbNeeded += sizeof(HMODULE) * 32;
961     HMODULE *pahModules = (HMODULE *)alloca(cbNeeded);
962     if (EnumProcessModules(hProcess, pahModules, cbNeeded, &cbNeeded))
963     {
964         const unsigned cModules = cbNeeded / sizeof(HMODULE);
965         for (unsigned i = 0; i < cModules; i++)
966         {
967             __try
968             {
969                 const KUPTR uImageBase = (KUPTR)pahModules[i];
970                 union
971                 {
972                     KU8                *pu8;
973                     PIMAGE_DOS_HEADER   pDos;
974                     PIMAGE_NT_HEADERS   pNt;
975                     PIMAGE_NT_HEADERS32 pNt32;
976                     PIMAGE_NT_HEADERS64 pNt64;
977                     KUPTR               u;
978                 } u;
979                 u.u = uImageBase;
980 
981                 /* reject modules higher than the address. */
982                 if (uAddress < u.u)
983                     continue;
984 
985                 /* Skip past the MZ header */
986                 if (u.pDos->e_magic == IMAGE_DOS_SIGNATURE)
987                     u.pu8 += u.pDos->e_lfanew;
988 
989                 /* Ignore anything which isn't an NT header. */
990                 if (u.pNt->Signature != IMAGE_NT_SIGNATURE)
991                     continue;
992 
993                 /* Extract necessary info from the optional header (comes in 32-bit and 64-bit variations, we simplify a bit). */
994                 KU32                    cbImage;
995                 PIMAGE_SECTION_HEADER   paSHs;
996                 if (u.pNt->FileHeader.SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER32))
997                 {
998                     paSHs = (PIMAGE_SECTION_HEADER)(u.pNt32 + 1);
999                     cbImage = u.pNt32->OptionalHeader.SizeOfImage;
1000                 }
1001                 else if (u.pNt->FileHeader.SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER64))
1002                 {
1003                     paSHs = (PIMAGE_SECTION_HEADER)(u.pNt64 + 1);
1004                     cbImage = u.pNt64->OptionalHeader.SizeOfImage;
1005                 }
1006                 else
1007                     continue;
1008 
1009                 /* Is our address within the image size */
1010                 KUPTR uRVA = uAddress - (KUPTR)pahModules[i];
1011                 if (uRVA >= cbImage)
1012                     continue;
1013 
1014                 /*
1015                  * Iterate the section headers and figure which section we're in.
1016                  * (segment == section + 1)
1017                  */
1018                 const KU32 cSHs = u.pNt->FileHeader.NumberOfSections;
1019                 if (uRVA < paSHs[0].VirtualAddress)
1020                 {
1021                     /* the implicit header section */
1022                     *puBasePtr          = uImageBase;
1023                     *pcbSegmentMinusOne = paSHs[0].VirtualAddress - 1;
1024                     *piSegment          = 0;
1025                 }
1026                 else
1027                 {
1028                     KU32 iSH = 0;
1029                     for (;;)
1030                     {
1031                         if (iSH >= cSHs)
1032                         {
1033                             /* this shouldn't happen, but in case it does simply deal with it. */
1034                             *puBasePtr          = paSHs[iSH - 1].VirtualAddress + paSHs[iSH - 1].Misc.VirtualSize + uImageBase;
1035                             *pcbSegmentMinusOne = cbImage - *puBasePtr;
1036                             *piSegment          = iSH + 1;
1037                             break;
1038                         }
1039                         if (uRVA - paSHs[iSH].VirtualAddress < paSHs[iSH].Misc.VirtualSize)
1040                         {
1041                             *puBasePtr          = paSHs[iSH].VirtualAddress + uImageBase;
1042                             *pcbSegmentMinusOne = paSHs[iSH].Misc.VirtualSize;
1043                             *piSegment          = iSH + 1;
1044                             break;
1045                         }
1046                         iSH++;
1047                     }
1048                 }
1049 
1050                 /*
1051                  * Finally, get the module name.
1052                  * There are multiple ways, try them all before giving up.
1053                  */
1054                 if (    !GetModuleFileNameEx(hProcess, pahModules[i], pszPath, cchPath)
1055                     &&  !GetModuleFileName(pahModules[i], pszPath, cchPath)
1056                     &&  !GetMappedFileName(hProcess, (PVOID)uAddress, pszPath, cchPath)
1057                     &&  !GetModuleBaseName(hProcess, pahModules[i], pszPath, cchPath))
1058                     *pszPath = '\0';
1059                 return 0;
1060             }
1061             __except (EXCEPTION_EXECUTE_HANDLER)
1062             {
1063             }
1064         }
1065     }
1066 
1067 #elif K_OS == K_OS_OS2
1068     /*
1069      * Just ask the loader.
1070      */
1071     ULONG   offObj = 0;
1072     ULONG   iObj = 0;
1073     HMODULE hmod = NULLHANDLE;
1074     APIRET rc = DosQueryModFromEIP(&hmod, &iObj, cchPath, pszPath, &offObj, uAddress);
1075     if (!rc)
1076     {
1077         *piSegment = iObj;
1078         *puBasePtr = uAddress - offObj;
1079         *pcbSegmentMinusOne = KPRF_ALIGN(offObj, 0x1000) - 1; /* minimum size */
1080 
1081         /*
1082          * Query the page attributes starting at the current page. The query will not enter
1083          * into the next object since PAG_BASE is requested.
1084          */
1085         ULONG cb     = ~0UL;
1086         ULONG fFlags = ~0UL;
1087         uAddress &= ~(KUPTR)0xfff;
1088         rc = DosQueryMem((PVOID)(uAddress, &cb, &fFlags);
1089         if (!rc)
1090         {
1091             *pcbSegmentMinusOne = (offObj & ~(KUPTR)0xfff) + KPRF_ALIGN(cb, 0x1000) - 1;
1092             if ((fFlags & PAG_BASE) && cb <= 0x1000) /* don't quite remember if PAG_BASE returns one page or not */
1093             {
1094                 cb = ~0UL;
1095                 fFlags = ~0UL;
1096                 rc = DosQueryMem((PVOID)(uAddress + 0x1000), &cb, &fFlags);
1097                 if (!rc & !(fFlags & (PAG_BASE | PAG_FREE)))
1098                     *pcbSegmentMinusOne += KPRF_ALIGN(cb, 0x1000);
1099             }
1100         }
1101         return 0;
1102     }
1103 
1104 #endif
1105     /* The common fallback */
1106     *pszPath = '\0';
1107     *piSegment = 0;
1108     *puBasePtr = 0;
1109     *pcbSegmentMinusOne = ~(KPRF_TYPE(,UPTR))0;
1110     return -1;
1111 }
1112 #define KPRF_GET_MODSEG(uAddress, pszPath, cchPath, piSegment, puBasePtr, pcbSegmentMinusOne) \
1113                                         kPrfGetModSeg(uAddress, pszPath, cchPath, piSegment, puBasePtr, pcbSegmentMinusOne)
1114 
1115 
1116 
1117 
1118 /*
1119  * Instantiate the implementation
1120  */
1121 #include "prfcorepre.cpp.h"
1122 
1123 #include "prfcoremodseg.cpp.h"
1124 #include "prfcorefunction.cpp.h"
1125 #include "prfcore.cpp.h"
1126 #include "prfcoreinit.cpp.h"
1127 #include "prfcoreterm.cpp.h"
1128 
1129 #include "prfcorepost.cpp.h"
1130 
1131 
1132 
1133 
1134 
1135 /**
1136  * Registers an unknown thread.
1137  *
1138  * @returns Pointer to the registered thread.
1139  */
KPRF_TYPE(P,THREAD)1140 static KPRF_TYPE(P,THREAD) kPrfGetThreadAutoReg(void)
1141 {
1142     KUPTR uStackBasePtr;
1143 
1144 #if 0
1145     /** @todo I'm sure Win32 has a way of obtaining the top and bottom of the stack, OS/2 did...
1146      * Some limit stuff in posix / ansi also comes to mind... */
1147 
1148 #elif K_OS == K_OS_OS2
1149     PTIB pTib;
1150     PPIB pPib;
1151     DosGetInfoBlocks(&pTib, &pPib); /* never fails except if you give it bad input, thus 'Get' not 'Query'. */
1152     /* I never recall which of these is the right one... */
1153     uStackBasePtr = (KUPTR)pTib->tib_pstack < (KUPTR)pTib->tib_pstack_limit
1154         ? (KUPTR)pTib->tib_pstack
1155         : (KUPTR)pTib->tib_pstack_limit;
1156 
1157 #else
1158     /* the default is top of the current stack page (assuming a page to be 4KB) */
1159     uStackBasePtr = (KUPTR)&uStackBasePtr;
1160     uStackBasePtr = (uStackBasePtr + 0xfff) & ~(KUPTR)0xfff;
1161 #endif
1162 
1163     return KPRF_NAME(RegisterThread)(uStackBasePtr, "");
1164 }
1165 
1166 
1167 /**
1168  * Get a env.var. variable.
1169  *
1170  * @returns pszValue.
1171  * @param   pszVar      The variable name.
1172  * @param   pszValue    Where to store the value.
1173  * @param   cchValue    The size of the value buffer.
1174  * @param   pszDefault  The default value.
1175  */
kPrfGetEnvString(const char * pszVar,char * pszValue,KU32 cchValue,const char * pszDefault)1176 static char *kPrfGetEnvString(const char *pszVar, char *pszValue, KU32 cchValue, const char *pszDefault)
1177 {
1178 #if K_OS == K_OS_WINDOWS
1179     if (GetEnvironmentVariable(pszVar, pszValue, cchValue))
1180         return pszValue;
1181 
1182 #elif K_OS == K_OS_OS2
1183     PSZ pszValue;
1184     if (    !DosScanEnv((PCSZ)pszVar, &pszValue)
1185         &&  !*pszValue)
1186         pszDefault = pszValue;
1187 
1188 #else
1189     const char *pszTmp = getenv(pszVar);
1190     if (pszTmp)
1191         pszDefault = pszTmp;
1192 
1193 #endif
1194 
1195     /*
1196      * Copy the result into the buffer.
1197      */
1198     char *psz = pszValue;
1199     while (*pszDefault && cchValue-- > 1)
1200         *psz++ = *pszDefault++;
1201     *psz = '\0';
1202 
1203     return pszValue;
1204 }
1205 
1206 
1207 /**
1208  * The the value of an env.var.
1209  *
1210  * @returns The value of the env.var.
1211  * @returns The default if the value was not found.
1212  * @param   pszVar      The variable name.
1213  * @param   uDefault    The default value.
1214  */
kPrfGetEnvValue(const char * pszVar,KU32 uDefault)1215 static KU32 kPrfGetEnvValue(const char *pszVar, KU32 uDefault)
1216 {
1217 #if K_OS == K_OS_WINDOWS
1218     char szBuf[128];
1219     const char *pszValue = szBuf;
1220     if (!GetEnvironmentVariable(pszVar, szBuf, sizeof(szBuf)))
1221         pszValue = NULL;
1222 
1223 #elif K_OS == K_OS_OS2
1224     PSZ pszValue;
1225     if (DosScanEnv((PCSZ)pszVar, &pszValue))
1226         pszValue = NULL;
1227 
1228 #else
1229     const char *pszValue = getenv(pszVar);
1230 
1231 #endif
1232 
1233     /*
1234      * Discard the obvious stuff.
1235      */
1236     if (!pszValue)
1237         return uDefault;
1238     while (*pszValue == ' ' || *pszValue == '\t')
1239         pszValue++;
1240     if (!*pszValue)
1241         return uDefault;
1242 
1243     /*
1244      * Interpret the value.
1245      */
1246     unsigned    uBase = 10;
1247     KU32        uValue = 0;
1248     const char *psz = pszValue;
1249 
1250     /* prefix - only hex */
1251     if (*psz == '0' && (psz[1] == 'x' || psz[1] == 'X'))
1252     {
1253         uBase = 16;
1254         psz += 2;
1255     }
1256 
1257     /* read the value */
1258     while (*psz)
1259     {
1260         unsigned char ch = (unsigned char)*psz;
1261         if (ch >= '0' && ch <= '9')
1262             ch -= '0';
1263         else if (   uBase > 10
1264                  && ch >= 'a' && ch <= 'f')
1265             ch -= 'a' + 10;
1266         else if (   uBase > 10
1267                  && ch >= 'a' && ch <= 'F')
1268             ch -= 'a' + 10;
1269         else
1270             break;
1271         uValue *= uBase;
1272         uValue += ch;
1273         psz++;
1274     }
1275 
1276     /* postfixes */
1277     switch (*psz)
1278     {
1279         case 'm':
1280         case 'M':
1281             uValue *= 1024*1024;
1282             break;
1283 
1284         case 'k':
1285         case 'K':
1286             uValue *= 1024;
1287             break;
1288     }
1289 
1290     /*
1291      * If the value is still 0, we return the default.
1292      */
1293     return uValue ? uValue : uDefault;
1294 }
1295 
1296 
1297 /**
1298  * Allocates memory.
1299  *
1300  * @returns Pointer to the allocated memory.
1301  * @returns NULL on failure.
1302  * @param   cb      The amount of memory (in bytes) to allocate.
1303  */
kPrfAllocMem(KU32 cb)1304 static void *kPrfAllocMem(KU32 cb)
1305 {
1306 #if K_OS == K_OS_WINDOWS
1307     void *pv = VirtualAlloc(NULL, cb, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
1308 
1309 #elif defined(KPRF_USE_MMAN)
1310     void *pv = mmap(NULL, cb, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
1311 
1312 #elif K_OS == K_OS_OS2
1313     void *pv;
1314 # ifdef INCL_DOSEXAPIS
1315     if (DosAllocMemEx(&pv, cb, PAG_READ | PAG_WRITE | PAG_EXECUTE | PAG_COMMIT | OBJ_FORK))s
1316 # else
1317     if (DosAllocMem(&pv, cb, PAG_READ | PAG_WRITE | PAG_EXECUTE | PAG_COMMIT))
1318 # endif
1319         pvBuf = NULL;
1320 
1321 #else
1322 # error not implemented
1323 #endif
1324     return pv;
1325 }
1326 
1327 
1328 /**
1329  * Frees memory.
1330  *
1331  * @param   pv      The memory to free.
1332  */
kPrfFreeMem(void * pv)1333 static void kPrfFreeMem(void *pv)
1334 {
1335 #if K_OS == K_OS_WINDOWS
1336     VirtualFree(pv, 0, MEM_RELEASE);
1337 
1338 #elif defined(KPRF_USE_MMAN)
1339     munmap(pv, 0); /** @todo check if 0 is allowed here.. */
1340 
1341 #elif K_OS == K_OS_OS2
1342 # ifdef INCL_DOSEXAPIS
1343     DosFreeMemEx(&pv);
1344 # else
1345     DosFreeMem(&pv);
1346 # endif
1347 
1348 #else
1349 # error not implemented
1350 #endif
1351 }
1352 
1353 
1354 /**
1355  * Writes a data buffer to a new file.
1356  *
1357  * Any existing file will be overwritten.
1358  *
1359  *
1360  * @returns 0 on success.
1361  * @returns -1 on failure.
1362  *
1363  * @param   pszName     The name of the file.
1364  * @param   pvData      The data to write.
1365  * @param   cbData      The amount of data to write.
1366  */
kPrfWriteFile(const char * pszName,const void * pvData,KU32 cbData)1367 static int kPrfWriteFile(const char *pszName, const void *pvData, KU32 cbData)
1368 {
1369 #if K_OS == K_OS_WINDOWS
1370     int rc = -1;
1371     HANDLE hFile = CreateFile(pszName,GENERIC_WRITE, FILE_SHARE_READ, NULL,
1372                               CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE);
1373     if (hFile != INVALID_HANDLE_VALUE)
1374     {
1375         DWORD dwWritten;
1376         if (    WriteFile(hFile, pvData, cbData, &dwWritten, NULL)
1377             &&  dwWritten == cbData)
1378             rc = 0;
1379         CloseHandle(hFile);
1380     }
1381     return rc;
1382 
1383 #elif K_OS == K_OS_OS2
1384     HFILE hFile;
1385     ULONG ulAction = 0;
1386     APIRET rc = DosOpen(pszName, &hFile, &ulAction, cbData, FILE_NORMAL,
1387                         OPEN_ACTION_REPLACE_IF_EXISTS | OPEN_ACTION_CREATE_IF_NEW,
1388                         OPEN_ACCESS_WRITEONLY | OPEN_SHARE_DENYWRITE | OPEN_FLAGS_NOINHERIT | OPEN_FLAGS_SEQUENTIAL,
1389                         NULL);
1390     if (!rc)
1391     {
1392         ULONG cbWritten;
1393         rc = DosWrite(hFile, pvData, cbData, &cbWritten);
1394         if (!rc && cbWritten != cbData)
1395             rc = -1;
1396         DosClose(hFile);
1397     }
1398     return rc ? -1 : 0;
1399 
1400 #else
1401     int rc = -1;
1402     int fd = open(pszName, O_WRONLY | O_CREAT | O_BINARY | O_TRUNC, 0666);
1403     if (fd >= 0)
1404     {
1405         if (write(fd, pvData, cbData) == cbData)
1406             rc = 0;
1407         close(fd);
1408     }
1409     return rc;
1410 
1411 #endif
1412 }
1413 
1414 
1415 
1416 /**
1417  * Initializes and start the profiling.
1418  *
1419  * This should typically be called from some kind of module init
1420  * function, so we can start profiling upon/before entering main().
1421  *
1422  * @returns 0 on success
1423  * @returns -1 on failure.
1424  *
1425  */
kPrfInitialize(void)1426 int kPrfInitialize(void)
1427 {
1428     /*
1429      * Only initialize once.
1430      */
1431     if (KPRF_GET_HDR())
1432         return 0;
1433 
1434     /*
1435      * Initial suggestions.
1436      */
1437     KU32    cbModSegs  = kPrfGetEnvValue("KPRF2_CBMODSEGS",  128*1024);
1438     KU32    cFunctions = kPrfGetEnvValue("KPRF2_CFUNCTIONS", 8192);
1439     KU32    cThreads   = kPrfGetEnvValue("KPRF2_CTHREADS",   256);
1440     KU32    cStacks    = kPrfGetEnvValue("KPRF2_CSTACKS",    48);
1441     KU32    cFrames    = kPrfGetEnvValue("KPRF2_CFRAMES",    448);
1442     KU32    fAffinity  = kPrfGetEnvValue("KPRF2_AFFINITY",   0);
1443 
1444     KU32    cb = KPRF_NAME(CalcSize)(cFunctions, cbModSegs, cThreads, cStacks, cFrames);
1445 
1446     /*
1447      * Allocate and initialize the data set.
1448      */
1449     void *pvBuf = kPrfAllocMem(cb);
1450     if (!pvBuf)
1451         return -1;
1452 
1453     KPRF_TYPE(P,HDR) pHdr = KPRF_NAME(Init)(pvBuf, cb, cFunctions, cbModSegs, cThreads, cStacks, cFrames);
1454     if (pHdr)
1455     {
1456         /*
1457          * Initialize semaphores.
1458          */
1459         if (!kPrfMutexInit(&g_ThreadsMutex))
1460         {
1461             if (!kPrfMutexInit(&g_ModSegsMutex))
1462             {
1463                 if (!kPrfRWLockInit(&g_FunctionsRWLock))
1464                 {
1465                     /*
1466                      * Allocate the TLS entry.
1467                      */
1468 #if K_OS == K_OS_WINDOWS
1469                     g_dwThreadTLS = TlsAlloc();
1470                     if (g_dwThreadTLS != TLS_OUT_OF_INDEXES)
1471 
1472 #elif defined(KPRF_USE_PTHREAD)
1473                     int rc = pthread_key_create(&g_ThreadKey, kPrfPThreadKeyDtor);
1474                     if (!rc)
1475 
1476 #elif K_OS == K_OS_OS2
1477                     int rc = DosAllocThreadLocalMemory(sizeof(void *), (PULONG*)&g_ppThread); /** @todo check if this is a count or a size. */
1478                     if (!rc)
1479 
1480 #endif
1481                     {
1482                         /*
1483                          * Apply the affinity mask, if specified.
1484                          */
1485                         if (fAffinity)
1486                         {
1487 #if K_OS == K_OS_WINDOWS
1488                             SetProcessAffinityMask(GetCurrentProcess(), fAffinity);
1489 #endif
1490                         }
1491 
1492                         g_pHdr = pHdr;
1493                         g_fEnabled = true;
1494                         return 0;
1495                     }
1496                     kPrfRWLockDelete(&g_FunctionsRWLock);
1497                 }
1498                 kPrfMutexDelete(&g_ModSegsMutex);
1499             }
1500             kPrfMutexDelete(&g_ThreadsMutex);
1501         }
1502     }
1503     kPrfFreeMem(pvBuf);
1504     return -1;
1505 }
1506 
1507 
1508 /**
1509  * Stops, dumps, and terminates the profiling.
1510  *
1511  * This should typically be called from some kind of module destruction
1512  * function, so we can profile parts of the termination sequence too.
1513  *
1514  * @returns 0 on success
1515  * @returns -1 on failure.
1516  *
1517  */
kPrfTerminate(void)1518 int kPrfTerminate(void)
1519 {
1520     /*
1521      * Stop the profiling.
1522      * As a safety precaution, sleep a little bit to allow threads
1523      * still at large inside profiler code some time to get out.
1524      */
1525     g_fEnabled = false;
1526     KPRF_TYPE(P,HDR) pHdr = g_pHdr;
1527     g_pHdr = NULL;
1528     if (!pHdr)
1529         return -1;
1530 
1531 #if K_OS == K_OS_WINDOWS
1532     Sleep(10);
1533 #elif K_OS == K_OS_OS2
1534     DosSleep(10);
1535 #else
1536     usleep(10000);
1537 #endif
1538 
1539     /*
1540      * Unwind all active threads and so forth.
1541      */
1542     KPRF_NAME(TerminateAll)(pHdr);
1543 
1544     /*
1545      * Use the stack space to fill in process details.
1546      */
1547 #if K_OS == K_OS_WINDOWS
1548     /* all is one single string */
1549     const char *pszCommandLine = GetCommandLine();
1550     if (pszCommandLine)
1551         KPRF_NAME(SetCommandLine)(pHdr, 1, &pszCommandLine);
1552 
1553 #elif K_OS == K_OS_OS2 || K_OS == K_OS_OS2
1554     PTIB pTib;
1555     PPIB pPib;
1556     DosGetInfoBlocks(&pTib, &pPib);
1557     if (pPib->pib_pchcmd)
1558     {
1559         /* Tradition say that the commandline is made up of two zero terminate strings
1560          * - first the executable name, then the arguments. Similar to what unix does,
1561          *   only completely mocked up because of the CMD.EXE tradition.
1562          */
1563         const char *apszArgs[2];
1564         apszArgs[0] = pPib->pib_pchcmd;
1565         apszArgs[1] = pPib->pib_pchcmd;
1566         while (apszArgs[1][0])
1567             apszArgs[1]++;
1568         apszArgs[1]++;
1569         KPRF_NAME(SetCommandLine)(pHdr, 2, apszArgs);
1570     }
1571 
1572 #else
1573     /* linux can read /proc/self/something I guess. Don't know about the rest... */
1574 
1575 #endif
1576 
1577     /*
1578      * Write the file to disk.
1579      */
1580     char szName[260 + 16];
1581     kPrfGetEnvString("KPRF2_FILE", szName, sizeof(szName) - 16, "kPrf2-");
1582 
1583     /* append the process id */
1584     KUPTR pid = kPrfGetProcessId();
1585     char *psz = szName;
1586     while (*psz)
1587         psz++;
1588 
1589     static char s_szDigits[0x11] = "0123456789abcdef";
1590     KU32 uShift = KPRF_BITS - 4;
1591     while (     uShift > 0
1592            &&   !(pid & (0xf << uShift)))
1593         uShift -= 4;
1594     *psz++ = s_szDigits[(pid >> uShift) & 0xf];
1595      while (uShift > 0)
1596      {
1597          uShift -= 4;
1598          *psz++ = s_szDigits[(pid >> uShift) & 0xf];
1599      }
1600 
1601     /* .kPrf2 */
1602     *psz++ = '.';
1603     *psz++ = 'k';
1604     *psz++ = 'P';
1605     *psz++ = 'r';
1606     *psz++ = 'f';
1607     *psz++ = '2';
1608     *psz++ = '\0';
1609 
1610     /* write the file. */
1611     int rc = kPrfWriteFile(szName, pHdr, pHdr->cb);
1612 
1613     /*
1614      * Free resources.
1615      */
1616     kPrfFreeMem(pHdr);
1617 #if K_OS == K_OS_WINDOWS
1618     TlsFree(g_dwThreadTLS);
1619     g_dwThreadTLS = TLS_OUT_OF_INDEXES;
1620 
1621 #elif defined(KPRF_USE_PTHREAD)
1622     pthread_key_delete(g_ThreadKey);
1623     g_ThreadKey = (pthread_key_t)-1;
1624 
1625 #elif K_OS == K_OS_OS2
1626     DosFreeThreadLocalMemory((PULONG)g_ppThread);
1627     g_ppThread = NULL;
1628 
1629 #else
1630 # error "port me!"
1631 #endif
1632 
1633     kPrfMutexDelete(&g_ThreadsMutex);
1634     kPrfMutexDelete(&g_ModSegsMutex);
1635     kPrfRWLockDelete(&g_FunctionsRWLock);
1636 
1637     return rc;
1638 }
1639 
1640 
1641 /**
1642  * Terminate the current thread.
1643  */
kPrfTerminateThread(void)1644 void kPrfTerminateThread(void)
1645 {
1646     KPRF_NAME(DeregisterThread)();
1647 }
1648 
1649 
1650 #ifdef KPRF_USE_PTHREAD
1651 /**
1652  * TLS destructor.
1653  */
kPrfPThreadKeyDtor(void * pvThread)1654 static void kPrfPThreadKeyDtor(void *pvThread)
1655 {
1656     KPRF_TYPE(P,HDR) pHdr = KPRF_GET_HDR();
1657     if (pHdr)
1658     {
1659         KPRF_TYPE(P,THREAD) pThread = (KPRF_TYPE(P,THREAD))pvThread;
1660         pthread_setspecific(g_ThreadKey, pvThread);
1661         KPRF_NAME(TerminateThread)(pHdr, pThread, KPRF_NOW());
1662         pthread_setspecific(g_ThreadKey, NULL);
1663     }
1664 }
1665 #endif
1666 
1667