📄 profiler.c
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft end-user
// license agreement (EULA) under which you licensed this SOFTWARE PRODUCT.
// If you did not accept the terms of the EULA, you are not authorized to use
// this source code. For a copy of the EULA, please see the LICENSE.RTF on your
// install media.
//
//------------------------------------------------------------------------------
//
// File: profiler.c
//
// This file contains implementation of profiler module suitable for MIPS
// CPU/SoC with count/compare CP0 timer. It probably can be used on most
// if not all MIPS platforms for kernel profiling.
//
#include <windows.h>
#include <nkintr.h>
#include <oal.h>
#include <oal_intr_mips.h>
//------------------------------------------------------------------------------
// Local Variables
static struct {
BOOL enabled; // is profiler active?
UINT32 countsPerHit; // counts per profiler interrupt
UINT32 counts; // counts in system tick
UINT32 maxPeriodMSec; // saved maximal timer period
VOID (*pUpdateRescheduleTime)(DWORD); // saved function pointer
} g_profiler;
//------------------------------------------------------------------------------
// Local Functions
VOID OALProfileIntrUpdateRescheduleTime(DWORD time);
//------------------------------------------------------------------------------
//
// Function: OEMProfileTimerEnable
//
// This function is called by kernel to start kernel profiling timer.
//
VOID OEMProfileTimerEnable(DWORD interval)
{
BOOL enabled;
OALMSG(TRUE, (L"+OEMProfileTimerEnable(%d)\r\n", interval));
// We can't enable timer second time
if (g_profiler.enabled) return;
// How many hi-res ticks per profiler hit
g_profiler.countsPerHit = (g_oalTimer.countsPerMSec * interval)/1000;
// Make sure that value isn't too small
if (g_profiler.countsPerHit < 8 * g_oalTimer.countsMargin) {
g_profiler.countsPerHit = 8 * g_oalTimer.countsMargin;
}
// Following code should not be interrupted
enabled = INTERRUPTS_ENABLE(FALSE);
// Action depends if system run with fixed or variable system tick
if (pOEMUpdateRescheduleTime == NULL) {
// Avoid idle longer than system tick, so set value to one system tick
g_profiler.maxPeriodMSec = g_oalTimer.maxPeriodMSec;
g_oalTimer.maxPeriodMSec = g_oalTimer.msecPerSysTick;
} else {
// Replace update reschedule function with profile aware version
g_profiler.pUpdateRescheduleTime = pOEMUpdateRescheduleTime;
pOEMUpdateRescheduleTime = OALProfileIntrUpdateRescheduleTime;
}
// Change actual period
g_profiler.counts = OALTimerUpdate(
g_profiler.countsPerHit, g_oalTimer.countsMargin
) * g_profiler.countsPerHit;
g_oalTimer.curCounts += g_profiler.counts;
// Hook profiler interrupt service routing
UnhookInterrupt(5, OALTimerIntrHandler);
HookInterrupt(5, OALProfileIntrHandler);
// Enable interrupts
INTERRUPTS_ENABLE(enabled);
// Set flag
g_profiler.enabled = TRUE;
OALMSG(TRUE, (L"-OEMProfileTimerEnable\r\n"));
}
//------------------------------------------------------------------------------
//
// Function: OEMProfileTimerEnable
//
// This function is called by kernel to stop kernel profiling timer.
//
VOID OEMProfileTimerDisable()
{
BOOL enabled;
OALMSG(TRUE, (L"+OEMProfileTimerDisable()\r\n"));
// No disable without enable
if (!g_profiler.enabled) goto cleanUp;
// Following code should not be interrupted
enabled = INTERRUPTS_ENABLE(FALSE);
// Action depends if system run with fixed or variable system tick
if (pOEMUpdateRescheduleTime == NULL) {
// Restore maximal idle
g_oalTimer.maxPeriodMSec = g_profiler.maxPeriodMSec;
} else {
// Restore original update reschedule function
pOEMUpdateRescheduleTime = g_profiler.pUpdateRescheduleTime;
}
// Restore original system tick period
OALTimerUpdate(
g_oalTimer.countsPerSysTick - g_profiler.counts,
g_oalTimer.countsMargin
);
// Hook original timer interrupt service routine
UnhookInterrupt(5, OALProfileIntrHandler);
HookInterrupt(5, OALTimerIntrHandler);
// Enable interrupts
INTERRUPTS_ENABLE(enabled);
// Reset flag
g_profiler.enabled = FALSE;
cleanUp:
OALMSG(TRUE, (L"-OEMProfileTimerDisable\r\n"));
}
//------------------------------------------------------------------------------
//
// Function: OALProfileIntrHandler
//
// This is timer interrupt handler which replace default handler in time when
// kernel profiling is active. It calls original interrupt handler in
// appropriate times.
//
UINT32 OALProfileIntrHandler()
{
UINT32 sysIntr;
// First call profiler
ProfilerHit(GetEPC());
// Get new counts value
g_profiler.counts += g_profiler.countsPerHit;
// If offset is bigger or equal to system tick then call original timer ISR
if (g_profiler.counts >= g_oalTimer.actualCountsPerSysTick) {
// Fix high resolution counter (it will be updated by original ISR)
g_oalTimer.curCounts -= g_profiler.counts - g_profiler.countsPerHit;
// Update offset counter
g_profiler.counts -= g_oalTimer.actualCountsPerSysTick;
// Call standard handler
sysIntr = OALTimerIntrHandler();
// Timer handler recharged timer to full period, so we must change it
OALTimerUpdate(g_profiler.countsPerHit, g_oalTimer.countsMargin);
} else {
// Update counter/compare for next interrupt
OALTimerRecharge(g_profiler.countsPerHit, g_oalTimer.countsMargin);
// Update high resolution counter
g_oalTimer.curCounts += g_profiler.countsPerHit;
// No other action
sysIntr = SYSINTR_NOP;
}
return sysIntr;
}
//------------------------------------------------------------------------------
//
// Function: OALProfileIntrUpdateRescheduleTime
//
// This function is used to update reschedule time when profiler is running.
// It is mostly same as OALIntrUpdateRescheduleTime it only doesn't call
// OALTimerExtendSysTick.
//
VOID OALProfileIntrUpdateRescheduleTime(DWORD time)
{
UINT32 baseMSec, diffMSec, diffCounts;
INT32 counts;
// Get current system timer counter
baseMSec = CurMSec;
// Return if we are already setup correctly
if (time == (baseMSec + g_oalTimer.actualMSecPerSysTick)) return;
// How far we are from next tick
counts = g_oalTimer.actualCountsPerSysTick - OALTimerCountsSinceSysTick();
// If timer interrupts occurs, or we are within 1 ms of the scheduled
// interrupt, just return - timer ISR will take care of it.
if (baseMSec != CurMSec || counts < (INT32)g_oalTimer.countsPerMSec) return;
// Calculate the distance between the new time and the last timer interrupt
diffMSec = time - baseMSec;
// Trying to set reschedule time prior or equal to CurMSec - this could
// happen if a thread is on its way to sleep while preempted before
// getting into the Sleep Queue
if (diffMSec <= 0) diffMSec = 0;
// Account for hardware limitation
if (diffMSec > g_oalTimer.maxPeriodMSec) {
diffMSec = g_oalTimer.maxPeriodMSec;
}
// Calculate count difference
diffCounts = diffMSec * g_oalTimer.countsPerMSec;
// Actual values to be used by interrupt handler (it is safe to do
// modification without any type of guard as long as we know we are
// at least 1 ms before timer interrupt).
g_oalTimer.actualMSecPerSysTick = diffMSec;
g_oalTimer.actualCountsPerSysTick = diffCounts;
// Tick shift is not enough if there was interrupt from timer
// which increased CurMSec -> increase g_oalTimer.countsMargin to fix.
DEBUGCHK(baseMSec == CurMSec);
}
//------------------------------------------------------------------------------
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -