3 * kProfiler Mark 2 - Core Code Template.
7 * Copyright (c) 2006-2007 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
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
18 * The above copyright notice and this permission notice shall be
19 * included in all copies or substantial portions of the Software.
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.
33 * Gets a function, create a new one if necessary.
35 static KPRF_TYPE(P
,FUNC
) KPRF_NAME(GetFunction
)(KPRF_TYPE(P
,HDR
) pHdr
, KPRF_TYPE(,UPTR
) uPC
)
38 * Perform a binary search of the function lookup table.
40 KPRF_TYPE(P
,FUNC
) paFunctions
= KPRF_OFF2PTR(P
,FUNC
, pHdr
->offFunctions
, pHdr
);
42 KPRF_FUNCS_READ_LOCK();
44 KI32 iLast
= pHdr
->cFunctions
- 1;
48 KU32 iFunction
= pHdr
->aiFunctions
[i
];
49 KPRF_TYPE(,IPTR
) iDiff
= uPC
- paFunctions
[iFunction
].uEntryPtr
;
52 KPRF_FUNCS_READ_UNLOCK();
53 return &paFunctions
[iFunction
];
63 i
= iStart
+ (iLast
- iStart
) / 2;
65 KPRF_FUNCS_READ_UNLOCK();
68 * It wasn't found, try add it.
70 if (pHdr
->cFunctions
< pHdr
->cMaxFunctions
)
71 return KPRF_NAME(NewFunction
)(pHdr
, uPC
);
79 static KU64
* KPRF_NAME(UnwindOne
)(KPRF_TYPE(P
,HDR
) pHdr
, KPRF_TYPE(P
,STACK
) pStack
, KPRF_TYPE(,UPTR
) uPC
, KU64 TS
)
82 * Pop off the frame and update the frame below / thread.
84 KPRF_TYPE(P
,FRAME
) pFrame
= &pStack
->aFrames
[--pStack
->cFrames
];
85 KU64
*pCurOverheadTicks
;
88 KPRF_TYPE(P
,FRAME
) pTopFrame
= pFrame
- 1;
89 pTopFrame
->OverheadTicks
+= pFrame
->OverheadTicks
+ pFrame
->CurOverheadTicks
;
90 pTopFrame
->SleepTicks
+= pFrame
->SleepTicks
;
91 pTopFrame
->OnTopOfStackStart
= TS
;
92 pTopFrame
->CurOverheadTicks
= 0;
94 pCurOverheadTicks
= &pTopFrame
->CurOverheadTicks
;
98 KPRF_TYPE(P
,THREAD
) pThread
= KPRF_OFF2PTR(P
,THREAD
, pStack
->offThread
, pHdr
);
99 pThread
->ProfiledTicks
+= TS
- pFrame
->OnStackStart
- pFrame
->CurOverheadTicks
- pFrame
->OverheadTicks
- pFrame
->SleepTicks
;
100 pThread
->OverheadTicks
+= pFrame
->OverheadTicks
+ pFrame
->CurOverheadTicks
;
101 pThread
->SleepTicks
+= pFrame
->SleepTicks
;
103 pCurOverheadTicks
= &pThread
->OverheadTicks
;
107 * Update the function (if any).
109 if (pFrame
->offFunction
)
111 KPRF_TYPE(P
,FUNC
) pFunc
= KPRF_OFF2PTR(P
,FUNC
, pFrame
->offFunction
, pHdr
);
114 KU64 Ticks
= TS
- pFrame
->OnStackStart
;
115 Ticks
-= pFrame
->OverheadTicks
+ pFrame
->CurOverheadTicks
+ pFrame
->SleepTicks
;
116 /** @todo adjust overhead */
117 KPRF_ASSERT(!(Ticks
>> 63));
118 if (pFunc
->OnStack
.MinTicks
> Ticks
)
119 KPRF_ATOMIC_SET64(&pFunc
->OnStack
.MinTicks
, Ticks
);
120 if (pFunc
->OnStack
.MaxTicks
< Ticks
)
121 KPRF_ATOMIC_SET64(&pFunc
->OnStack
.MaxTicks
, Ticks
);
122 KPRF_ATOMIC_ADD64(&pFunc
->OnStack
.SumTicks
, Ticks
);
124 /* Time on top of stack */
125 Ticks
= TS
- pFrame
->OnTopOfStackStart
;
126 Ticks
-= pFrame
->CurOverheadTicks
;
127 Ticks
+= pFrame
->OnTopOfStackTicks
;
128 /** @todo adjust overhead */
129 KPRF_ASSERT(!(Ticks
>> 63));
130 if (pFunc
->OnTopOfStack
.MinTicks
> Ticks
)
131 KPRF_ATOMIC_SET64(&pFunc
->OnTopOfStack
.MinTicks
, Ticks
);
132 if (pFunc
->OnTopOfStack
.MaxTicks
< Ticks
)
133 KPRF_ATOMIC_SET64(&pFunc
->OnTopOfStack
.MaxTicks
, Ticks
);
134 KPRF_ATOMIC_ADD64(&pFunc
->OnTopOfStack
.SumTicks
, Ticks
);
138 KPRF_ATOMIC_ADD64(&pFunc
->cCalls
, pFrame
->cCalls
);
141 return pCurOverheadTicks
;
148 * On MSC+AMD64 we have to be very very careful here, because the uFramePtr cannot be trusted.
150 static KU64
* KPRF_NAME(UnwindInt
)(KPRF_TYPE(P
,HDR
) pHdr
, KPRF_TYPE(P
,STACK
) pStack
, KPRF_TYPE(,UPTR
) uPC
, KPRF_TYPE(,UPTR
) uFramePtr
, KU64 TS
)
152 /** @todo need to deal with alternative stacks! */
155 * Pop the stack until we're down below the current frame (uFramePtr).
157 KI32 iFrame
= pStack
->cFrames
- 1;
158 KPRF_TYPE(P
,FRAME
) pFrame
= &pStack
->aFrames
[iFrame
];
160 /* the most frequent case first. */
161 #if K_OS == K_OS_WINDOWS && K_ARCH == K_ARCH_AMD64
162 if ( uFramePtr
== pFrame
->uFramePtr
163 || ( pFrame
->uFramePtr
< uFramePtr
165 && pFrame
[-1].uFramePtr
> uFramePtr
))
166 return KPRF_NAME(UnwindOne
)(pHdr
, pStack
, uPC
, TS
);
168 if (uFramePtr
== pFrame
->uFramePtr
)
169 return KPRF_NAME(UnwindOne
)(pHdr
, pStack
, uPC
, TS
);
173 if (pFrame
->uFramePtr
> uFramePtr
)
174 return &pFrame
->CurOverheadTicks
;
176 /* one or more, possibly all */
177 KU64
*pCurOverheadTicks
= KPRF_NAME(UnwindOne
)(pHdr
, pStack
, uPC
, TS
);
180 #if K_OS == K_OS_WINDOWS && K_ARCH == K_ARCH_AMD64
181 && pFrame
->uFramePtr
<= uFramePtr
182 && pFrame
[-1].uFramePtr
> uFramePtr
)
184 && pFrame
->uFramePtr
<= uFramePtr
)
187 KPRF_TYPE(P
,THREAD
) pThread
= KPRF_OFF2PTR(P
,THREAD
, pStack
->offThread
, pHdr
);
188 pThread
->cUnwinds
++; /* (This is the reason for what looks like a bad loop unrolling.) */
190 pCurOverheadTicks
= KPRF_NAME(UnwindOne
)(pHdr
, pStack
, uPC
, TS
);
193 #if K_OS == K_OS_WINDOWS && K_ARCH == K_ARCH_AMD64
195 && pFrame
->uFramePtr
<= uFramePtr
196 && pFrame
[-1].uFramePtr
> uFramePtr
)
199 && pFrame
->uFramePtr
<= uFramePtr
)
202 pCurOverheadTicks
= KPRF_NAME(UnwindOne
)(pHdr
, pStack
, uPC
, TS
);
208 return pCurOverheadTicks
;
216 * @returns Where to account overhead.
217 * @returns NULL if profiling is inactive.
219 * @param uPC The program counter register. (not relative)
220 * @param uFramePtr The stack frame address. This must match the one passed to kPrfLeave. (not relative)
221 * @param TS The timestamp when we entered into the profiler.
222 * This must not be modified touched!
226 KPRF_DECL_FUNC(KU64
*, Enter
)(KPRF_TYPE(,UPTR
) uPC
, KPRF_TYPE(,UPTR
) uFramePtr
, const KU64 TS
)
229 * Is profiling active ?
231 if (!KPRF_IS_ACTIVE())
235 * Get the header and adjust input addresses.
237 KPRF_TYPE(P
,HDR
) pHdr
= KPRF_GET_HDR();
240 const KPRF_TYPE(,UPTR
) uBasePtr
= pHdr
->uBasePtr
;
243 uFramePtr
-= uBasePtr
;
248 * Get the current thread. Reject unknown, inactive (in whatever way),
249 * and thread which has performed a stack switch.
251 KPRF_TYPE(P
,THREAD
) pThread
= KPRF_GET_THREAD();
254 KPRF_TYPE(,THREADSTATE
) enmThreadState
= pThread
->enmState
;
255 if ( enmThreadState
!= KPRF_TYPE(,THREADSTATE_ACTIVE
)
256 && enmThreadState
!= KPRF_TYPE(,THREADSTATE_OVERFLOWED
)
259 if (pThread
->uStackBasePtr
< uFramePtr
) /* ASSUMES stack direction */
261 pThread
->cStackSwitchRejects
++;
264 pThread
->enmState
= KPRF_TYPE(,THREADSTATE_SUSPENDED
);
268 * Update the thread statistics.
271 KPRF_TYPE(,UPTR
) cbStack
= pThread
->uStackBasePtr
- uFramePtr
; /* ASSUMES stack direction */
272 if (pThread
->cbMaxStack
< cbStack
)
273 pThread
->cbMaxStack
= cbStack
;
276 * Check if an longjmp or throw has taken place.
277 * This check will not work if a stack switch has taken place (can fix that later).
279 KPRF_TYPE(P
,STACK
) pStack
= KPRF_OFF2PTR(P
,STACK
, pThread
->offStack
, pHdr
);
280 KU32 iFrame
= pStack
->cFrames
;
281 KPRF_TYPE(P
,FRAME
) pFrame
= &pStack
->aFrames
[iFrame
];
283 #if K_OS == K_OS_WINDOWS && K_ARCH == K_ARCH_AMD64
284 && 0) /* don't bother her yet because of _penter/_pexit frame problems. */
286 && pThread
->uStackBasePtr
>= uFramePtr
/* ASSUMES stack direction */
287 && pFrame
[-1].uFramePtr
+ (KPRF_BITS
- 8) / 8 < uFramePtr
) /* ASSUMES stack direction */
290 KPRF_NAME(UnwindInt
)(pHdr
, pStack
, uPC
, uFramePtr
, TS
);
291 iFrame
= pStack
->cFrames
;
295 * Allocate a new stack frame.
297 if (iFrame
>= pHdr
->cMaxStackFrames
)
300 pThread
->enmState
= KPRF_TYPE(,THREADSTATE_OVERFLOWED
);
301 pThread
->cOverflows
+= enmThreadState
!= KPRF_TYPE(,THREADSTATE_OVERFLOWED
);
302 return &pStack
->aFrames
[iFrame
- 1].CurOverheadTicks
;
307 * Update the old top frame if any.
311 KPRF_TYPE(P
,FRAME
) pOldFrame
= pFrame
- 1;
312 pOldFrame
->OnTopOfStackTicks
+= TS
- pOldFrame
->OnTopOfStackStart
;
317 * Fill in the new frame.
319 pFrame
->CurOverheadTicks
= 0;
320 pFrame
->OverheadTicks
= 0;
321 pFrame
->SleepTicks
= 0;
322 pFrame
->OnStackStart
= TS
;
323 pFrame
->OnTopOfStackStart
= TS
;
324 pFrame
->OnTopOfStackTicks
= 0;
326 pFrame
->uFramePtr
= uFramePtr
;
329 * Find the relevant function.
331 KPRF_TYPE(P
,FUNC
) pFunc
= KPRF_NAME(GetFunction
)(pHdr
, uPC
);
334 pFrame
->offFunction
= KPRF_PTR2OFF(pFunc
, pHdr
);
338 pFrame
->offFunction
= 0;
341 * Nearly done, We only have to reactivate the thread and account overhead.
342 * The latter is delegated to the caller.
344 pThread
->enmState
= KPRF_TYPE(,THREADSTATE_ACTIVE
);
345 return &pFrame
->CurOverheadTicks
;
352 * @returns Where to account overhead.
353 * @returns NULL if profiling is inactive.
355 * @param uPC The program counter register.
356 * @param uFramePtr The stack frame address. This must match the one passed to kPrfEnter.
357 * @param TS The timestamp when we entered into the profiler.
358 * This must not be modified because the caller could be using it!
361 KPRF_DECL_FUNC(KU64
*, Leave
)(KPRF_TYPE(,UPTR
) uPC
, KPRF_TYPE(,UPTR
) uFramePtr
, const KU64 TS
)
364 * Is profiling active ?
366 if (!KPRF_IS_ACTIVE())
370 * Get the header and adjust input addresses.
372 KPRF_TYPE(P
,HDR
) pHdr
= KPRF_GET_HDR();
375 const KPRF_TYPE(,UPTR
) uBasePtr
= pHdr
->uBasePtr
;
378 uFramePtr
-= uBasePtr
;
383 * Get the current thread and suspend profiling of the thread until we leave this function.
384 * Also reject threads which aren't active in some way.
386 KPRF_TYPE(P
,THREAD
) pThread
= KPRF_GET_THREAD();
389 KPRF_TYPE(,THREADSTATE
) enmThreadState
= pThread
->enmState
;
390 if ( enmThreadState
!= KPRF_TYPE(,THREADSTATE_ACTIVE
)
391 && enmThreadState
!= KPRF_TYPE(,THREADSTATE_OVERFLOWED
)
394 KPRF_TYPE(P
,STACK
) pStack
= KPRF_OFF2PTR(P
,STACK
, pThread
->offStack
, pHdr
);
395 if (!pStack
->cFrames
)
397 pThread
->enmState
= KPRF_TYPE(,THREADSTATE_SUSPENDED
);
400 * Unwind the stack down to and including the entry indicated by uFramePtr.
401 * Leave it to the caller to update the overhead.
403 KU64
*pCurOverheadTicks
= KPRF_NAME(UnwindInt
)(pHdr
, pStack
, uPC
, uFramePtr
, TS
);
405 pThread
->enmState
= enmThreadState
;
406 return pCurOverheadTicks
;
411 * Register the current thread.
413 * A thread can only be profiled if it has been registered by a call to this function.
415 * @param uPC The program counter register.
416 * @param uStackBasePtr The base of the stack.
418 KPRF_DECL_FUNC(KPRF_TYPE(P
,THREAD
), RegisterThread
)(KPRF_TYPE(,UPTR
) uStackBasePtr
, const char *pszName
)
421 * Get the header and adjust input address.
422 * (It doesn't matter whether we're active or not.)
424 KPRF_TYPE(P
,HDR
) pHdr
= KPRF_GET_HDR();
427 const KPRF_TYPE(,UPTR
) uBasePtr
= pHdr
->uBasePtr
;
429 uStackBasePtr
-= uBasePtr
;
433 * Allocate a thread and a stack.
436 if (pHdr
->cThreads
< pHdr
->cMaxThreads
)
438 KPRF_TYPE(P
,STACK
) pStack
= KPRF_OFF2PTR(P
,STACK
, pHdr
->offStacks
, pHdr
);
439 KU32 cLeft
= pHdr
->cMaxStacks
;
442 if (!pStack
->offThread
)
444 /* init the stack. */
446 pStack
->offThread
= pHdr
->offThreads
+ pHdr
->cbThread
* pHdr
->cThreads
++;
449 /* init the thread */
450 KPRF_TYPE(P
,THREAD
) pThread
= KPRF_OFF2PTR(P
,THREAD
, pStack
->offThread
, pHdr
);
451 pThread
->ThreadId
= KPRF_GET_THREADID();
454 while (i
< sizeof(pThread
->szName
) - 1 && *pszName
)
455 pThread
->szName
[i
++] = *pszName
++;
456 while (i
< sizeof(pThread
->szName
))
457 pThread
->szName
[i
++] = '\0';
458 pThread
->enmState
= KPRF_TYPE(,THREADSTATE_SUSPENDED
);
459 pThread
->Reserved0
= KPRF_TYPE(,THREADSTATE_TERMINATED
);
460 pThread
->uStackBasePtr
= uStackBasePtr
;
461 pThread
->cbMaxStack
= 0;
463 pThread
->cOverflows
= 0;
464 pThread
->cStackSwitchRejects
= 0;
465 pThread
->cUnwinds
= 0;
466 pThread
->ProfiledTicks
= 0;
467 pThread
->OverheadTicks
= 0;
468 pThread
->SleepTicks
= 0;
469 pThread
->offStack
= KPRF_PTR2OFF(pStack
, pHdr
);
472 /* set the thread and make it active. */
473 KPRF_THREADS_UNLOCK();
474 KPRF_SET_THREAD(pThread
);
475 pThread
->enmState
= KPRF_TYPE(,THREADSTATE_ACTIVE
);
480 pStack
= KPRF_TYPE(P
,STACK
)(((KPRF_TYPE(,UPTR
))pStack
+ pHdr
->cbStack
));
481 } while (--cLeft
> 0);
484 KPRF_THREADS_UNLOCK();
490 * Terminates a thread.
492 * To terminate the current thread use DeregisterThread(), because that
493 * cleans up the TLS entry too.
495 * @param pHdr The profiler data set header.
496 * @param pThread The thread to terminate.
497 * @param TS The timestamp to use when terminating the thread.
499 KPRF_DECL_FUNC(void, TerminateThread
)(KPRF_TYPE(P
,HDR
) pHdr
, KPRF_TYPE(P
,THREAD
) pThread
, KU64 TS
)
501 if (pThread
->enmState
== KPRF_TYPE(,THREADSTATE_TERMINATED
))
503 pThread
->enmState
= KPRF_TYPE(,THREADSTATE_TERMINATED
);
506 * Unwind the entire stack.
508 if (pThread
->offStack
)
510 KPRF_TYPE(P
,STACK
) pStack
= KPRF_OFF2PTR(P
,STACK
, pThread
->offStack
, pHdr
);
511 for (KU32 cFrames
= pStack
->cFrames
; cFrames
> 0; cFrames
--)
512 KPRF_NAME(UnwindOne
)(pHdr
, pStack
, 0, TS
);
517 pThread
->offStack
= 0;
519 pStack
->offThread
= 0;
521 KPRF_THREADS_UNLOCK();
527 * Deregister (terminate) the current thread.
529 KPRF_DECL_FUNC(void, DeregisterThread
)(void)
531 KU64 TS
= KPRF_NOW();
534 * Get the header, then get the thread and mark it terminated.
535 * (It doesn't matter whether we're active or not.)
537 KPRF_TYPE(P
,HDR
) pHdr
= KPRF_GET_HDR();
541 KPRF_TYPE(P
,THREAD
) pThread
= KPRF_GET_THREAD();
542 KPRF_SET_THREAD(NULL
);
545 KPRF_NAME(TerminateThread
)(pHdr
, pThread
, TS
);
550 * Resumes / restarts a thread.
552 * @param fReset If set the stack is reset.
554 KPRF_DECL_FUNC(void, ResumeThread
)(int fReset
)
556 KU64 TS
= KPRF_NOW();
559 * Get the header, then get the thread and mark it terminated.
560 * (It doesn't matter whether we're active or not.)
562 KPRF_TYPE(P
,HDR
) pHdr
= KPRF_GET_HDR();
566 KPRF_TYPE(P
,THREAD
) pThread
= KPRF_GET_THREAD();
569 if (pThread
->enmState
!= KPRF_TYPE(,THREADSTATE_SUSPENDED
))
573 * Reset (unwind) the stack?
575 KPRF_TYPE(P
,STACK
) pStack
= KPRF_OFF2PTR(P
,STACK
, pThread
->offStack
, pHdr
);
578 KU32 cFrames
= pStack
->cFrames
;
579 while (cFrames
-- > 0)
580 KPRF_NAME(UnwindOne
)(pHdr
, pStack
, 0, TS
);
583 * If we've got any thing on the stack, we'll have to stop the sleeping period.
585 else if (pStack
->cFrames
> 0)
587 KPRF_TYPE(P
,FRAME
) pFrame
= &pStack
->aFrames
[pStack
->cFrames
- 1];
589 /* update the sleeping time and set the start of the new top-of-stack period. */
590 pFrame
->SleepTicks
+= TS
- pFrame
->OnTopOfStackStart
;
591 pFrame
->OnTopOfStackStart
= TS
;
593 /** @todo we're not accounting overhead here! */
596 * We're done, switch the thread to active state.
598 pThread
->enmState
= KPRF_TYPE(,THREADSTATE_ACTIVE
);
603 * Suspend / completes a thread.
605 * The thread will be in a suspend state where the time will be accounted for as sleeping.
607 * @param fUnwind If set the stack is unwound and the thread statistics updated.
609 KPRF_DECL_FUNC(void, SuspendThread
)(int fUnwind
)
611 KU64 TS
= KPRF_NOW();
614 * Get the header, then get the thread and mark it terminated.
615 * (It doesn't matter whether we're active or not.)
617 KPRF_TYPE(P
,HDR
) pHdr
= KPRF_GET_HDR();
621 KPRF_TYPE(P
,THREAD
) pThread
= KPRF_GET_THREAD();
624 if ( pThread
->enmState
!= KPRF_TYPE(,THREADSTATE_ACTIVE
)
625 && pThread
->enmState
!= KPRF_TYPE(,THREADSTATE_OVERFLOWED
)
626 && (pThread
->enmState
!= KPRF_TYPE(,THREADSTATE_SUSPENDED
) || fUnwind
))
629 pThread
->enmState
= KPRF_TYPE(,THREADSTATE_SUSPENDED
);
634 KPRF_TYPE(P
,STACK
) pStack
= KPRF_OFF2PTR(P
,STACK
, pThread
->offStack
, pHdr
);
637 KU32 cFrames
= pStack
->cFrames
;
638 while (cFrames
-- > 0)
639 KPRF_NAME(UnwindOne
)(pHdr
, pStack
, 0, TS
);
642 * If we've got any thing on the stack, we'll have to record the sleeping period
643 * of the thread. If not we'll ignore it (for now at least).
645 else if (pStack
->cFrames
> 0)
647 KPRF_TYPE(P
,FRAME
) pFrame
= &pStack
->aFrames
[pStack
->cFrames
- 1];
649 /* update the top of stack time and set the start of the sleep period. */
650 pFrame
->OnTopOfStackTicks
+= TS
- pFrame
->OnTopOfStackStart
;
651 pFrame
->OnTopOfStackStart
= TS
;
654 /** @todo we're not accounting overhead here! */