singrdk/base/Windows/singx86/sample.cpp

852 lines
22 KiB
C++
Raw Normal View History

2008-03-05 09:52:00 -05:00
///////////////////////////////////////////////////////////////////////////////
//
// profile.cpp - Extension to dump profiling information.
//
2008-11-17 18:29:00 -05:00
// Copyright Microsoft Corporation. All rights reserved.
2008-03-05 09:52:00 -05:00
//
//
///////////////////////////////////////////////////////////////////////////////
//
// Preprocessor material
// #define TEST 1
#ifdef TEST
#include <windows.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define ExtOut printf
#else
#include "singx86.h"
#define assert //
#endif
2008-11-17 18:29:00 -05:00
#include "diagnose.h"
2008-03-05 09:52:00 -05:00
#define UNUSED(x) (x) = (x);
///////////////////////////////////////////////////////////////////////////////
//
// Forward declarations
extern "C" {
int OriginCompare(const void* a, const void* b);
int TargetCompare(const void* a, const void* b);
}
///////////////////////////////////////////////////////////////////////////////
//
// Global variables
///////////////////////////////////////////////////////////////////////////////
//
// Utility methods
#ifdef TEST
static const char* fake_methods[] = { "foo", "bar", "puck", "po", "dizzy", "bazz", "escher", "ringo", "borix" };
static const size_t n_fake_methods = sizeof(fake_methods) / sizeof(fake_methods[0]);
ULONG64 MethodIp(ULONG64 ip)
{
return ip;
}
void GetMethodName(ULONG64 ip, PSTR buffer, ULONG bufferLength)
{
strncpy(buffer, fake_methods[ip], bufferLength);
}
#else // TEST
ULONG64 RoundIp(ULONG64 ip)
{
ULONG64 displacement;
if (g_ExtSymbols->GetNameByOffset(ip, NULL, 0, NULL, &displacement) == S_OK) {
return (ip - displacement);
}
return ip;
}
void GetMethodName(ULONG64 ip, PSTR buffer, ULONG bufferLength)
{
ULONG64 displacement;
ULONG bufferUsed = 0;
int status = g_ExtSymbols->GetNameByOffset(ip, buffer, bufferLength,
&bufferUsed, &displacement);
if (status != S_OK) {
_snprintf(buffer, bufferLength, "%I64x", ip);
return;
}
else if (displacement != 0) {
_snprintf(buffer + bufferUsed - 1, bufferLength - bufferUsed, "+0x%I64x", displacement);
}
}
#endif // TEST
///////////////////////////////////////////////////////////////////////////////
//
// Target Node
struct TargetNode
{
ULONG64 jumpTarget;
int jumpCount;
struct TargetNode* next;
struct TargetNode* prev;
TargetNode(ULONG64 theJumpTarget = 0)
: jumpTarget(theJumpTarget), jumpCount(1)
{
next = this;
prev = this;
}
inline ULONG64 JumpTarget() const { return jumpTarget; }
inline int JumpCount() const { return jumpCount; }
static int CompareJumps(const TargetNode* a, const TargetNode* b)
{
if (a->jumpCount > b->jumpCount) {
return -1;
} else if (a->jumpCount < b->jumpCount) {
return +1;
}
return 0;
}
};
///////////////////////////////////////////////////////////////////////////////
//
// Originating Node
struct OriginNode
{
struct OriginNode* next;
struct OriginNode* prev;
private:
TargetNode sentinel;
ULONG64 jumpOrigin;
int jumpCount;
int nodeCount;
inline void Append(TargetNode* node)
{
node->next = sentinel.next;
node->next->prev = node;
node->prev = &sentinel;
sentinel.next = node;
nodeCount++;
}
public:
OriginNode(ULONG64 theJumpOrigin = 0) : sentinel(0)
{
jumpOrigin = theJumpOrigin;
jumpCount = 0;
nodeCount = 0;
this->next = this;
this->prev = this;
}
~OriginNode()
{
TargetNode* node = sentinel.next;
while (node != &sentinel) {
TargetNode* next = node->next;
delete node;
node = next;
}
}
void AddJump(ULONG64 theJumpTarget)
{
jumpCount++;
TargetNode* node = sentinel.next;
TargetNode* stop = &sentinel;
while (node != stop) {
if (node->jumpTarget == theJumpTarget) {
node->jumpCount++;
return;
}
node = node->next;
}
Append(new TargetNode(theJumpTarget));
}
inline int JumpCount() const
{
return jumpCount;
}
inline ULONG64 JumpOrigin() const { return jumpOrigin; }
TargetNode** CreateSortTable()
{
TargetNode** sortTable = new TargetNode* [nodeCount];
int insert = 0;
TargetNode* stop = &sentinel;
TargetNode* node = stop->next;
while (node != stop) {
sortTable[insert++] = node;
node = node->next;
}
assert(insert == nodeCount);
qsort(sortTable, nodeCount, sizeof(TargetNode*), TargetCompare);
return sortTable;
}
void ReleaseSortTable(TargetNode** sortTable)
{
delete [] sortTable;
}
void DisplayTargets(const char* preamble)
{
char name[512];
TargetNode** sortTable = CreateSortTable();
for (int i = 0; i < nodeCount; i++) {
TargetNode* node = sortTable[i];
GetMethodName(node->JumpTarget(), name, sizeof(name) - 1);
ExtOut("%s [%d] %s\n", preamble, node->JumpCount(), name);
}
ReleaseSortTable(sortTable);
}
static int CompareJumps(const OriginNode* a, const OriginNode* b)
{
if (a->jumpCount> b->jumpCount) {
return -1;
}
else if (a->jumpCount < b->jumpCount) {
return +1;
}
return 0;
}
};
///////////////////////////////////////////////////////////////////////////////
//
// Jump Table to store all IP jumps
class JumpTable
{
static const int hashBins = 32;
OriginNode hashTable[hashBins];
int totalJumps;
int nodeCount;
inline int Hash(ULONG64 theJumpOrigin)
{
return (int) ((theJumpOrigin ^
(theJumpOrigin >> 12) ^
(theJumpOrigin >> 24)
) % hashBins);
}
public:
JumpTable()
: nodeCount(0), totalJumps(0)
{
}
~JumpTable()
{
for (int i = 0; i < hashBins; i++) {
OriginNode* sentinel = &hashTable[i];
OriginNode* node = sentinel->next;
while (node != sentinel) {
OriginNode* next = node->next;
delete node;
node = next;
}
}
}
void AddJump(ULONG64 theJumpOrigin, ULONG64 theJumpTarget)
{
int bin = Hash(theJumpOrigin);
OriginNode* sentinel = &hashTable[bin];
OriginNode* node = sentinel->next;
while (node != sentinel) {
if (node->JumpOrigin() == theJumpOrigin) {
node->AddJump(theJumpTarget);
totalJumps++;
return;
}
node = node->next;
}
node = new OriginNode(theJumpOrigin);
nodeCount++;
node->AddJump(theJumpTarget);
totalJumps++;
node->next = sentinel->next;
node->prev = sentinel;
node->next->prev = node;
sentinel->next = node;
}
OriginNode** CreateSortTable()
{
// Build table to sort
OriginNode** sortTable = new OriginNode* [nodeCount];
int insert = 0;
for (int i = 0; i < hashBins; i++)
{
OriginNode* sentinel = &hashTable[i];
OriginNode* node = sentinel->next;
while (node != sentinel) {
assert(node->JumpOrigin() < 0xffffffffULL);
sortTable[insert++] = node;
node = node->next;
}
}
qsort(sortTable, nodeCount, sizeof(OriginNode*), OriginCompare);
for (int i = 1; i < nodeCount; i++) {
assert(sortTable[i-1]->JumpCount() >= sortTable[i]->JumpCount());
}
return sortTable;
}
void ReleaseSortTable(OriginNode** sortTable)
{
delete [] sortTable;
}
void Display(const char* parentPreamble, const char* childPreamble)
{
OriginNode** sortTable = CreateSortTable();
char name[512];
int lastJumps = 0x7fffffff;
for (int i = 0; i < nodeCount; i++) {
OriginNode* node = sortTable[i];
int nodeJumps = node->JumpCount();
assert(nodeJumps <= lastJumps);
lastJumps = nodeJumps;
GetMethodName(node->JumpOrigin(), name, sizeof(name) - 1);
ExtOut("%d. %s %s [%d calls (%.2f%%)]\n",
i, parentPreamble, name, nodeJumps,
100.0 * (float)nodeJumps / (float) totalJumps);
node->DisplayTargets(childPreamble);
}
ReleaseSortTable(sortTable);
}
};
int OriginCompare(const void* pa, const void* pb)
{
return OriginNode::CompareJumps(*((const OriginNode**)pa),
*((const OriginNode**)pb));
}
int TargetCompare(const void* pa, const void* pb)
{
return TargetNode::CompareJumps(*(const TargetNode**)pa,
*(const TargetNode**)pb);
}
#ifdef TEST
int main()
{
JumpTable jt;
for (int calls = 0; calls < 150; calls++) {
int n = sizeof(fake_methods) / sizeof(fake_methods[0]);
int s = rand() % n_fake_methods;
int t = rand() % n_fake_methods;
jt.AddJump((ULONG64)s, (ULONG64)t);
}
jt.Display(" ", " >>");
return 0;
}
#else
static HRESULT Usage()
{
ExtOut("Usage:\n"
" !sample {options}\n"
"Options:\n"
2008-11-17 18:29:00 -05:00
" -e size : Enables sample profiling for the system. Size represents\n"
" : the memory buffer in kbytes used for profiling\n"
" : This command takes effect only on initial breakpoint in debugger\n"
2008-03-05 09:52:00 -05:00
" -c : Print caller-callee information\n"
" -f : Print frequency distribution of methods\n"
" -k : Print callee-caller information\n"
" -l : Analyze call graph leafs only\n"
" -n count : Print count most recent sample stack traces\n"
" -t : Print sample stack traces\n"
" -x : Use exact IP values rather than rounding to method start\n"
);
return S_FALSE;
}
2008-11-17 18:29:00 -05:00
void
EnableSampleProfiler(ULONG64 MemorySize)
{
HRESULT status;
BOOL OKSymbols = FALSE;
EXT_CHECK(SetBound("Kernel::ProfilerBufferSize", MemorySize));
ExtOut("Sampling profiler is enabled for a buffer of %ld KBytes\n",
(ULONG)MemorySize / 1024);
OKSymbols = TRUE;
Exit:
if (!OKSymbols)
{
ExtOut("Error accessing variables. Please check symbols for kernel\n");
}
return;
}
2008-03-05 09:52:00 -05:00
static void
ClearLog()
{
ULONG64 address;
int newPcLength = 0;
2008-11-17 18:29:00 -05:00
if (g_ExtSymbols->GetOffsetByName("!Processor::pcLength", &address) == S_OK) {
2008-03-05 09:52:00 -05:00
g_ExtData->WriteVirtual(address, &newPcLength, 4, NULL);
ExtOut("Log cleared.\n");
return;
}
ExtOut("Failed: Could not locate log length variable.\n");
}
2008-11-17 18:29:00 -05:00
class SampleStats: public VariableArray {
2008-03-05 09:52:00 -05:00
2008-11-17 18:29:00 -05:00
public:
2008-03-05 09:52:00 -05:00
2008-11-17 18:29:00 -05:00
SampleStats() : VariableArray(){};
void Sort();
};
static int ItemCompare(const void * e1, const void * e2)
{
UINT64 *ptr1 = (UINT64 *) e1;
UINT64 *ptr2 = (UINT64 *) e2;
if (ptr1[0] < ptr2[0]) {
return -1;
} else if (ptr1[0] == ptr2[0]) {
return 0;
2008-03-05 09:52:00 -05:00
}
2008-11-17 18:29:00 -05:00
return 1;
2008-03-05 09:52:00 -05:00
}
2008-11-17 18:29:00 -05:00
static int ItemCompareFreq(const void * e1, const void * e2)
2008-03-05 09:52:00 -05:00
{
2008-11-17 18:29:00 -05:00
UINT64 *ptr1 = (UINT64 *) e1;
UINT64 *ptr2 = (UINT64 *) e2;
2008-03-05 09:52:00 -05:00
2008-11-17 18:29:00 -05:00
if (ptr1[1] < ptr2[1]) {
return 1;
} else if (ptr1[1] == ptr2[1]) {
return 0;
}
2008-03-05 09:52:00 -05:00
2008-11-17 18:29:00 -05:00
return -1;
}
2008-03-05 09:52:00 -05:00
2008-11-17 18:29:00 -05:00
void SampleStats::Sort()
{
//
// Sort all ranges by address to make easier searching for overlapped ranges
//
2008-03-05 09:52:00 -05:00
2008-11-17 18:29:00 -05:00
qsort(Array, InUse / 2, 2*sizeof(UINT64), ItemCompare);
}
2008-03-05 09:52:00 -05:00
2008-11-17 18:29:00 -05:00
class SampleTracing : public EventingEnumerator
{
2008-03-05 09:52:00 -05:00
2008-11-17 18:29:00 -05:00
public:
bool doExactIps;
bool doCallerCallee;
bool doCalleeCaller;
bool doLeavesOnly;
bool doTraces;
bool doFrequencies;
int mostRecent;
SampleStats * Stats;
2008-03-05 09:52:00 -05:00
2008-11-17 18:29:00 -05:00
SampleTracing();
~SampleTracing();
2008-03-05 09:52:00 -05:00
2008-11-17 18:29:00 -05:00
// Override callouts
2008-03-05 09:52:00 -05:00
2008-11-17 18:29:00 -05:00
bool virtual TypeCallout(EventTypeEntry * entryDescriptor);
bool virtual EntryCallout(EntryHeader *header, EventTypeEntry * entryDescriptor);
2008-03-05 09:52:00 -05:00
2008-11-17 18:29:00 -05:00
void AnalyzeTraces();
private:
JumpTable foreTable;
JumpTable backTable;
UINT64 profileType;
UINT64 LastTimestamp;
int CrtItem;
};
2008-03-05 09:52:00 -05:00
2008-11-17 18:29:00 -05:00
SampleTracing::SampleTracing()
{
doExactIps = false;
doCallerCallee = false;
doCalleeCaller = false;
doLeavesOnly = false;
doTraces = false;
doFrequencies = false;
mostRecent = 0;
profileType = 0;
LastTimestamp = 0;
CrtItem = 0;
Stats = new SampleStats();
}
SampleTracing::~SampleTracing()
{
if (Stats) delete Stats;
}
bool SampleTracing::TypeCallout(EventTypeEntry * entryDescriptor)
{
if (!EventingEnumerator::TypeCallout(entryDescriptor)) {
2008-03-05 09:52:00 -05:00
2008-11-17 18:29:00 -05:00
if (!EventingEnumerator::TypeCallout(SystemHeaderType)) {
return FALSE;
}
2008-03-05 09:52:00 -05:00
}
2008-11-17 18:29:00 -05:00
if (_stricmp(entryDescriptor->Name, "ProfileEvent") == 0) {
2008-03-05 09:52:00 -05:00
2008-11-17 18:29:00 -05:00
profileType = (UINT64)entryDescriptor->Key;
2008-03-05 09:52:00 -05:00
2008-11-17 18:29:00 -05:00
}
return TRUE;
}
2008-03-05 09:52:00 -05:00
2008-11-17 18:29:00 -05:00
bool SampleTracing::EntryCallout(EntryHeader *header, EventTypeEntry * entryDescriptor)
{
if (entryDescriptor == NULL) {
return FALSE;
2008-03-05 09:52:00 -05:00
}
2008-11-17 18:29:00 -05:00
//
// Filter only for the events of interest
//
2008-03-05 09:52:00 -05:00
2008-11-17 18:29:00 -05:00
if (entryDescriptor->Key == profileType) {
2008-03-05 09:52:00 -05:00
2008-11-17 18:29:00 -05:00
if (doTraces) {
2008-03-05 09:52:00 -05:00
2008-11-17 18:29:00 -05:00
ULONG cpuId;
UINT64 timestamp;
2008-03-05 09:52:00 -05:00
2008-11-17 18:29:00 -05:00
//
// Switch to the metadata context of an entry to access the metadata fields
//
2008-03-05 09:52:00 -05:00
2008-11-17 18:29:00 -05:00
ULONG_PTR ctx = SWITCH_TO_METADATA_CURSOR();
2008-03-05 09:52:00 -05:00
2008-11-17 18:29:00 -05:00
FieldEntry * field = SystemHeaderType->GetField("Cpu");
cpuId = (ULONG)field->GetFieldNumericValue();
2008-03-05 09:52:00 -05:00
2008-11-17 18:29:00 -05:00
field = SystemHeaderType->GetField("Timestamp");
timestamp = field->GetFieldNumericValue();
2008-03-05 09:52:00 -05:00
2008-11-17 18:29:00 -05:00
RESTORE_CURSOR(ctx);
2008-03-05 09:52:00 -05:00
2008-11-17 18:29:00 -05:00
ExtOut("%5ld %2ld %18I64x %12I64d:",
(int)CrtItem,
(int)cpuId,
timestamp,
(LastTimestamp != 0 ? (timestamp - LastTimestamp) : 0));
LastTimestamp = timestamp;
CrtItem += 1;
2008-03-05 09:52:00 -05:00
}
2008-11-17 18:29:00 -05:00
//
// Prepare to access the array of IP entries
//
FieldEntry * array = entryDescriptor->GetField("StackTrace");
void * buffer = entryDescriptor->GetFieldArray((int)array->GetFieldNumericValue());
int count = array->GetArraySize(buffer);
2008-03-05 09:52:00 -05:00
int doneFirst = 0;
ULONG64 lastIp = 0;
bool leafJumpDone = false;
bool leafIpDone = false;
2008-11-17 18:29:00 -05:00
CHAR name[512];
for (int i = 0; i < count; i++) {
UINT64 eip = (UINT64)array->GetFieldNumericValue(buffer, i);
2008-03-05 09:52:00 -05:00
if (!doExactIps) {
eip = RoundIp(eip);
}
if ((leafIpDone == false || doLeavesOnly == false) && eip != 0) {
2008-11-17 18:29:00 -05:00
Stats->Add(eip);
Stats->Add(0);
2008-03-05 09:52:00 -05:00
leafIpDone = true;
}
if (lastIp != 0 && eip != 0 &&
(leafJumpDone == false || doLeavesOnly == false)) {
foreTable.AddJump(lastIp, eip);
backTable.AddJump(eip, lastIp);
leafJumpDone = true;
}
lastIp = eip;
if (eip == 0) {
break;
}
doneFirst |= 1;
if (doTraces == false)
continue;
2008-11-17 18:29:00 -05:00
2008-03-05 09:52:00 -05:00
if (g_ExtSymbols->GetNameByOffset(eip, (PSTR)&name, sizeof(name), NULL, NULL) == S_OK) {
if (doneFirst)
2008-11-17 18:29:00 -05:00
ExtOut(" <- %s (%p)", name, eip);
2008-03-05 09:52:00 -05:00
else
2008-11-17 18:29:00 -05:00
ExtOut(" %s (%p)", name, eip);
2008-03-05 09:52:00 -05:00
}
else {
if (doneFirst)
ExtOut(" <- %p", eip);
else
ExtOut(" %p", eip);
}
2008-11-17 18:29:00 -05:00
}
2008-03-05 09:52:00 -05:00
if (doTraces)
ExtOut("\n");
}
2008-11-17 18:29:00 -05:00
return TRUE;
}
void SampleTracing::AnalyzeTraces()
{
CHAR name[512];
2008-03-05 09:52:00 -05:00
if (doFrequencies) {
ExtOut("==================================================================\n");
ExtOut("Raw instruction hits\n");
2008-11-17 18:29:00 -05:00
if (Stats->InUse > 0) {
Stats->Sort();
2008-03-05 09:52:00 -05:00
int outIndex = 0;
2008-11-17 18:29:00 -05:00
ULONG64 lastIp = Stats->Array[0];
for (int i = 0; i < Stats->InUse; i+= 2) {
if (Stats->Array[i] == lastIp) {
Stats->Array[outIndex + 1] += 1;
2008-03-05 09:52:00 -05:00
}
else {
2008-11-17 18:29:00 -05:00
outIndex += 2;
Stats->Array[outIndex] = Stats->Array[i];
Stats->Array[outIndex + 1] = 1;
lastIp = Stats->Array[i];
2008-03-05 09:52:00 -05:00
}
}
// Sort on frequency and display
2008-11-17 18:29:00 -05:00
qsort(Stats->Array, outIndex / 2, 2*sizeof(UINT64), ItemCompareFreq);
2008-03-05 09:52:00 -05:00
ExtOut("Top of the pops has %d entries:\n", outIndex);
2008-11-17 18:29:00 -05:00
for (int i = 0; i < outIndex; i += 2) {
if (g_ExtSymbols->GetNameByOffset(Stats->Array[i],
2008-03-05 09:52:00 -05:00
(PSTR)&name,
sizeof(name),
NULL, NULL) == S_OK) {
2008-11-17 18:29:00 -05:00
ExtOut("% 7d %p %s\n", (int)Stats->Array[i + 1], Stats->Array[i], name);
2008-03-05 09:52:00 -05:00
}
else {
2008-11-17 18:29:00 -05:00
ExtOut("% 7d %p (unknown symbol)\n", (int)Stats->Array[i + 1], Stats->Array[i]);
2008-03-05 09:52:00 -05:00
}
}
}
}
if (doCallerCallee) {
ExtOut("==================================================================\n");
ExtOut("Calls by target method to other methods:\n");
backTable.Display("Calls made by", " ");
}
if (doCalleeCaller) {
ExtOut("==================================================================\n");
ExtOut("Calls to target method by other methods.\n");
foreTable.Display("Calls made to", " ");
}
2008-11-17 18:29:00 -05:00
}
EXT_DECL(sample) // Defines: PDEBUG_CLIENT Client, PCSTR args
{
EXT_ENTER(); // Defines: HRESULT status = S_OK;
pointerSize = (g_ExtControl->IsPointer64Bit() == S_OK) ? 8 : 4;
SourceCollector * collector = new SourceCollector("SampleProfiler:0", "Timestamp");
SampleTracing * dump = NULL;
if (collector) {
dump = new SampleTracing();
if (dump == NULL) {
ExtOut("Not enough memory\n");
EXT_CHECK(~S_OK);
}
}
collector->TypeHandle = 0;
collector->GroupByController = TRUE;
collector->cascadeEnumerator = dump;
while (*args != '\0') {
while (*args == ' ' || * args == '\t') {
args++;
}
if (*args != '-' && *args != '/') {
Usage();
EXT_CHECK(~S_OK);
}
args++;
switch (tolower(*args++)) {
case 'e' : {
SKIP_WHITESPACES(args);
int bufferSize = (int)GetValue(args, true);
EnableSampleProfiler(bufferSize*1024);
goto Exit;
}
break;
case 'c':
dump->doCallerCallee = true;
break;
case 'f':
dump->doFrequencies = true;
break;
case 'k':
dump->doCalleeCaller = true;
break;
case 'l':
dump->doLeavesOnly = true;
break;
case 'n':
while ((*args == ' ' || *args == '\t') && *args != '\0') {
args++;
}
dump->mostRecent = atoi(args);
while (*args != ' ' && *args != '\t' && *args != '\0') {
args++;
}
break;
case 't':
dump->doTraces = true;
break;
case 'x':
dump->doExactIps = true;
break;
default:
Usage();
EXT_CHECK(~S_OK);
}
}
if (dump->doCallerCallee == false &&
dump->doCalleeCaller == false &&
dump->doTraces == false &&
dump->doFrequencies == false) {
Usage();
EXT_CHECK(~S_OK);
}
if (dump->doTraces) {
ExtOut("<Sample Round> <DeltaTicks> <interrupt> <eip ...>\n");
}
// Allocate statistics array
ExtOut("Starting analysis of sample log\n");
if (dump->doLeavesOnly)
ExtOut("Analyzing leaf nodes only.\n");
if (dump->doTraces) {
ExtOut("==================================================================\n");
ExtOut("Raw call stacks\n");
}
if (dump->doExactIps) {
ExtOut("Exact IPs\n");
}
WalkTracingDatabase(collector, FALSE);
dump->AnalyzeTraces();
2008-03-05 09:52:00 -05:00
Exit:
2008-11-17 18:29:00 -05:00
if (collector) delete collector;
if (dump) delete dump;
2008-03-05 09:52:00 -05:00
ExtRelease();
2008-11-17 18:29:00 -05:00
return S_OK;
2008-03-05 09:52:00 -05:00
}
2008-11-17 18:29:00 -05:00
2008-03-05 09:52:00 -05:00
#endif // TEST