📄 profiler.c
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// This source code is licensed under Microsoft Shared Source License
// Version 1.0 for Windows CE.
// For a copy of the license visit http://go.microsoft.com/fwlink/?LinkId=3223.
//
/*++
Module Name:
Abstract:
This file implements the NK kernel profiler support.
Functions:
Notes:
--*/
#define C_ONLY
#include "kernel.h"
#include <profiler.h>
#define PROFILEMSG(cond,printf_exp) \
((cond)?(NKDbgPrintfW printf_exp),1:0)
// Turning this zone on can break unbuffered mode, and can break buffered mode
// if the buffer overruns
#define ZONE_UNACCOUNTED 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
#endif // KCALL_PROFILE
int (*PProfileInterrupt)(void) = NULL; // pointer to profiler ISR,
// set in platform profiler code
BOOL bProfileTimerRunning = FALSE; // this variable is used in OEMIdle(), so must be defined even in a non-profiling kernel
const volatile DWORD dwProfileBufferMax = 0; // Can be overwritten as a FIXUPVAR in config.bib
#ifdef NKPROF
// Used to track what mode the profiler is in
DWORD g_dwProfilerFlags; // ObjCall, KCall, Buffer, CeLog
// Used for symbol lookup
DWORD g_dwNumROMModules; // Number of modules in all XIP regions
DWORD g_dwFirstROMDLL; // First DLL address over all XIP regions
DWORD g_dwNKIndex; // Index (wrt first XIP region) of NK in PROFentry array
#if defined(x86) // Callstack capture is only supported on x86 right now
// Temp buffer used to hold callstack during profiler hit; not threadsafe but
// all profiler hits should be serialized
#define PROFILER_MAX_STACK_FRAME 50
static BYTE g_ProfilerStackBuffer[PROFILER_MAX_STACK_FRAME*sizeof(CallSnapshot)];
static CONTEXT g_ProfilerContext;
#endif // defined(x86)
// Total number of profiler samples is g_ProfilerState_dwSamplesRecorded + g_ProfilerState_dwSamplesDropped + g_ProfilerState_dwSamplesInIdle
static DWORD g_ProfilerState_dwSamplesRecorded; // Number of profiler hits that were recorded in the buffer or symbol list
static DWORD g_ProfilerState_dwSamplesDropped; // Number of profiler hits that were not recorded due to buffer overrun
//------------------------------------------------------------------------------
// System state that is tracked while the profiler is running in Monte Carlo mode.
DWORD g_ProfilerState_dwInterrupts; // Number of interrupts that occurred while profiling, including profiler interrupt
DWORD g_ProfilerState_dwProfilerIntsInTLB; // Number of TLB misses that had profiler interrupts pending
static DWORD g_ProfilerState_dwSamplesInIdle; // Number of profiler samples when no threads were scheduled
extern DWORD dwCeLogTLBMiss; // Number of TLB misses since boot (always running)
// System state counters that will run when profiling is paused are tracked by
// recording start values while running and elapsed values during the pause.
static DWORD g_ProfilerState_dwTLBCount; // Used to count TLB misses while profiling
static DWORD g_ProfilerState_dwTickCount; // Used to count ticks while profiling
//------------------------------------------------------------------------------
extern void SC_DumpKCallProfile(DWORD bReset);
extern BOOL ProfilerAllocBuffer(void);
extern BOOL ProfilerFreeBuffer(void);
#define NUM_MODULES 200 // max number of modules to display in report
// platform routine to disable profiler timer
extern void OEMProfileTimerDisable(void);
// platform routine to enable profiler timer
extern void OEMProfileTimerEnable(DWORD dwUSec);
typedef struct {
DWORD ra; // Interrupt program counter
TOCentry * pte;
} PROFBUFENTRY, *PPROFBUFENTRY;
#define ENTRIES_PER_PAGE (PAGE_SIZE / sizeof(PROFBUFENTRY))
//
// The number of bits to shift is a page shift - log2(sizeof(PROFBUFENTRY))
//
#if PAGE_SIZE == 4096
#define PROF_PAGE_SHIFT 9
#elif PAGE_SIZE == 2048
#define PROF_PAGE_SHIFT 8
#elif PAGE_SIZE == 1024
#define PROF_PAGE_SHIFT 7
#else
#error "Unsupported Page Size"
#endif // PAGE_SIZE
#define PROF_ENTRY_MASK ((1 << PROF_PAGE_SHIFT) - 1)
void ClearProfileHits(void); // clear all profile counters
#define PROFILE_BUFFER_MAX 256*1024*1024 // Absolute max of 256 MB
#define PAGES_IN_MAX_BUFFER PROFILE_BUFFER_MAX/PAGE_SIZE
#define BLOCKS_PER_SECTION 0x200
#define PAGES_PER_SECTION BLOCKS_PER_SECTION * PAGES_PER_BLOCK
PPROFBUFENTRY g_pPBE[PAGES_IN_MAX_BUFFER];
HANDLE g_hProfileBuf; // Handle of memory-mapped file
PDWORD g_pdwProfileBufStart; // Pointer to profile buffer (mmfile)
DWORD g_dwProfileBufNumPages; // Total number of pages in buffer
DWORD g_dwProfileBufNumEntries; // Total number of profiler hits that fit in buffer
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// Look up the profiler symbol entry for the given module
PROFentry* GetProfEntryFromTOCEntry(
TOCentry *tocexeptr
)
{
ROMChain_t *pROM = ROMChain; // Chain of ROM XIP regions
DWORD dwModIndex; // Index of module wrt first XIP region
TOCentry *tocptr; // table of contents entry pointer
// Look for the module in each successive XIP region, to find its index
// in the profiler symbol table.
dwModIndex = 0;
while (pROM) {
tocptr = (TOCentry *)(pROM->pTOC+1); // tocptr-> first entry in ROM
if ((tocexeptr >= tocptr)
&& (tocexeptr < tocptr + pROM->pTOC->nummods)) {
// Found it
dwModIndex += ((DWORD)tocexeptr - (DWORD)tocptr) / sizeof(TOCentry);
return (PROFentry *)/*pROM->*/pTOC->ulProfileOffset + dwModIndex; // All ROMs point to same profiler table
}
dwModIndex += pROM->pTOC->nummods;
pROM = pROM->pNext;
}
// Not found in any XIP region
return NULL;
}
/*
* ProfilerSymbolHit - Lookup profiler interrupt call address and record hit count
*
* Input: ra - interrupt return address
* tocexeptr - pCurProf.oe->tocexeptr at time of interrupt
*
* Attempts to find symbol address for the interrupt call address and increments
* hit counts for module and symbol if found.
*/
void ProfilerSymbolHit(
unsigned int ra,
TOCentry *tocexeptr
)
{
PROFentry *profptr; // profile entry pointer
if (IsKernelVa(ra)) {
// The hit is inside NK
ra &= 0xdfffffff; // mask off uncached bit
profptr = (PROFentry *)pTOC->ulProfileOffset + g_dwNKIndex;
} else if (ZeroPtr(ra) >= g_dwFirstROMDLL) {
// The hit is in a dll in the ROM
profptr = (PROFentry *)pTOC->ulProfileOffset+1;
while (!((ZeroPtr(ra) >= ZeroPtr(profptr->ulStartAddr)) && (ZeroPtr(ra) <= ZeroPtr(profptr->ulEndAddr)))) {
//
// This is not the module/section for this hit.
//
profptr++;
if (profptr >= (PROFentry *)pTOC->ulProfileOffset + g_dwNumROMModules) {
PROFILEMSG(ZONE_UNACCOUNTED, (TEXT("Hit dropped (ROM DLL not found), ra=0x%08x tocexeptr=0x%08x\r\n"), ra, tocexeptr));
return;
}
}
} else {
// This is an exe in the ROM, something loaded in RAM or unknown
profptr = GetProfEntryFromTOCEntry(tocexeptr);
if (!profptr) {
// Not found in any XIP region
PROFILEMSG(ZONE_UNACCOUNTED, (TEXT("Hit dropped (module not found), ra=0x%08x tocexeptr=0x%08x\r\n"), ra, tocexeptr));
return;
}
}
//
// We've picked a particular module and section (profptr), now verify the
// hit is really in bounds.
//
if ((ra < profptr->ulStartAddr) || (ra > profptr->ulEndAddr)) {
// Most likely a 0xDFFFnnnn hit inside PSL entry
PROFILEMSG(ZONE_UNACCOUNTED, (TEXT("Hit dropped (out of bounds), ra=0x%08x tocexeptr=0x%08x (0x%08x 0x%08x 0x%08x)\r\n"),
ra, tocexeptr, profptr, profptr->ulStartAddr, profptr->ulEndAddr));
return;
}
profptr->ulHits++; // increment hits for this module
if (profptr->ulNumSym) {
SYMentry *symptr; // profiler symbol entry pointer
unsigned int iSym; // symbol counter
SYMentry *pClosestSym; // nearest symbol entry found
// look up the symbol if module found
iSym = profptr->ulNumSym - 1;
symptr = (SYMentry*)profptr->ulHitAddress;
pClosestSym = symptr;
// Scan the whole list of symbols, looking for the closest match.
while (iSym) {
// Keep track of the closest symbol found
if (((unsigned int)symptr->ulFuncAddress <= ra)
&& (symptr->ulFuncAddress >= pClosestSym->ulFuncAddress)) {
pClosestSym = symptr;
}
iSym--;
symptr++;
}
pClosestSym->ulFuncHits++; // inc hit count for this symbol
}
return;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
static VOID
DoFreeBuffer(void)
{
BOOL fRet;
CALLBACKINFO cbi;
// Need to use callbacks because the kernel owns the map instead of the app
if (g_pdwProfileBufStart) {
// UnmapViewOfFile(g_pdwProfileBufStart);
cbi.hProc = ProcArray[0].hProc;
cbi.pfn = (FARPROC)UnmapViewOfFile;
cbi.pvArg0 = g_pdwProfileBufStart;
fRet = (BOOL)PerformCallBack(&cbi);
ASSERT(fRet);
g_pdwProfileBufStart = NULL;
}
if (g_hProfileBuf) {
// CloseHandle(g_hProfileBuf);
cbi.hProc = ProcArray[0].hProc;
cbi.pfn = (FARPROC)CloseHandle;
cbi.pvArg0 = g_hProfileBuf;
fRet = (BOOL)PerformCallBack(&cbi);
ASSERT(fRet);
g_hProfileBuf = NULL;
}
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
BOOL
ProfilerFreeBuffer(void)
{
DWORD dwMemSize;
BOOL fRet;
if (g_pdwProfileBufStart) {
dwMemSize = g_dwProfileBufNumPages * PAGE_SIZE;
fRet = SC_UnlockPages(g_pdwProfileBufStart, dwMemSize);
ASSERT(fRet);
}
DoFreeBuffer();
return TRUE;
}
#define SECTION_SIZE (1 << VA_SECTION)
//------------------------------------------------------------------------------
// Lock a large block with iterative calls to LockPages
//------------------------------------------------------------------------------
BOOL
ProfLockPages(
LPVOID lpvAddress,
DWORD cbSize,
PDWORD pPFNs,
int fOptions
)
{
BOOL fRet = FALSE;
DWORD section;
DWORD dwNumSections = (cbSize + SECTION_SIZE - 1) >> VA_SECTION;
for (section = 0; section < dwNumSections; section++) {
fRet = SC_LockPages(lpvAddress, cbSize < SECTION_SIZE ? cbSize : SECTION_SIZE, pPFNs, fOptions);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -