852 lines
22 KiB
C++
852 lines
22 KiB
C++
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// profile.cpp - Extension to dump profiling information.
|
|
//
|
|
// Copyright Microsoft Corporation. All rights reserved.
|
|
//
|
|
//
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// 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
|
|
|
|
#include "diagnose.h"
|
|
|
|
#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"
|
|
" -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"
|
|
" -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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
ClearLog()
|
|
{
|
|
ULONG64 address;
|
|
int newPcLength = 0;
|
|
if (g_ExtSymbols->GetOffsetByName("!Processor::pcLength", &address) == S_OK) {
|
|
g_ExtData->WriteVirtual(address, &newPcLength, 4, NULL);
|
|
ExtOut("Log cleared.\n");
|
|
return;
|
|
}
|
|
ExtOut("Failed: Could not locate log length variable.\n");
|
|
}
|
|
|
|
class SampleStats: public VariableArray {
|
|
|
|
public:
|
|
|
|
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;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int ItemCompareFreq(const void * e1, const void * e2)
|
|
{
|
|
UINT64 *ptr1 = (UINT64 *) e1;
|
|
UINT64 *ptr2 = (UINT64 *) e2;
|
|
|
|
if (ptr1[1] < ptr2[1]) {
|
|
return 1;
|
|
} else if (ptr1[1] == ptr2[1]) {
|
|
return 0;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
void SampleStats::Sort()
|
|
{
|
|
//
|
|
// Sort all ranges by address to make easier searching for overlapped ranges
|
|
//
|
|
|
|
qsort(Array, InUse / 2, 2*sizeof(UINT64), ItemCompare);
|
|
}
|
|
|
|
class SampleTracing : public EventingEnumerator
|
|
{
|
|
|
|
public:
|
|
bool doExactIps;
|
|
bool doCallerCallee;
|
|
bool doCalleeCaller;
|
|
bool doLeavesOnly;
|
|
bool doTraces;
|
|
bool doFrequencies;
|
|
int mostRecent;
|
|
SampleStats * Stats;
|
|
|
|
SampleTracing();
|
|
~SampleTracing();
|
|
|
|
// Override callouts
|
|
|
|
bool virtual TypeCallout(EventTypeEntry * entryDescriptor);
|
|
bool virtual EntryCallout(EntryHeader *header, EventTypeEntry * entryDescriptor);
|
|
|
|
void AnalyzeTraces();
|
|
|
|
private:
|
|
JumpTable foreTable;
|
|
JumpTable backTable;
|
|
UINT64 profileType;
|
|
UINT64 LastTimestamp;
|
|
int CrtItem;
|
|
};
|
|
|
|
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)) {
|
|
|
|
if (!EventingEnumerator::TypeCallout(SystemHeaderType)) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (_stricmp(entryDescriptor->Name, "ProfileEvent") == 0) {
|
|
|
|
profileType = (UINT64)entryDescriptor->Key;
|
|
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
bool SampleTracing::EntryCallout(EntryHeader *header, EventTypeEntry * entryDescriptor)
|
|
{
|
|
if (entryDescriptor == NULL) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Filter only for the events of interest
|
|
//
|
|
|
|
if (entryDescriptor->Key == profileType) {
|
|
|
|
if (doTraces) {
|
|
|
|
ULONG cpuId;
|
|
UINT64 timestamp;
|
|
|
|
//
|
|
// Switch to the metadata context of an entry to access the metadata fields
|
|
//
|
|
|
|
ULONG_PTR ctx = SWITCH_TO_METADATA_CURSOR();
|
|
|
|
FieldEntry * field = SystemHeaderType->GetField("Cpu");
|
|
cpuId = (ULONG)field->GetFieldNumericValue();
|
|
|
|
field = SystemHeaderType->GetField("Timestamp");
|
|
timestamp = field->GetFieldNumericValue();
|
|
|
|
RESTORE_CURSOR(ctx);
|
|
|
|
ExtOut("%5ld %2ld %18I64x %12I64d:",
|
|
(int)CrtItem,
|
|
(int)cpuId,
|
|
timestamp,
|
|
(LastTimestamp != 0 ? (timestamp - LastTimestamp) : 0));
|
|
|
|
LastTimestamp = timestamp;
|
|
CrtItem += 1;
|
|
}
|
|
|
|
//
|
|
// 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);
|
|
|
|
int doneFirst = 0;
|
|
ULONG64 lastIp = 0;
|
|
bool leafJumpDone = false;
|
|
bool leafIpDone = false;
|
|
CHAR name[512];
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
UINT64 eip = (UINT64)array->GetFieldNumericValue(buffer, i);
|
|
|
|
if (!doExactIps) {
|
|
eip = RoundIp(eip);
|
|
}
|
|
|
|
if ((leafIpDone == false || doLeavesOnly == false) && eip != 0) {
|
|
Stats->Add(eip);
|
|
Stats->Add(0);
|
|
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;
|
|
|
|
|
|
if (g_ExtSymbols->GetNameByOffset(eip, (PSTR)&name, sizeof(name), NULL, NULL) == S_OK) {
|
|
if (doneFirst)
|
|
ExtOut(" <- %s (%p)", name, eip);
|
|
else
|
|
ExtOut(" %s (%p)", name, eip);
|
|
}
|
|
else {
|
|
if (doneFirst)
|
|
ExtOut(" <- %p", eip);
|
|
else
|
|
ExtOut(" %p", eip);
|
|
}
|
|
}
|
|
|
|
if (doTraces)
|
|
ExtOut("\n");
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
void SampleTracing::AnalyzeTraces()
|
|
{
|
|
CHAR name[512];
|
|
if (doFrequencies) {
|
|
ExtOut("==================================================================\n");
|
|
ExtOut("Raw instruction hits\n");
|
|
|
|
if (Stats->InUse > 0) {
|
|
|
|
Stats->Sort();
|
|
|
|
int outIndex = 0;
|
|
ULONG64 lastIp = Stats->Array[0];
|
|
for (int i = 0; i < Stats->InUse; i+= 2) {
|
|
if (Stats->Array[i] == lastIp) {
|
|
Stats->Array[outIndex + 1] += 1;
|
|
}
|
|
else {
|
|
outIndex += 2;
|
|
Stats->Array[outIndex] = Stats->Array[i];
|
|
Stats->Array[outIndex + 1] = 1;
|
|
lastIp = Stats->Array[i];
|
|
}
|
|
}
|
|
|
|
// Sort on frequency and display
|
|
qsort(Stats->Array, outIndex / 2, 2*sizeof(UINT64), ItemCompareFreq);
|
|
ExtOut("Top of the pops has %d entries:\n", outIndex);
|
|
|
|
for (int i = 0; i < outIndex; i += 2) {
|
|
if (g_ExtSymbols->GetNameByOffset(Stats->Array[i],
|
|
(PSTR)&name,
|
|
sizeof(name),
|
|
NULL, NULL) == S_OK) {
|
|
ExtOut("% 7d %p %s\n", (int)Stats->Array[i + 1], Stats->Array[i], name);
|
|
}
|
|
else {
|
|
ExtOut("% 7d %p (unknown symbol)\n", (int)Stats->Array[i + 1], Stats->Array[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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", " ");
|
|
}
|
|
}
|
|
|
|
|
|
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();
|
|
|
|
|
|
Exit:
|
|
if (collector) delete collector;
|
|
if (dump) delete dump;
|
|
ExtRelease();
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
#endif // TEST
|