📄 profiler.c
字号:
} while ((dwNumFrames == PROFILER_MAX_STACK_FRAME)
&& (KGetLastError(pCurThread) == ERROR_INSUFFICIENT_BUFFER));
// Restore thread LastError
KSetLastError(pCurThread, dwLastError);
return TRUE;
}
#endif // defined(x86)
#endif // NKPROF
//------------------------------------------------------------------------------
// GetEPC - Get exception program counter
//
// Output: returns EPC, zero if cpu not supported
//------------------------------------------------------------------------------
DWORD GetEPC(void) {
#if defined(x86) && defined(NKPROF)
extern PTHREAD pthFakeStruct;
PTHREAD pTh;
if (KData.cNest < 0) {
pTh = pthFakeStruct;
} else {
pTh = pCurThread;
}
return(GetThreadIP(pTh));
#elif defined(ARM)
// -- NOT SUPPORTED ON ARM --
// The address is not saved to the thread context on ARM, so GetEPC
// cannot be used within an ISR. Instead use the "ra" value that is
// passed to OEMInterruptHandler.
return 0;
#else
return(GetThreadIP(pCurThread));
#endif
}
//------------------------------------------------------------------------------
// ProfilerHit - OEMProfilerISR calls this routine to record profile hit
//
// Input: ra - interrupt return address
//
// if buffer profiling, save ra and pCurProc->oe.tocptr in buffer
// else do symbol lookup in ISR
//------------------------------------------------------------------------------
void
ProfilerHit(
unsigned int ra
)
{
#ifdef NKPROF
OEMProfilerData data;
data.ra = ra;
data.dwBufSize = 0;
ProfilerHitEx(&data);
#endif // NKPROF
}
//------------------------------------------------------------------------------
// ProfilerHitEx - Generalized version of ProfilerHit. A hardware-specific
// profiler ISR calls this routine to record a buffer of profiling data.
// No lookups done during this routine -- just copy the data into a buffer and
// return.
// Input: pData - OEM-specified buffer of profiling information (RA may be fixed up)
//------------------------------------------------------------------------------
void
ProfilerHitEx(
OEMProfilerData *pData
)
{
#ifdef NKPROF
if (!RunList.pth && !RunList.pRunnable) { // IDLE
// CeLog should record idle hits, but other modes should just
// increment a counter and exit.
g_ProfilerState_dwSamplesInIdle++;
if (!(g_dwProfilerFlags & PROFILE_CELOG)) {
return;
}
pData->ra = (DWORD) IDLE_STATE;
}
if (g_dwProfilerFlags & PROFILE_CELOG) {
if (IsCeLogStatus(CELOGSTATUS_ENABLED_PROFILE)) {
// Fixup RA
if (IsKernelVa(pData->ra)) { // if high bit set, this is NK
pData->ra &= 0xdfffffff; // mask off uncached bit
} else {
pData->ra = (DWORD)MapPtrProc(pData->ra, pCurProc);
}
// Send data to celog
if (g_dwProfilerFlags & PROFILE_OEMDEFINED) {
g_pfnCeLogData(FALSE, CELID_OEMPROFILER_HIT, pData,
pData->dwBufSize + 2*sizeof(DWORD), // dwBufSize does not include header; can't use sizeof(OEMProfilerData) because the 0-byte array has nonzero size
0, CELZONE_PROFILER, 0, FALSE);
} else {
g_pfnCeLogData(FALSE, CELID_MONTECARLO_HIT, &(pData->ra), sizeof(DWORD), 0,
CELZONE_PROFILER, 0, FALSE);
}
#if defined(x86) // Callstack capture is only supported on x86 right now
// Send call stack data if necessary
if ((g_dwProfilerFlags & PROFILE_CALLSTACK)
&& (RunList.pth || RunList.pRunnable) // Not IDLE_STATE - idle has no stack
&& !(KData.cNest < 0)) { // Not a nested interrupt or KCall - no way to get stack in those cases?
LogCallStack();
}
#endif
}
} else if (g_dwProfilerFlags & (PROFILE_BUFFER)) {
//
// If profiling to buffer and there is still room in the buffer
//
if (g_ProfilerState_dwSamplesRecorded < g_dwProfileBufNumEntries) {
PPROFBUFENTRY pEntry;
if (IsKernelVa(pData->ra)) // if high bit set, this is NK
pData->ra &= 0xdfffffff; // mask off uncached bit
pEntry = GetEntryPointer(g_ProfilerState_dwSamplesRecorded);
g_ProfilerState_dwSamplesRecorded++;
//
// Record an entry point
//
pEntry->ra = pData->ra;
pEntry->pte = pCurProc->oe.tocptr;
} else {
//
// No place to record this hit. Let's remember how many we dropped.
//
g_ProfilerState_dwSamplesDropped++;
PROFILEMSG(ZONE_UNACCOUNTED, (TEXT("Hit dropped (buffer full), ra=0x%08x\r\n"), pData->ra));
}
} else {
// No buffer. Just lookup the symbol now.
//
g_ProfilerState_dwSamplesRecorded++;
ProfilerSymbolHit(pData->ra, pCurProc->oe.tocptr);
}
#endif // NKPROF
}
//------------------------------------------------------------------------------
// SC_ProfileSyscall - Turns profiler on and off. The workhorse behind
// ProfileStart(), ProfileStartEx(), ProfileStop() and ProfileCaptureStatus().
//------------------------------------------------------------------------------
void
SC_ProfileSyscall(
DWORD* lpdwArg
)
{
#ifdef NKPROF
static int scPauseContinueCalls = 0;
static BOOL bStart = FALSE;
static DWORD dwCeLogZoneUser, dwCeLogZoneCE, dwCeLogZoneProcess;
const ProfilerControl* pControl = (const ProfilerControl*) lpdwArg;
if (pControl
&& (pControl->dwVersion == 1)
&& (pControl->dwReserved == 0)) {
if (pControl->dwOptions & PROFILE_START) {
// ProfileStart() / ProfileStartEx()
if (pControl->dwOptions & PROFILE_CONTINUE) {
if (bStart && ((g_dwProfilerFlags & (PROFILE_OBJCALL | PROFILE_KCALL)) == 0)) {
++scPauseContinueCalls;
// Start profiler timer on 0 to 1 transition
if (1 == scPauseContinueCalls) {
if (g_dwProfilerFlags & PROFILE_OEMDEFINED) {
// OEM-specific profiler
OEMIoControl(IOCTL_HAL_OEM_PROFILER,
(ProfilerControl*) pControl, // non-const
sizeof(ProfilerControl) + pControl->OEM.dwControlSize,
NULL, 0, NULL);
} else {
// Monte Carlo profiling
// Set system state counters to running (total) values
g_ProfilerState_dwTLBCount = dwCeLogTLBMiss - g_ProfilerState_dwTLBCount;
g_ProfilerState_dwTickCount = GetTickCount() - g_ProfilerState_dwTickCount;
OEMProfileTimerEnable(pControl->Kernel.dwUSecInterval);
bProfileTimerRunning = TRUE;
}
}
}
} else if (pControl->dwOptions & PROFILE_PAUSE) {
if (bStart && ((g_dwProfilerFlags & (PROFILE_OBJCALL | PROFILE_KCALL)) == 0)) {
--scPauseContinueCalls;
// Stop profiler timer on 1 to 0 transition
if (!scPauseContinueCalls) {
if (g_dwProfilerFlags & PROFILE_OEMDEFINED) {
// OEM-specific profiler
OEMIoControl(IOCTL_HAL_OEM_PROFILER,
(ProfilerControl*) pControl, // non-const
sizeof(ProfilerControl) + pControl->OEM.dwControlSize,
NULL, 0, NULL);
} else {
// Monte Carlo profiling
OEMProfileTimerDisable();
bProfileTimerRunning = FALSE;
// Set system state counters to paused (elapsed) values
g_ProfilerState_dwTLBCount = dwCeLogTLBMiss - g_ProfilerState_dwTLBCount;
g_ProfilerState_dwTickCount = GetTickCount() - g_ProfilerState_dwTickCount;
}
}
}
} else {
// Protect against multiple starts
if (bStart) {
PROFILEMSG(1, (TEXT("Kernel Profiler: Ignoring multiple profiler starts\r\n")));
return;
}
// Protect against running a profiling kernel on an unprofiled image
if (((pTOC->ulProfileLen == 0) || (pTOC->ulProfileOffset == 0))
&& ((pControl->dwOptions & PROFILE_CELOG) == 0)) { // Allow profiling with celog even if no symbols
PROFILEMSG(1, (TEXT("Kernel Profiler: Unable to start, PROFILE=OFF in config.bib!\r\n")));
return;
}
// Pre-compute information about all of the XIP regions for
// faster access while profiling
if ((g_dwNumROMModules == 0) || (g_dwFirstROMDLL == 0)) {
ROMChain_t *pROM = ROMChain;
g_dwFirstROMDLL = (DWORD)-1;
while (pROM) {
// Figure out the module index (wrt first XIP region) of nk.exe
// NK is the first module of the pTOC
if (pROM->pTOC == pTOC) {
g_dwNKIndex = g_dwNumROMModules;
}
// Count the total number of modules
g_dwNumROMModules += pROM->pTOC->nummods;
// Find the lowest ROM DLL address
if (pROM->pTOC->dllfirst < g_dwFirstROMDLL) {
g_dwFirstROMDLL = pROM->pTOC->dllfirst;
}
pROM = pROM->pNext;
}
DEBUGCHK(g_dwFirstROMDLL != (DWORD)-1);
}
if ((pControl->dwOptions & PROFILE_CELOG) && !IsCeLogStatus(CELOGSTATUS_ENABLED_ANY)) {
PROFILEMSG(1, (TEXT("Kernel Profiler: Unable to start, CeLog is not loaded!\r\n")));
return;
}
// Debug output so the user knows exactly what's going on
if (pControl->dwOptions & PROFILE_KCALL) {
PROFILEMSG(1, (TEXT("Kernel Profiler: Gathering KCall data\r\n")));
} else {
PROFILEMSG(1, (TEXT("Kernel Profiler: Gathering %s data in %s mode\r\n"),
(pControl->dwOptions & PROFILE_OEMDEFINED) ? TEXT("OEM-Defined")
: (pControl->dwOptions & PROFILE_OBJCALL) ? TEXT("ObjectCall")
: TEXT("MonteCarlo"),
(pControl->dwOptions & PROFILE_CELOG) ? TEXT("CeLog")
: ((pControl->dwOptions & PROFILE_BUFFER) ? TEXT("buffered")
: TEXT("unbuffered"))));
}
OEMProfileTimerDisable(); // disable profiler timer
bProfileTimerRunning = FALSE;
bStart = TRUE;
++scPauseContinueCalls;
ClearProfileHits(); // reset all profile counts
g_dwProfilerFlags = 0;
//
// Determine the storage mode for the data
//
if (pControl->dwOptions & PROFILE_CELOG) {
// Disable CeLog general logging; log only profile events
CeLogEnableStatus(CELOGSTATUS_ENABLED_PROFILE);
// Make sure the correct zones are turned on; save the
// old zone settings to restore later
SC_CeLogGetZones(&dwCeLogZoneUser, &dwCeLogZoneCE,
&dwCeLogZoneProcess, NULL);
SC_CeLogSetZones(0xFFFFFFFF, CELZONE_PROFILER, 0xFFFFFFFF);
SC_CeLogReSync();
// Log the start event
if (pControl->dwOptions & PROFILE_OEMDEFINED) {
g_pfnCeLogData(TRUE, CELID_PROFILER_START, pControl,
sizeof(ProfilerControl) + pControl->OEM.dwControlSize, 0,
CELZONE_PROFILER, 0, FALSE);
} else {
g_pfnCeLogData(TRUE, CELID_PROFILER_START, pControl,
sizeof(ProfilerControl), 0,
CELZONE_PROFILER, 0, FALSE);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -