/*---------------------------------------------------------------------------* Project: TwlSDK - OS File: os_functionCost.c Copyright 2003-2008 Nintendo. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. $Date:: 2008-09-18#$ $Rev: 8573 $ $Author: okubata_ryoma $ *---------------------------------------------------------------------------*/ #include #if defined(OS_PROFILE_AVAILABLE) && defined(OS_PROFILE_FUNCTION_COST) #ifdef OS_NO_FUNCTIONCOST //---- dummy function SDK_WEAK_SYMBOL asm void __PROFILE_ENTRY( void ){ bx lr } SDK_WEAK_SYMBOL asm void __PROFILE_EXIT( void ){ bx lr } #else // ifdef OS_NO_FUNCTIONCOST //---- function prototype declaration extern u32 OSi_GetTick_noProfile(void); extern void OSi_SetSystemCallbackInSwitchThread(void *callback); //---- flag for thread initializing extern BOOL OSi_IsThreadInitialized; static OSFunctionCostInfo *OSi_GetFunctionCostInfo(void); static void OSi_SetStatistics(void *statBuf, OSFunctionCost *entry, OSFunctionCost *exit); static void OSi_CalcThreadInterval(register OSThread *saveThread, register OSThread *loadThread); //---- pointer to functionCost at not using threadSystem static OSFunctionCostInfo *OSi_DefaultFunctionCostInfo; //---- buffer check number #define OSi_FUNCTIONCOST_CHECKNUM_BUFFER 0xfddb597d //================================================================================ // ENABLE/DISABLE //================================================================================ /*---------------------------------------------------------------------------* Name: OS_EnableFunctionCost Description: enable functionCost Arguments: None Returns: previous status. TRUE if enable *---------------------------------------------------------------------------*/ #include asm BOOL OS_EnableFunctionCost( void ) { //---- check if functionCost not available stmfd sp!, {lr} bl OSi_GetFunctionCostInfo ldmfd sp!, {lr} cmp r0, #0 bxeq lr mov r2, r0 ldrh r0, [ r2, #OSFunctionCostInfo.enable ] mov r1, #1 strh r1, [ r2, #OSFunctionCostInfo.enable ] bx lr } #include /*---------------------------------------------------------------------------* Name: OS_DisableFunctionCost Description: disble functionCost Arguments: None Returns: previous status. TRUE if enable *---------------------------------------------------------------------------*/ #include asm BOOL OS_DisableFunctionCost( void ) { //---- check if functionCost not available stmfd sp!, {lr} bl OSi_GetFunctionCostInfo ldmfd sp!, {lr} cmp r0, #0 bxeq lr mov r2, r0 ldrh r0, [ r2, #OSFunctionCostInfo.enable ] mov r1, #0 strh r1, [ r2, #OSFunctionCostInfo.enable ] bx lr } #include /*---------------------------------------------------------------------------* Name: OS_RestoreFunctionCost Description: set status of functionCost Arguments: enable : TRUE if set to be enable, FALSE if not Returns: previous status. TRUE if enable *---------------------------------------------------------------------------*/ #include asm BOOL OS_RestoreFunctionCost( BOOL ) { //---- check if functionCost not available stmfd sp!, {r0, lr} bl OSi_GetFunctionCostInfo mov r1, r0 ldmfd sp!, {r0, lr} cmp r1, #0 bxeq lr ldrh r2, [ r1, #OSFunctionCostInfo.enable ] strh r0, [ r1, #OSFunctionCostInfo.enable ] mov r0, r2 bx lr } #include //================================================================================ // SETTING //================================================================================ /*---------------------------------------------------------------------------* Name: OS_InitFunctionCost Description: Initialize functionCost buffer, and start recording. Arguments: buf address to buffer for recording entering and exitting function. size size of buffer (by byte) Returns: None *---------------------------------------------------------------------------*/ #pragma profile off void OS_InitFunctionCost(void *buf, u32 size) { OSFunctionCostInfo **infoPtr; SDK_ASSERTMSG(((u32)buf & 3) == 0, "FunctionCost buffer must be aligned by 4"); SDK_ASSERTMSG((size >= OSi_COSTINFO_SIZEOF_HEADERPART + sizeof(OSFunctionCost)), "to small FunctionCost buffer"); //---- need Tick if (!OS_IsTickAvailable()) { OS_Panic("OS_InitFunctionCost: need tick.\n"); } #ifdef SDK_NO_THREAD //---- pointer to functionCostInfo is in static valiable infoPtr = &OSi_DefaultFunctionCostInfo; #else //---- pointer to requiredCostInfo is in OSThread structure SDK_ASSERTMSG(OS_IsThreadAvailable(), "OS_InitFunctionCost: thread system not initialized."); infoPtr = (OSFunctionCostInfo **)&(OS_GetCurrentThread()->profiler); OSi_SetSystemCallbackInSwitchThread(OSi_CalcThreadInterval); #endif *infoPtr = (OSFunctionCostInfo *)buf; //(*infoPtr)->current = (OSFunctionCost *)(*infoPtr)->array; // not need because clear function includes this. (*infoPtr)->limit = (OSFunctionCost *)((u32)buf + OSi_COSTINFO_SIZEOF_HEADERPART + OS_CalcFunctionCostLines(size) * sizeof(OSFunctionCost)); (*infoPtr)->enable = (u16)TRUE; #ifdef OSi_FUNCTIONCOST_THREAD_INTERVAL (*infoPtr)->breakTime = 0; #endif //---- clear function cost buffer OSi_ClearThreadFunctionCostBuffer(NULL); } #pragma profile reset //================================================================================ // UTILITY //================================================================================ /*---------------------------------------------------------------------------* Name: OS_CalcFunctionCostLines Description: calculate number of lines to be able to be allocated Arguments: size : FunctionCost buffer size same as argument of OS_InitFunctionCost()'s size Returns: number of lines to be able to allocate in 'size' byte *---------------------------------------------------------------------------*/ int OS_CalcFunctionCostLines(u32 size) { int n = (int)((size - OSi_COSTINFO_SIZEOF_HEADERPART) / sizeof(OSFunctionCost)); return (n < 0) ? 0 : n; } /*---------------------------------------------------------------------------* Name: OS_CalcFunctionCostBufferSize Description: calculate buffer size to allocate specified lines Arguments: lines : lines to want to allocate Returns: buffer size to need *---------------------------------------------------------------------------*/ u32 OS_CalcFunctionCostBufferSize(int lines) { SDK_ASSERT(lines >= 0); return OSi_COSTINFO_SIZEOF_HEADERPART + lines * sizeof(OSFunctionCost); } /*---------------------------------------------------------------------------* Name: OSi_ClearThreadFunctionCostBuffer Description: clear function cost buffer of specified thread Arguments: thread : NULL means current thread. Other is thread pointer. Returns: None *---------------------------------------------------------------------------*/ #pragma profile off void OSi_ClearThreadFunctionCostBuffer(OSThread *thread) { OSFunctionCostInfo *infoPtr; if (!thread) { infoPtr = OSi_GetFunctionCostInfo(); } else { infoPtr = (OSFunctionCostInfo *)(thread->profiler); } if (infoPtr) { infoPtr->current = infoPtr->array; //---- for check overflowing buffer limit *(u32 *)(infoPtr->limit - 1) = OSi_FUNCTIONCOST_CHECKNUM_BUFFER; } } #pragma profile reset /*---------------------------------------------------------------------------* Name: OS_GetFunctionCostInfo Description: get pointer to functionCostInfo structure Arguments: None Returns: pointer. NULL if not functionCost initialized *---------------------------------------------------------------------------*/ #include static asm OSFunctionCostInfo* OSi_GetFunctionCostInfo( void ) { #ifdef SDK_NO_THREAD ldr r0, =OSi_DefaultFunctionCostInfo ldr r0, [ r0, #0 ] bx lr #else ldr r0, =OSi_ThreadInfo ldr r0, [ r0, #OSThreadInfo.current ] // r0 = currentThread. 0 if no currentThread cmp r0, #0 ldrne r0, [ r0, #OSThread.profiler ] // r0 = functionCostInfo bx lr #endif } #include /*---------------------------------------------------------------------------* Name: OS_CheckFunctionCostBuffer Description: check if function cost buffer overflows the limit. Arguments: None Returns: FALSE if overflowed. TRUE if not *---------------------------------------------------------------------------*/ #ifndef OS_NO_FUNCTIONCOST #pragma profile off BOOL OS_CheckFunctionCostBuffer(void *buf) { OSFunctionCostInfo *infoPtr = buf; if (*(u32 *)(infoPtr->limit - 1) != OSi_FUNCTIONCOST_CHECKNUM_BUFFER) { //OS_Printf("OVER!\n"); return FALSE; } return TRUE; } #pragma profile reset #endif //================================================================================ // PROFILE //================================================================================ /*---------------------------------------------------------------------------* Name: __PROFILE_ENTRY Description: entry of function in profiler Arguments: None Returns: None *---------------------------------------------------------------------------*/ #include asm void __PROFILE_ENTRY( void ) { #ifndef SDK_NO_THREAD //---- check if thread system not initialized stmfd sp!, {r0} ldr r0, =OSi_IsThreadInitialized ldr r0, [ r0, #0 ] cmp r0, #0 ldmfd sp!, {r0} bxeq lr #endif stmfd sp!, {r1-r4, lr} //---- disable interrupt mrs r4, cpsr orr r1, r4, #HW_PSR_IRQ_DISABLE msr cpsr_c, r1 //---- check if functionCost not available stmfd sp!, {r0} bl OSi_GetFunctionCostInfo mov r1, r0 ldmfd sp!, {r0} cmp r1, #0 beq _exit //---- check if functionCost master enable ldrh r2, [ r1, #OSFunctionCostInfo.enable ] cmp r2, #0 beq _exit //---- get currentPtr of FunctionCost ldr r2, [ r1, #OSFunctionCostInfo.current ] cmp r2, #0 beq _exit //---- check if buffer is over limit ldr r3, [ r1, #OSFunctionCostInfo.limit ] cmp r2, r3 bpl _exit //---- store pointer to function name string str r0, [ r2, #OSFunctionCost.entry.name ] //---- store time stmfd sp!, {r1-r2} bl OSi_GetTick_noProfile ldmfd sp!, {r1-r2} str r0, [ r2, #OSFunctionCost.entry.time ] #ifdef OSi_FUNCTIONCOST_THREAD_INTERVAL //---- clear interval time mov r3, #0 str r3, [ r2, #OSFunctionCost.exit.interval ] #endif //---- increment pointer add r2, r2, #OSi_SIZEOF_FUNCTIONCOST str r2, [ r1, #OSFunctionCostInfo.current ] _exit: //---- restore interrupt msr cpsr_c, r4 ldmfd sp!, {r1-r4, lr} bx lr } #include /*---------------------------------------------------------------------------* Name: __PROFILE_EXIT Description: exit of function in profiler Arguments: None Returns: None *---------------------------------------------------------------------------*/ #include asm void __PROFILE_EXIT( void ) { stmfd sp!, {r0-r3, lr} #ifndef SDK_NO_THREAD //---- check if thread system not initialized ldr r0, =OSi_IsThreadInitialized ldr r0, [ r0, #0 ] cmp r0, #0 beq _exit #endif //---- disable interrupt mrs r3, cpsr orr r1, r3, #HW_PSR_IRQ_DISABLE msr cpsr_c, r1 //---- check if functionCost not available bl OSi_GetFunctionCostInfo mov r1, r0 cmp r1, #0 beq _exit_ri //---- check if functionCost master enable ldrh r2, [ r1, #OSFunctionCostInfo.enable ] cmp r2, #0 beq _exit_ri //---- get currentPtr of functionCost ldr r2, [ r1, #OSFunctionCostInfo.current ] // r2 = currentPtr of trace cmp r2, #0 beq _exit_ri //---- check if buffer is over limit ldr r0, [ r1, #OSFunctionCostInfo.limit ] cmp r2, r0 bpl _exit_ri //---- store endmark ldr r0, =OSi_FUNCTIONCOST_EXIT_TAG str r0, [ r2, #OSFunctionCost.exit.tag ] //---- store time stmfd sp!, {r1-r3} bl OSi_GetTick_noProfile ldmfd sp!, {r1-r3} str r0, [ r2, #OSFunctionCost.exit.time ] //---- increment pointer add r2, r2, #OSi_SIZEOF_FUNCTIONCOST str r2, [ r1, #OSFunctionCostInfo.current ] _exit_ri: //---- restore interrupt msr cpsr_c, r3 _exit: ldmfd sp!, {r0-r3, lr} bx lr } #include //================================================================================ // STATISTICS //================================================================================ /*---------------------------------------------------------------------------* Name: OS_InitStatistics Description: initialize statistics buffer Arguments: statBuf pointer to statistics buffer size size of statBuf Returns: None *---------------------------------------------------------------------------*/ #pragma profile off void OS_InitStatistics(void *statBuf, u32 size) { OSFunctionCostStatisticsInfo *infoPtr = statBuf; u32 *p; if (!infoPtr || size <= OSi_STATISTICS_LEAST_SIZE) { return; } //---- buffersize infoPtr->size = ((size - OSi_STATISTICS_SIZEOF_HEADERPART) / sizeof(OSFunctionCostStatistics)) * sizeof(OSFunctionCostStatistics) + OSi_STATISTICS_SIZEOF_HEADERPART; infoPtr->limit = (OSFunctionCostStatistics *)((u32)infoPtr + infoPtr->size); p = (u32 *)infoPtr->array; while ((u32)p < (u32)infoPtr->limit) { *p++ = 0; } //---- for check overflowing buffer limit *(u32 *)((OSFunctionCostStatistics *)p - 1) = OSi_FUNCTIONCOST_CHECKNUM_BUFFER; } #pragma profile reset /*---------------------------------------------------------------------------* Name: OSi_SetStatistics Description: modify result to statBuf Arguments: statBuf pointer to statistics buffer entry OSFunctionCost pointer of entering function exit OSFunctionCost pointer of exiting function Returns: None *---------------------------------------------------------------------------*/ #pragma profile off static void OSi_SetStatistics(void *statBuf, OSFunctionCost *entry, OSFunctionCost *exit) { OSFunctionCostStatisticsInfo *infoPtr = statBuf; OSFunctionCostStatistics *p = infoPtr->array; u32 time; //---- calc cost // (variables are u32, so even if entry>exit, it's ok) time = exit->exit.time - entry->entry.time; #ifdef OSi_FUNCTIONCOST_THREAD_INTERVAL //---- consider for interval by switching thread // (maybe always time>interval) time = (time > entry->entry.interval) ? (u32)(time - entry->entry.interval) : 0; #endif while ((u32)p < (u32)infoPtr->limit) { if (!p->name) { p->name = entry->entry.name; p->count = 1; p->time = time; break; } if (p->name == entry->entry.name) { p->count++; p->time += time; break; } p++; } } #pragma profile reset /*---------------------------------------------------------------------------* Name: OS_CalcThreadStatistics Description: calculate statistics to statBuf Arguments: statBuf : pointer to statistics buffer thread thread to calculate. if current or no thread then specify NULL Returns: None *---------------------------------------------------------------------------*/ #pragma profile off void OS_CalcThreadStatistics(void *statBuf, OSThread *thread) { OSFunctionCostInfo *infoPtr; OSFunctionCost *p, *array, *limit; if (!thread) { infoPtr = OSi_GetFunctionCostInfo(); } else { infoPtr = (OSFunctionCostInfo *)(thread->profiler); } if (!infoPtr || !(limit = infoPtr->current)) { return; } p = array = (OSFunctionCost *)&(infoPtr->array); while (p < limit) { u32 name = p->entry.name; if (name == OSi_FUNCTIONCOST_EXIT_TAG) { int cnt = 0; OSFunctionCost *bp = p - 1; #if 0 //---- for debug if (bp->entry.name == OSi_FUNCTIONCOST_EXIT_TAG) { #ifdef OSi_FUNCTIONCOST_THREAD_INTERVAL OS_Printf("----: %x %x\n", bp->entry.time, bp->entry.interval); #else OS_Printf("----: %x\n", bp->entry.time); #endif } else { #ifdef OSi_FUNCTIONCOST_THREAD_INTERVAL OS_Printf("%s: %x %x\n", bp->entry.name, bp->entry.time, bp->entry.interval); #else OS_Printf("%s: %x\n", bp->entry.name, bp->entry.time); #endif } #endif while (bp >= array) { if (bp->entry.name == OSi_FUNCTIONCOST_EXIT_TAG) { cnt++; } else { if (--cnt < 0) { OSi_SetStatistics(statBuf, bp, p); break; } } bp--; } } else { if (!name) continue; } p++; } //---- clear buffer OSi_ClearThreadFunctionCostBuffer(thread); } #pragma profile reset /*---------------------------------------------------------------------------* Name: OS_CheckStatisticsBuffer Description: check if statistics buffer overflows the limit. Arguments: None Returns: FALSE if overflowed. TRUE if not *---------------------------------------------------------------------------*/ #ifndef OS_NO_FUNCTIONCOST #pragma profile off BOOL OS_CheckStatisticsBuffer(void *statBuf) { OSFunctionCostStatisticsInfo *infoPtr = statBuf; if (*(u32 *)(infoPtr->limit - 1) != OSi_FUNCTIONCOST_CHECKNUM_BUFFER) { //OS_Printf("OVER!\n"); return FALSE; } return TRUE; } #pragma profile reset #endif //================================================================================ // DUMP //================================================================================ /*---------------------------------------------------------------------------* Name: OS_DumpStatistics Description: dump statistics of functionCost Arguments: statBuf buffer for statistics Returns: None *---------------------------------------------------------------------------*/ #pragma profile off void OS_DumpStatistics(void *statBuf) { OSFunctionCostStatisticsInfo *infoPtr = statBuf; OSFunctionCostStatistics *p = infoPtr->array; BOOL displayed = FALSE; #ifdef SDK_NO_THREAD return; #endif OS_Printf("---- functionCost statistics\n"); while ((u32)p < (u32)infoPtr->limit) { // not output buffer check number if (p->name && (p->name != OSi_FUNCTIONCOST_CHECKNUM_BUFFER)) { displayed = TRUE; OS_Printf("%s: count %d, cost %lld\n", p->name, p->count, p->time); } p++; } if (!displayed) { OS_Printf("no data\n"); } } #pragma profile reset /*---------------------------------------------------------------------------* Name: OSi_CalcThreadInterval Description: calculate interval from save context to load context for functionCost profiler. destroy r0-r3 registers Arguments: saveThread pointer to saving thread loadThread pointer to loading thread Returns: None *---------------------------------------------------------------------------*/ #include #pragma profile off static asm void OSi_CalcThreadInterval( register OSThread* saveThread, register OSThread* loadThread ) { #pragma unused( saveThread, loadThread ) #ifndef SDK_NO_THREAD #ifdef OSi_FUNCTIONCOST_THREAD_INTERVAL stmfd sp!, {lr} //==== about saving thread //---- check if functionCost not available cmp r0, #0 // r0 = saving thread beq _skip_saveThread ldr r2, [ r0, #OSThread.profiler ] // r2 = functionCostInfo cmp r2, #0 beq _skip_saveThread //---- check if functionCost master enable ldrh r3, [ r2, #OSFunctionCostInfo.enable ] // r3 = master enable cmp r3, #0 beq _skip_saveThread //---- store current time as break time stmfd sp!, {r1-r3} bl OSi_GetTick_noProfile // r0 = currentTime ldmfd sp!, {r1-r3} str r0, [ r2, #OSFunctionCostInfo.breakTime ] _skip_saveThread: //==== about loading thread //---- check if functionCost not available cmp r1, #0 // r1 = loadingThread beq _skip_loadThread ldr r2, [ r1, #OSThread.profiler ] // r2 = functionCostInfo cmp r2, #0 beq _skip_loadThread //---- check if functionCost master enable ldrh r3, [ r2, #OSFunctionCostInfo.enable ] // r3 = master enable cmp r3, #0 beq _skip_loadThread //---- get currentPtr of functionCost ldr r3, [ r2, #OSFunctionCostInfo.current ] // r3 = currentPtr of trace cmp r3, #0 beq _skip_loadThread sub r1, r3, #OSi_SIZEOF_FUNCTIONCOST // r1 = previous Ptr //---- check if buffer is below array add r0, r2, #OSFunctionCostInfo.array[0] // r0 = array Ptr cmp r1, r0 bmi _skip_loadThread //---- store current time as break time stmfd sp!, {r1-r3} bl OSi_GetTick_noProfile // r0 = currentTime ldmfd sp!, {r1-r3} ldr r3, [ r2, #OSFunctionCostInfo.breakTime ] // r3 = breakTime sub r3, r0, r3 // r3 = currentTime - breakTime = interval ldr r0, [ r1, #OSFunctionCost.entry.interval ] // add interval add r0, r0, r3 str r0, [ r1, #OSFunctionCost.entry.interval ] _skip_loadThread: ldmfd sp!, {lr} #endif // ifdef OSi_FUNCTIONCOST_THREAD_INTERVAL bx lr #endif // ifndef SDK_NO_THREAD } #pragma profile reset #include #endif // ifdef OS_NO_FUNCTIONCOST #endif // defined(OS_PROFILE_AVAILABLE) && defined(OS_PROFILE_FUNCTION_COST)