kDefs.h: Added risc-v to the K_ARCH_XXX defines.
[kstuff-mirror.git] / kProfiler2 / prfcore.cpp.h
blob1c48455b2688bf3e757df8c36650fcfb813f41aa
1 /* $Id$ */
2 /** @file
3 * kProfiler Mark 2 - Core Code Template.
4 */
6 /*
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
16 * conditions:
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.
32 /**
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();
43 KI32 iStart = 0;
44 KI32 iLast = pHdr->cFunctions - 1;
45 KI32 i = iLast / 2;
46 for (;;)
48 KU32 iFunction = pHdr->aiFunctions[i];
49 KPRF_TYPE(,IPTR) iDiff = uPC - paFunctions[iFunction].uEntryPtr;
50 if (!iDiff)
52 KPRF_FUNCS_READ_UNLOCK();
53 return &paFunctions[iFunction];
55 if (iLast == iStart)
56 break;
57 if (iDiff < 0)
58 iLast = i - 1;
59 else
60 iStart = i + 1;
61 if (iLast < iStart)
62 break;
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);
72 return NULL;
76 /**
77 * Unwind one frame.
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;
86 if (pStack->cFrames)
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;
96 else
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);
113 /* Time on stack */
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);
136 /* calls */
137 if (pFrame->cCalls)
138 KPRF_ATOMIC_ADD64(&pFunc->cCalls, pFrame->cCalls);
141 return pCurOverheadTicks;
146 * Unwinds the stack.
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
164 && iFrame > 0
165 && pFrame[-1].uFramePtr > uFramePtr))
166 return KPRF_NAME(UnwindOne)(pHdr, pStack, uPC, TS);
167 #else
168 if (uFramePtr == pFrame->uFramePtr)
169 return KPRF_NAME(UnwindOne)(pHdr, pStack, uPC, TS);
170 #endif
172 /* none? */
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);
178 pFrame--;
179 if ( iFrame > 0
180 #if K_OS == K_OS_WINDOWS && K_ARCH == K_ARCH_AMD64
181 && pFrame->uFramePtr <= uFramePtr
182 && pFrame[-1].uFramePtr > uFramePtr)
183 #else
184 && pFrame->uFramePtr <= uFramePtr)
185 #endif
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);
191 iFrame -= 2;
192 pFrame--;
193 #if K_OS == K_OS_WINDOWS && K_ARCH == K_ARCH_AMD64
194 while ( iFrame > 0
195 && pFrame->uFramePtr <= uFramePtr
196 && pFrame[-1].uFramePtr > uFramePtr)
197 #else
198 while ( iFrame >= 0
199 && pFrame->uFramePtr <= uFramePtr)
200 #endif
202 pCurOverheadTicks = KPRF_NAME(UnwindOne)(pHdr, pStack, uPC, TS);
203 iFrame--;
204 pFrame--;
208 return pCurOverheadTicks;
214 * Enter function.
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!
224 * @internal ?
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())
232 return NULL;
235 * Get the header and adjust input addresses.
237 KPRF_TYPE(P,HDR) pHdr = KPRF_GET_HDR();
238 if (!pHdr)
239 return NULL;
240 const KPRF_TYPE(,UPTR) uBasePtr = pHdr->uBasePtr;
241 if (uBasePtr)
243 uFramePtr -= uBasePtr;
244 uPC -= 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();
252 if (!pThread)
253 return NULL;
254 KPRF_TYPE(,THREADSTATE) enmThreadState = pThread->enmState;
255 if ( enmThreadState != KPRF_TYPE(,THREADSTATE_ACTIVE)
256 && enmThreadState != KPRF_TYPE(,THREADSTATE_OVERFLOWED)
258 return NULL;
259 if (pThread->uStackBasePtr < uFramePtr) /* ASSUMES stack direction */
261 pThread->cStackSwitchRejects++;
262 return NULL;
264 pThread->enmState = KPRF_TYPE(,THREADSTATE_SUSPENDED);
268 * Update the thread statistics.
270 pThread->cCalls++;
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];
282 if ( 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. */
285 #else
286 && pThread->uStackBasePtr >= uFramePtr /* ASSUMES stack direction */
287 && pFrame[-1].uFramePtr + (KPRF_BITS - 8) / 8 < uFramePtr) /* ASSUMES stack direction */
288 #endif
290 KPRF_NAME(UnwindInt)(pHdr, pStack, uPC, uFramePtr, TS);
291 iFrame = pStack->cFrames;
295 * Allocate a new stack frame.
297 if (iFrame >= pHdr->cMaxStackFrames)
299 /* overflow */
300 pThread->enmState = KPRF_TYPE(,THREADSTATE_OVERFLOWED);
301 pThread->cOverflows += enmThreadState != KPRF_TYPE(,THREADSTATE_OVERFLOWED);
302 return &pStack->aFrames[iFrame - 1].CurOverheadTicks;
304 pStack->cFrames++;
307 * Update the old top frame if any.
309 if (iFrame)
311 KPRF_TYPE(P,FRAME) pOldFrame = pFrame - 1;
312 pOldFrame->OnTopOfStackTicks += TS - pOldFrame->OnTopOfStackStart;
313 pOldFrame->cCalls++;
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;
325 pFrame->cCalls = 0;
326 pFrame->uFramePtr = uFramePtr;
329 * Find the relevant function.
331 KPRF_TYPE(P,FUNC) pFunc = KPRF_NAME(GetFunction)(pHdr, uPC);
332 if (pFunc)
334 pFrame->offFunction = KPRF_PTR2OFF(pFunc, pHdr);
335 pFunc->cOnStack++;
337 else
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;
350 * Leave function.
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!
359 * @internal
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())
367 return NULL;
370 * Get the header and adjust input addresses.
372 KPRF_TYPE(P,HDR) pHdr = KPRF_GET_HDR();
373 if (!pHdr)
374 return NULL;
375 const KPRF_TYPE(,UPTR) uBasePtr = pHdr->uBasePtr;
376 if (uBasePtr)
378 uFramePtr -= uBasePtr;
379 uPC -= 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();
387 if (!pThread)
388 return NULL;
389 KPRF_TYPE(,THREADSTATE) enmThreadState = pThread->enmState;
390 if ( enmThreadState != KPRF_TYPE(,THREADSTATE_ACTIVE)
391 && enmThreadState != KPRF_TYPE(,THREADSTATE_OVERFLOWED)
393 return NULL;
394 KPRF_TYPE(P,STACK) pStack = KPRF_OFF2PTR(P,STACK, pThread->offStack, pHdr);
395 if (!pStack->cFrames)
396 return NULL;
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();
425 if (!pHdr)
426 return NULL;
427 const KPRF_TYPE(,UPTR) uBasePtr = pHdr->uBasePtr;
428 if (uBasePtr)
429 uStackBasePtr -= uBasePtr;
433 * Allocate a thread and a stack.
435 KPRF_THREADS_LOCK();
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. */
445 pStack->cFrames = 0;
446 pStack->offThread = pHdr->offThreads + pHdr->cbThread * pHdr->cThreads++;
447 pHdr->cStacks++;
449 /* init the thread */
450 KPRF_TYPE(P,THREAD) pThread = KPRF_OFF2PTR(P,THREAD, pStack->offThread, pHdr);
451 pThread->ThreadId = KPRF_GET_THREADID();
452 unsigned i = 0;
453 if (pszName)
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;
462 pThread->cCalls = 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);
476 return pThread;
479 /* next */
480 pStack = KPRF_TYPE(P,STACK)(((KPRF_TYPE(,UPTR))pStack + pHdr->cbStack));
481 } while (--cLeft > 0);
484 KPRF_THREADS_UNLOCK();
485 return NULL;
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))
502 return;
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);
515 * Free the stack.
517 pThread->offStack = 0;
518 KPRF_THREADS_LOCK();
519 pStack->offThread = 0;
520 pHdr->cStacks--;
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();
538 if (!pHdr)
539 return;
541 KPRF_TYPE(P,THREAD) pThread = KPRF_GET_THREAD();
542 KPRF_SET_THREAD(NULL);
543 if (!pThread)
544 return;
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();
563 if (!pHdr)
564 return;
566 KPRF_TYPE(P,THREAD) pThread = KPRF_GET_THREAD();
567 if (!pThread)
568 return;
569 if (pThread->enmState != KPRF_TYPE(,THREADSTATE_SUSPENDED))
570 return;
573 * Reset (unwind) the stack?
575 KPRF_TYPE(P,STACK) pStack = KPRF_OFF2PTR(P,STACK, pThread->offStack, pHdr);
576 if (fReset)
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();
618 if (!pHdr)
619 return;
621 KPRF_TYPE(P,THREAD) pThread = KPRF_GET_THREAD();
622 if (!pThread)
623 return;
624 if ( pThread->enmState != KPRF_TYPE(,THREADSTATE_ACTIVE)
625 && pThread->enmState != KPRF_TYPE(,THREADSTATE_OVERFLOWED)
626 && (pThread->enmState != KPRF_TYPE(,THREADSTATE_SUSPENDED) || fUnwind))
627 return;
629 pThread->enmState = KPRF_TYPE(,THREADSTATE_SUSPENDED);
632 * Unwind the stack?
634 KPRF_TYPE(P,STACK) pStack = KPRF_OFF2PTR(P,STACK, pThread->offStack, pHdr);
635 if (fUnwind)
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! */