📄 profiler.c
字号:
/*++Copyright (c) 1995-2000 Microsoft Corporation. All rights reserved.Module Name: Abstract: This file implements the NK kernel profiler support.Functions:Notes: --*/#define C_ONLY#include "kernel.h"#include "romldr.h"#include "xtime.h"#include <profiler.h>#define PROFILEMSG(cond,printf_exp) \ ((cond)?(NKDbgPrintfW printf_exp),1:0)#ifdef KCALL_PROFILE#include "kcall.h"KPRF_t KPRFInfo[MAX_KCALL_PROFILE];#ifdef NKPROF#define KCALL_NextThread 10 // Next thread's entry in array#endif#endifint (*PProfileInterrupt)(void)=NULL; // pointer to profiler ISR, // set in platform profiler codeBOOL bProfileTimerRunning=FALSE; // this variable is used in OEMIdle(), so must be defined even in a non-profiling kernel#ifdef NKPROFBOOL bProfileObjCall=FALSE; // object call profiling flag//volatile BOOL bProfileKCall=FALSE; // kcall profiling flagBOOL bProfileKCall=FALSE; // kcall profiling flagBOOL bProfileBuffer=FALSE; // profile to buffer flagextern void SC_DumpKCallProfile(DWORD bReset);extern BOOL ProfilerAllocBuffer(void);extern BOOL ProfilerFreeBuffer(void);#endif#ifdef WINCECODETESTextern void CT_Profile(DWORD Start);#endif// SC_ProfileSyscall - profiler syscall supportvoid SC_ProfileSyscall(LPXT lpXt){#ifdef NKPROF static int scPauseContinueCalls = 0; static BOOL bStart = FALSE; if (lpXt->dwOp == XTIME_PROFILE_DATA) { if(!lpXt->dwTime[0]) // if starting profiling { if(lpXt->dwTime[2] & PROFILE_CONTINUE) { if(bStart) { ++scPauseContinueCalls; // start profiler timer on 0 to 1 transition if(1 == scPauseContinueCalls) { OEMProfileTimerEnable(lpXt->dwTime[1]); bProfileTimerRunning = TRUE; } } } else if(lpXt->dwTime[2] & PROFILE_PAUSE) { if(bStart) { --scPauseContinueCalls; // start profiler timer on 1 to 0 transition if(!scPauseContinueCalls) { OEMProfileTimerDisable(); bProfileTimerRunning = FALSE; } } } else { OEMProfileTimerDisable(); // disable profiler timer bProfileTimerRunning = FALSE; bStart = TRUE; ++scPauseContinueCalls; dwProfileCount=0; // clear profile count ClearProfileHits(); // reset all profile counts // check object call profile flag if (lpXt->dwTime[2] & PROFILE_OBJCALL) bProfileObjCall=TRUE; else bProfileObjCall=FALSE; // check kcall profile flag if (lpXt->dwTime[2] & PROFILE_KCALL) { bProfileKCall=TRUE; return; } else { bProfileKCall=FALSE; } // check profile to buffer flag if (lpXt->dwTime[2] & PROFILE_BUFFER) { bProfileBuffer=TRUE; if (!ProfilerAllocBuffer()) { bProfileBuffer=FALSE; } } else { bProfileBuffer=FALSE; } if (bProfileObjCall) { // We don't need special timers for this case. return; } // start profiler timer if(!(lpXt->dwTime[2] & PROFILE_STARTPAUSED)) { // start profiler timer OEMProfileTimerEnable(lpXt->dwTime[1]); bProfileTimerRunning = TRUE; } } } else { if(bProfileTimerRunning) { OEMProfileTimerDisable(); // disable profiler timer bProfileTimerRunning = FALSE; } // stopping profile bStart = FALSE; if (bProfileKCall) // if profile KCALL enabled { bProfileKCall=FALSE; // dump the KCall profile data SC_DumpKCallProfile(TRUE); } else // else display profile hit report { ProfilerReport(); } if(bProfileBuffer) { ProfilerFreeBuffer(); bProfileBuffer = FALSE; } bProfileObjCall = FALSE; scPauseContinueCalls = 0; } }#endif#ifdef WINCECODETEST if (lpXt->dwOp == XTIME_CODETEST) { // Start/Stop CodeTEST profiling CT_Profile(lpXt->dwTime[0]); }#endif}void SC_TurnOnProfiling(void) { DEBUGMSG(ZONE_ENTRY,(L"SC_TurnOnProfiling entry\r\n")); SET_PROFILE(pCurThread);#if SHx ProfileFlag = 1; // profile status#endif DEBUGMSG(ZONE_ENTRY,(L"SC_TurnOnProfiling exit\r\n"));}void SC_TurnOffProfiling(void) { DEBUGMSG(ZONE_ENTRY,(L"SC_TurnOffProfiling entry\r\n")); CLEAR_PROFILE(pCurThread);#if SHx ProfileFlag = 0; // profile status#endif DEBUGMSG(ZONE_ENTRY,(L"SC_TurnOffProfiling exit\r\n"));}#ifdef KCALL_PROFILEvoid GetKCallProfile(KPRF_t *pkprf, int loop, BOOL bReset) { KCALLPROFON(26); memcpy(pkprf,&KPRFInfo[loop],sizeof(KPRF_t)); if (bReset && (loop != 26)) memset(&KPRFInfo[loop],0,sizeof(KPRF_t)); KCALLPROFOFF(26); if (bReset && (loop == 26)) memset(&KPRFInfo[loop],0,sizeof(KPRF_t));}#endif//// Convert the number of ticks to microseconds.//static DWORD local_ScaleDown(DWORD dwIn){ LARGE_INTEGER liFreq; SC_QueryPerformanceFrequency(&liFreq); return ((DWORD) (((__int64) dwIn * 1000000) / liFreq.LowPart));}void SC_DumpKCallProfile(DWORD bReset) {#ifdef KCALL_PROFILE extern DWORD local_ScaleDown(DWORD); int loop; KPRF_t kprf; DWORD min = 0xffffffff, max = 0, total = 0, calls = 0;#ifdef NKPROF calls = local_ScaleDown(1000); PROFILEMSG(1,(L"Resolution: %d.%3.3d usec per tick\r\n",calls/1000,calls%1000)); KCall((PKFN)GetKCallProfile,&kprf,KCALL_NextThread,bReset); PROFILEMSG(1,(L"NextThread: Calls=%u Min=%u Max=%u Ave=%u\r\n", kprf.hits,local_ScaleDown(kprf.min), local_ScaleDown(kprf.max),kprf.hits ? local_ScaleDown(kprf.total)/kprf.hits : 0)); for (loop = 0; loop < MAX_KCALL_PROFILE; loop++) { if (loop != KCALL_NextThread) { KCall((PKFN)GetKCallProfile,&kprf,loop,bReset); if (kprf.max > max) max = kprf.max; total+=kprf.total; calls+= kprf.hits; } } PROFILEMSG(1,(L"Other Kernel calls: Max=%u Avg=%u\r\n",max,calls ? local_ScaleDown(total)/calls : 0));#else calls = local_ScaleDown(1000); PROFILEMSG(1,(L"Resolution: %d.%3.3d usec per tick\r\n",calls/1000,calls%1000)); PROFILEMSG(1,(L"Index Entrypoint Calls uSecs Min Max Ave\r\n")); for (loop = 0; loop < MAX_KCALL_PROFILE; loop++) { KCall((PKFN)GetKCallProfile,&kprf,loop,bReset); PROFILEMSG(1,(L"%5d %-30s %8d %10d %6d %6d %6d\r\n", loop, pKCallName[loop], kprf.hits,local_ScaleDown(kprf.total),local_ScaleDown(kprf.min), local_ScaleDown(kprf.max),kprf.hits ? local_ScaleDown(kprf.total)/kprf.hits : 0)); if (kprf.min && (kprf.min < min)) min = kprf.min; if (kprf.max > max) max = kprf.max; calls += kprf.hits; total += kprf.total; } PROFILEMSG(1,(L"\r\n")); PROFILEMSG(1,(L" %-30s %8d %10d %6d %6d %6d\r\n", L"Summary", calls,local_ScaleDown(total),local_ScaleDown(min),local_ScaleDown(max),calls ? local_ScaleDown(total)/calls : 0));#endif#endif}#ifdef CELOG//------------------------------------------------------------------------------//------------------------------------------------------------------------------// CELOG RESYNC API: This function generates a series of calls to CeLogData, // to log all currently-existing processes and threads.//// This buffer is used during resync to minimize stack usageBYTE g_pCeLogSyncBuffer[(MAX_PATH * sizeof(WCHAR)) + max(sizeof(CEL_PROCESS_CREATE), sizeof(CEL_THREAD_CREATE))];// Helper func to generate a celog process create event, minimizing stack usage.// At most one of lpProcNameA, lpProcNameW can be non-null!_inline void CELOG_SyncProcess(HANDLE hProcess, LPSTR lpProcNameA, LPWSTR lpProcNameW){ PCEL_PROCESS_CREATE pcl = (PCEL_PROCESS_CREATE) &g_pCeLogSyncBuffer; WORD wLen = 0; pcl->hProcess = hProcess; if (lpProcNameW) { wLen = strlenW(lpProcNameW) + 1; kstrcpyW(pcl->szName, lpProcNameW); } else if (lpProcNameA) { wLen = strlen(lpProcNameA) + 1; KAsciiToUnicode(pcl->szName, lpProcNameA, MAX_PATH); } CELOGDATA(TRUE, CELID_PROCESS_CREATE, (PVOID) pcl, (WORD) (sizeof(CEL_PROCESS_CREATE) + (wLen * sizeof(WCHAR))), 0, CELZONE_PROCESS);}// Helper func to generate a celog thread create event, minimizing stack usage._inline void CELOG_SyncThread(PTHREAD pThread, HANDLE hProcess){ if (pThread) { PCEL_THREAD_CREATE pcl = (PCEL_THREAD_CREATE) &g_pCeLogSyncBuffer; WORD wLen = 0; pcl->hProcess = hProcess; pcl->hThread = pThread->hTh; // Look up the thread's function name and module handle. GetThreadName(pThread, &(pcl->hModule), pcl->szName); if (pcl->szName[0] != 0) { wLen = strlenW(pcl->szName) + 1; } CELOGDATA(TRUE, CELID_THREAD_CREATE, (PVOID) pcl, (WORD)(sizeof(CEL_THREAD_CREATE) + (wLen * sizeof(WCHAR))), 0, CELZONE_THREAD); }}BOOL CeLogReSync(){ DWORD dwProc; // KCall so nobody changes ProcArray out from under us if (!InSysCall()) { return KCall((PKFN)CeLogReSync); } KCALLPROFON(74); // Since we're in a KCall, we must limit stack usage, so we can't call // CELOG_ProcessCreate or CELOG_ThreadCreate -- instead use CELOG_Sync*. for (dwProc = 0; dwProc < 32; dwProc++) { if (ProcArray[dwProc].dwVMBase) { THREAD* pThread; // Log the process name. Since we are in a KCall, we must use the // table of contents for everything besides nk.exe, because we // cannot reach into the memory owned by other processes. if (dwProc == 0) { // NK.EXE CELOG_SyncProcess(ProcArray[dwProc].hProc, NULL, ProcArray[dwProc].lpszProcName); } else if ((ProcArray[dwProc].oe.filetype == FT_ROMIMAGE) && (ProcArray[dwProc].oe.tocptr)) { // Get the name from the TOC CELOG_SyncProcess(ProcArray[dwProc].hProc, ProcArray[dwProc].oe.tocptr->lpszFileName, NULL); } else { // We can't do anything if the process isn't nk.exe and isn't // in the table of contents. CELOG_SyncProcess(ProcArray[dwProc].hProc, NULL, NULL); } // Log the existence of each of the process' threads as a ThreadCreate pThread = ProcArray[dwProc].pTh; while (pThread != NULL) { CELOG_SyncThread(pThread, pThread->pOwnerProc->hProc); pThread = pThread->pNextInProc;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -