Skip to content

Instantly share code, notes, and snippets.

@pervognsen
Last active January 22, 2019 17:08
Show Gist options
  • Save pervognsen/73597a3a732a10922418d62c6c86a427 to your computer and use it in GitHub Desktop.
Save pervognsen/73597a3a732a10922418d62c6c86a427 to your computer and use it in GitHub Desktop.
/// zzz - very sleepy replacement, based on etw
#define INITGUID
#include <windows.h>
#include <psapi.h>
#include <tdh.h>
#include <evntrace.h>
#include <guiddef.h>
#pragma warning(disable: 4091)
#include <dbghelp.h>
#include <winternl.h>
#include <stdio.h>
#include <stdint.h>
#include <stdarg.h>
#include <vector>
#include <algorithm>
#include <unordered_map>
#pragma comment(lib, "dbghelp.lib")
#pragma comment(lib, "ntdll.lib")
#define Assert(x) \
if (!(x)) { MessageBoxA(0, #x, "Assertion Failure", MB_OK); __debugbreak(); }
HANDLE output_file = INVALID_HANDLE_VALUE;
void Outputf(char *format, ...) {
static char buffer[1024 * 1024];
va_list arg_list;
va_start(arg_list, format);
uint32_t length = vsprintf_s(buffer, sizeof(buffer), format, arg_list);
va_end(arg_list);
OutputDebugStringA(buffer);
if (output_file != INVALID_HANDLE_VALUE) {
DWORD written_length;
WriteFile(output_file, buffer, length, &written_length, 0);
}
}
uint64_t timer_frequency;
void ResetTimer(uint64_t *timer) {
QueryPerformanceCounter((LARGE_INTEGER *)timer);
}
uint64_t GetTimerMilliseconds(uint64_t *timer) {
LARGE_INTEGER current_time;
QueryPerformanceCounter(&current_time);
uint64_t duration = current_time.QuadPart - *timer;
return (1000 * duration) / timer_frequency;
}
enum { PAGE_SIZE = 4096, GB = 1024 * 1024 * 1024 };
struct Range {
char *base;
char *commit_end;
char *reserve_end;
};
void AllocateRange(Range *range, size_t reserve_size) {
reserve_size = (reserve_size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
range->base = (char *)VirtualAlloc(0, reserve_size, MEM_RESERVE, PAGE_READWRITE);
Assert(range->base);
size_t initial_commit_size = PAGE_SIZE;
void *pointer = VirtualAlloc(range->base, initial_commit_size, MEM_COMMIT, PAGE_READWRITE);
Assert(pointer);
range->commit_end = range->base + initial_commit_size;
range->reserve_end = range->base + reserve_size;
}
void FreeRange(Range *range) {
VirtualFree(range->base, range->reserve_end - range->base, MEM_RELEASE);
range->base = 0;
range->commit_end = 0;
range->reserve_end = 0;
}
void CommitRange(Range *range, void *base, size_t size) {
char *pointer = (char *)base;
Assert(range->base <= pointer && pointer + size < range->reserve_end);
if (pointer + size >= range->commit_end) {
size_t commit_growth = (pointer + size - range->commit_end + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
VirtualAlloc(range->commit_end, commit_growth, MEM_COMMIT, PAGE_READWRITE);
range->commit_end += commit_growth;
}
}
void ElevatePrivilege() {
BOOL success;
HANDLE token = 0;
success = OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token);
Assert(success);
TOKEN_PRIVILEGES token_privileges;
ZeroMemory(&token_privileges, sizeof(token_privileges));
token_privileges.PrivilegeCount = 1;
token_privileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
LookupPrivilegeValue(NULL, SE_SYSTEM_PROFILE_NAME, &token_privileges.Privileges[0].Luid);
AdjustTokenPrivileges(token, FALSE, &token_privileges, 0, (PTOKEN_PRIVILEGES)NULL, 0);
::CloseHandle(token);
}
struct ProfileCapture {
bool delete_old_capture;
WCHAR *filename;
ULONG maximum_size_in_megabytes;
TRACEHANDLE trace_handle;
EVENT_TRACE_PROPERTIES *properties;
};
DEFINE_GUID(
PerfInfoGuid,
0xce1dbfb4,
0x137e,
0x4da6,
0x87, 0xb0, 0x3f, 0x59, 0xaa, 0x10, 0x2c, 0xbc
);
DEFINE_GUID(
StackWalkGuid,
0xdef2fe46,
0x7bd6,
0x4b80,
0xbd, 0x94, 0xf5, 0x7f, 0xe2, 0x0d, 0x0c, 0xe3
);
DEFINE_GUID(
ProcessGuid,
0x3d6fa8d0,
0xfe05,
0x11d0,
0x9d, 0xda, 0x00, 0xc0, 0x4f, 0xd7, 0xba, 0x7c
);
DEFINE_GUID(
ThreadGuid,
0x3d6fa8d1,
0xfe05,
0x11d0,
0x9d, 0xda, 0x00, 0xc0, 0x4f, 0xd7, 0xba, 0x7c
);
DEFINE_GUID(
ImageLoadGuid,
0x2cb15d1d,
0x5fc1,
0x11d2,
0xab, 0xe1, 0x00, 0xa0, 0xc9, 0x11, 0xf5, 0x18
);
enum EventType {
EVENT_STACK_WALK = 32,
EVENT_SAMPLED_PROFILE = 46,
EVENT_PROCESS_START = 1,
EVENT_PROCESS_END = 2,
EVENT_PROCESS_DC_START = 3,
EVENT_PROCESS_DC_END = 4,
EVENT_THREAD_START = 1,
EVENT_THREAD_END = 2,
EVENT_THREAD_DC_START = 3,
EVENT_THREAD_DC_END = 4,
EVENT_IMAGE_LOAD = 10,
EVENT_IMAGE_UNLOAD = 2,
EVENT_IMAGE_DC_LOAD = 3,
EVENT_IMAGE_DC_UNLOAD = 4,
};
struct Event_SampledProfile {
void *instruction_pointer;
uint32_t thread_id;
};
struct Event_StackWalk {
uint64_t timestamp;
uint32_t process_id;
uint32_t thread_id;
void *instruction_pointers[1];
};
struct Event_Process {
void *unique_process_key;
uint32_t process_id;
uint32_t parent_id;
uint32_t session_id;
int32_t exit_status;
void *directory_table_base;
void *user_token[2];
SID sid;
};
struct Event_Thread {
uint32_t process_id;
uint32_t thread_id;
};
struct Event_ImageLoad {
void *image_base;
size_t image_size;
uint32_t process_id;
uint32_t image_checksum;
uint32_t time_date_stamp;
uint32_t reserved0;
void *default_base;
uint32_t reserved1[4];
WCHAR filename[1];
};
struct ProfileProcess;
struct ProfileImage {
uint64_t load_time;
uint64_t unload_time;
ProfileProcess *process;
char *name;
char *filename;
void *base;
size_t size;
ProfileImage *next_image;
};
struct ProfileStack;
struct ProfileSymbol {
char *name;
ProfileImage *image;
uint32_t exclusive_count;
uint32_t inclusive_count;
uint64_t exclusive_time;
uint64_t inclusive_time;
};
struct ProfileProcess {
uint64_t start_time;
uint64_t stop_time;
uint32_t process_id;
char *filename;
ProfileImage *first_image;
};
struct ProfileThread {
uint64_t start_time;
uint64_t stop_time;
uint32_t thread_id;
uint64_t last_stack_time;
ProfileProcess *process;
};
struct ProfileStack {
uint64_t time;
uint64_t timespan;
ProfileThread *thread;
void **instruction_pointers;
ProfileSymbol **symbols;
size_t instruction_pointers_count;
};
struct ProfileAnalysis {
uint64_t performance_frequency;
Range processes_range;
ProfileProcess *processes;
size_t processes_count;
Range threads_range;
ProfileThread *threads;
size_t threads_count;
Range images_range;
ProfileImage *images;
size_t images_count;
Range stacks_range;
ProfileStack *stacks;
size_t stacks_count;
Range symbols_range;
ProfileSymbol *symbols;
size_t symbols_count;
Range data_range;
char *data;
char *data_end;
};
void *AllocateProfileData(ProfileAnalysis *analysis, size_t size) {
uintptr_t address = (uintptr_t)analysis->data_end;
address = (address + 7) & ~7;
char *pointer = (char *)address;
CommitRange(&analysis->data_range, pointer, (pointer - analysis->data_end) + size);
analysis->data_end = pointer + size;
return pointer;
}
ProfileImage *AddProfileImage(ProfileAnalysis *analysis) {
ProfileImage *image = analysis->images + analysis->images_count;
CommitRange(&analysis->images_range, image, sizeof(ProfileImage));
analysis->images_count++;
return image;
}
ProfileProcess *AddProfileProcess(ProfileAnalysis *analysis) {
ProfileProcess *process = analysis->processes + analysis->processes_count;
CommitRange(&analysis->processes_range, process, sizeof(ProfileProcess));
analysis->processes_count++;
return process;
}
ProfileThread *AddProfileThread(ProfileAnalysis *analysis) {
ProfileThread *thread = analysis->threads + analysis->threads_count;
CommitRange(&analysis->threads_range, thread, sizeof(ProfileThread));
analysis->threads_count++;
return thread;
}
ProfileStack *AddProfileStack(ProfileAnalysis *analysis) {
ProfileStack *stack = analysis->stacks + analysis->stacks_count;
CommitRange(&analysis->stacks_range, stack, sizeof(ProfileStack));
analysis->stacks_count++;
return stack;
}
ProfileSymbol *AddProfileSymbol(ProfileAnalysis *analysis) {
ProfileSymbol *symbol = analysis->symbols + analysis->symbols_count;
CommitRange(&analysis->symbols_range, symbol, sizeof(ProfileSymbol));
analysis->symbols_count++;
return symbol;
}
struct EventRecordCallbackContext {
ProfileAnalysis *analysis;
EVENT_TRACE_LOGFILE *event_trace_logfile;
std::unordered_map<uint32_t, ProfileProcess *> processes;
std::unordered_map<uint32_t, ProfileThread *> threads;
};
void WINAPI EventRecordCallback(EVENT_RECORD *event_record) {
EventRecordCallbackContext *context = (EventRecordCallbackContext *)event_record->UserContext;
ProfileAnalysis *analysis = context->analysis;
std::unordered_map<uint32_t, ProfileProcess *> &processes = context->processes;
std::unordered_map<uint32_t, ProfileThread *> &threads = context->threads;
GUID provider_guid = event_record->EventHeader.ProviderId;
uint8_t event_type = event_record->EventHeader.EventDescriptor.Opcode;
uint32_t event_process_id = event_record->EventHeader.ProcessId;
uint8_t event_version = event_record->EventHeader.EventDescriptor.Version;
uint64_t event_time = event_record->EventHeader.TimeStamp.QuadPart;
if (IsEqualGUID(provider_guid, StackWalkGuid)) {
Assert(event_version == 2);
if (event_type == EVENT_STACK_WALK) {
uint32_t header_size = sizeof(uint64_t) + sizeof(uint32_t) + sizeof(uint32_t);
uint32_t instruction_pointers_count = (event_record->UserDataLength - header_size) / sizeof(void *);
Event_StackWalk *event_stack = (Event_StackWalk *)event_record->UserData;
ProfileThread *thread = threads[event_stack->thread_id];
if (thread) {
ProfileStack *stack = AddProfileStack(analysis);
stack->time = event_time;
stack->thread = thread;
if (thread->last_stack_time) {
stack->timespan = event_time - thread->last_stack_time;
} else {
stack->timespan = 0;
}
thread->last_stack_time = event_time;
stack->instruction_pointers_count = instruction_pointers_count;
size_t instruction_pointers_size = instruction_pointers_count * sizeof(void *);
stack->instruction_pointers = (void **)AllocateProfileData(analysis, instruction_pointers_size);
memcpy(stack->instruction_pointers, event_stack->instruction_pointers, instruction_pointers_size);
size_t symbols_size = instruction_pointers_count * sizeof(ProfileSymbol *);
stack->symbols = (ProfileSymbol **)AllocateProfileData(analysis, symbols_size);
memset(stack->symbols, 0, symbols_size);
}
}
} else if (IsEqualGUID(provider_guid, ProcessGuid)) {
Assert(event_version == 3);
Event_Process *event_process = (Event_Process *)event_record->UserData;
switch (event_type) {
case EVENT_PROCESS_DC_START:
case EVENT_PROCESS_START: {
uint32_t sid_length = GetLengthSid(&event_process->sid);
char *filename = (char *)&event_process->sid + sid_length;
ProfileProcess *process = AddProfileProcess(analysis);
process->start_time = event_time;
process->stop_time = 0;
process->process_id = event_process->process_id;
size_t filename_size = strlen(filename) + 1;
process->filename = (char *)AllocateProfileData(analysis, filename_size);
memcpy(process->filename, filename, filename_size);
process->first_image = 0;
processes[process->process_id] = process;
break;
}
case EVENT_PROCESS_DC_END:
case EVENT_PROCESS_END: {
auto it = processes.find(event_process->process_id);
if (it != processes.end()) {
ProfileProcess *process = it->second;
process->stop_time = event_time;
if (event_type == EVENT_PROCESS_END) {
processes.erase(it);
}
}
break;
}
}
} else if (IsEqualGUID(provider_guid, ThreadGuid)) {
Assert(event_version == 3);
Event_Thread *event_thread = (Event_Thread *)event_record->UserData;
switch (event_type) {
case EVENT_THREAD_DC_START:
case EVENT_THREAD_START: {
ProfileProcess *process = processes[event_thread->process_id];
if (process) {
ProfileThread *thread = AddProfileThread(analysis);
thread->start_time = event_time;
thread->stop_time = 0;
thread->last_stack_time = 0;
thread->thread_id = event_thread->thread_id;
thread->process = process;
threads[thread->thread_id] = thread;
}
break;
}
case EVENT_THREAD_DC_END:
case EVENT_THREAD_END: {
auto it = threads.find(event_thread->thread_id);
if (it != threads.end()) {
ProfileThread *thread = it->second;
thread->stop_time = event_time;
if (event_type == EVENT_THREAD_END) {
threads.erase(it);
}
}
break;
}
}
} else if (IsEqualGUID(provider_guid, ImageLoadGuid)) {
Assert(event_version == 2);
Event_ImageLoad *event_image = (Event_ImageLoad*)event_record->UserData;
switch (event_type) {
case EVENT_IMAGE_DC_LOAD:
case EVENT_IMAGE_LOAD: {
ProfileProcess *process = processes[event_image->process_id];
Assert(process);
ProfileImage *image = AddProfileImage(analysis);
image->next_image = process->first_image;
process->first_image = image;
image->load_time = event_time;
image->unload_time = 0;
image->process = process;
image->base = event_image->image_base;
image->size = event_image->image_size;
uint32_t filename_size = WideCharToMultiByte(CP_ACP, 0, event_image->filename, -1, image->filename, 0, 0, 0) + 1;
image->filename = (char *)AllocateProfileData(analysis, filename_size);
WideCharToMultiByte(CP_ACP, 0, event_image->filename, -1, image->filename, filename_size, 0, 0);
image->filename[filename_size - 1] = 0;
image->name = image->filename;
image->name = image->filename + filename_size - 1;
while (image->name != image->filename && *image->name != '\\') {
image->name--;
}
if (*image->name == '\\') {
image->name++;
}
break;
}
case EVENT_IMAGE_DC_UNLOAD:
case EVENT_IMAGE_UNLOAD: {
ProfileProcess *process = processes[event_image->process_id];
Assert(process);
for (ProfileImage *image = process->first_image; image; image = image->next_image) {
if (image->base == event_image->image_base) {
image->unload_time = event_time;
break;
}
}
break;
}
}
}
}
void InitializeProfileAnalysis(ProfileAnalysis *analysis) {
AllocateRange(&analysis->processes_range, GB);
analysis->processes = (ProfileProcess *)analysis->processes_range.base;
analysis->processes_count = 0;
AllocateRange(&analysis->threads_range, GB);
analysis->threads = (ProfileThread *)analysis->threads_range.base;
analysis->threads_count = 0;
AllocateRange(&analysis->images_range, GB);
analysis->images = (ProfileImage *)analysis->images_range.base;
analysis->images_count = 0;
AllocateRange(&analysis->stacks_range, GB);
analysis->stacks = (ProfileStack *)analysis->stacks_range.base;
analysis->stacks_count = 0;
AllocateRange(&analysis->symbols_range, GB);
analysis->symbols = (ProfileSymbol *)analysis->symbols_range.base;
analysis->symbols_count = 0;
AllocateRange(&analysis->data_range, GB);
analysis->data = (char *)analysis->data_range.base;
analysis->data_end = analysis->data;
}
void ParseProfileCapture(ProfileAnalysis *analysis, ProfileCapture *capture) {
EVENT_TRACE_LOGFILE event_trace_logfile = {0};
event_trace_logfile.LogFileName = capture->filename;
event_trace_logfile.ProcessTraceMode = PROCESS_TRACE_MODE_EVENT_RECORD | PROCESS_TRACE_MODE_RAW_TIMESTAMP;
event_trace_logfile.EventRecordCallback = EventRecordCallback;
EventRecordCallbackContext context;
context.analysis = analysis;
context.event_trace_logfile = &event_trace_logfile;
event_trace_logfile.Context = &context;
TRACEHANDLE trace_handle = OpenTrace(&event_trace_logfile);
analysis->performance_frequency = event_trace_logfile.LogfileHeader.PerfFreq.QuadPart;
Assert(event_trace_logfile.EventsLost == 0);
ProcessTrace(&trace_handle, 1, 0, 0);
CloseTrace(trace_handle);
}
HANDLE OpenImageFile(char *filename) {
int filename_length = (int)strlen(filename);
int unicode_filename_size = sizeof(WCHAR) * filename_length;
WCHAR *unicode_filename = (WCHAR *)_alloca(unicode_filename_size);
int unicode_filename_length = MultiByteToWideChar(CP_ACP, 0, filename, filename_length, unicode_filename, unicode_filename_size);
Assert(unicode_filename_length == filename_length);
UNICODE_STRING unicode_string;
unicode_string.Buffer = unicode_filename;
unicode_string.Length = unicode_filename_size;
unicode_string.MaximumLength = unicode_filename_size;
OBJECT_ATTRIBUTES object_attributes = {0};
InitializeObjectAttributes(&object_attributes, &unicode_string, OBJ_CASE_INSENSITIVE, 0, 0);
IO_STATUS_BLOCK io_status_block = {0};
HANDLE file;
NTSTATUS status = NtCreateFile(&file, FILE_READ_DATA, &object_attributes, &io_status_block, 0, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN, 0, 0, 0);
if (NT_SUCCESS(status)) {
return file;
} else {
return INVALID_HANDLE_VALUE;
}
}
ProfileImage *GetInstructionPointerImage(ProfileAnalysis *analysis, ProfileProcess *process, void *instruction_pointer) {
for (ProfileImage *image = process->first_image; image; image = image->next_image) {
if (image->base <= instruction_pointer && instruction_pointer < (char *)image->base + image->size) {
return image;
}
}
ProfileProcess *process0 = analysis->processes;
if (process0) {
for (ProfileImage *image = process0->first_image; image; image = image->next_image) {
if (image->base <= instruction_pointer && instruction_pointer < (char *)image->base + image->size) {
return image;
}
}
}
return 0;
}
float GetProfileTimeInSeconds(ProfileAnalysis *analysis, uint64_t time) {
return (float)time / analysis->performance_frequency;
}
void SymbolizeProfileAnalysisProcess(ProfileAnalysis *analysis, uint32_t process_id) {
ProfileProcess *process = 0;
for (size_t i = 0; i < analysis->processes_count; i++) {
if (analysis->processes[i].process_id == process_id) {
process = analysis->processes + i;
break;
}
}
Assert(process);
Outputf("Process: %s, %.2fs\n", process->filename, GetProfileTimeInSeconds(analysis, process->stop_time - process->start_time));
HANDLE handle = (HANDLE)1;
BOOL success = SymInitialize(handle, 0, FALSE);
Assert(success);
for (ProfileImage *image = process->first_image; image; image = image->next_image) {
HANDLE image_file = OpenImageFile(image->filename);
if (image_file == INVALID_HANDLE_VALUE) {
continue;
}
DWORD64 base_address = SymLoadModuleExW(handle, image_file, 0, 0, (DWORD64)image->base, 0, 0, 0);
CloseHandle(image_file);
}
std::unordered_map<ULONG, ProfileSymbol *> pdb_index_to_symbol;
std::unordered_map<void *, ProfileSymbol *> instruction_pointer_to_symbol;
Range last_stack_range = {0};
AllocateRange(&last_stack_range, GB);
ProfileStack **last_stack = (ProfileStack **)last_stack_range.base;
ProfileStack **last_stack_end = last_stack + analysis->symbols_count;
CommitRange(&last_stack_range, last_stack, analysis->symbols_count * sizeof(ProfileStack *));
for (size_t i = 0; i < analysis->stacks_count; i++) {
ProfileStack *stack = (ProfileStack *)analysis->stacks + i;
if (stack->thread->process != process) {
continue;
}
for (size_t j = 0; j < stack->instruction_pointers_count; j++) {
void *instruction_pointer = stack->instruction_pointers[j];
auto it = instruction_pointer_to_symbol.find(instruction_pointer);
ProfileSymbol *symbol = 0;
if (it == instruction_pointer_to_symbol.end()) {
enum { MAX_PDB_SYMBOL_SIZE = 1024 };
char pdb_symbol_data[MAX_PDB_SYMBOL_SIZE];
SYMBOL_INFO *pdb_symbol = (SYMBOL_INFO *)pdb_symbol_data;
pdb_symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
pdb_symbol->MaxNameLen = MAX_PDB_SYMBOL_SIZE - sizeof(SYMBOL_INFO) - 1;
if (SymFromAddr(handle, (DWORD64)instruction_pointer, 0, pdb_symbol)) {
symbol = pdb_index_to_symbol[pdb_symbol->Index];
if (!symbol) {
symbol = AddProfileSymbol(analysis);
symbol->image = GetInstructionPointerImage(analysis, process, instruction_pointer);
symbol->name = (char *)AllocateProfileData(analysis, pdb_symbol->NameLen + 1);
symbol->exclusive_count = 0;
symbol->inclusive_count = 0;
symbol->exclusive_time = 0;
symbol->inclusive_time = 0;
memcpy(symbol->name, pdb_symbol->Name, pdb_symbol->NameLen + 1);
CommitRange(&last_stack_range, last_stack_end, sizeof(ProfileStack *));
last_stack_end++;
pdb_index_to_symbol[pdb_symbol->Index] = symbol;
}
}
instruction_pointer_to_symbol[instruction_pointer] = symbol;
} else {
symbol = it->second;
}
stack->symbols[j] = symbol;
if (symbol) {
if (j == 0) {
symbol->exclusive_count++;
symbol->exclusive_time += stack->timespan;
}
size_t symbol_index = symbol - analysis->symbols;
if (last_stack[symbol_index] != stack) {
symbol->inclusive_count++;
symbol->inclusive_time += stack->timespan;
last_stack[symbol_index] = stack;
}
}
}
}
FreeRange(&last_stack_range);
SymCleanup(handle);
}
void OutputInclusiveCounts(ProfileAnalysis *analysis, uint32_t process_id) {
std::vector<std::pair<int, ProfileSymbol *>> counts;
Outputf("Inclusive counts:\n");
for (size_t i = 0; i < analysis->symbols_count; i++) {
ProfileSymbol *symbol = analysis->symbols + i;
if (symbol->inclusive_count && symbol->image->process->process_id == process_id) {
counts.push_back(std::make_pair(symbol->inclusive_count, symbol));
}
}
std::sort(counts.begin(), counts.end());
for (auto it = counts.rbegin(); it != counts.rend(); ++it) {
Outputf(" %5ux %s\n", it->first, it->second->name);
}
}
void OutputExclusiveCounts(ProfileAnalysis *analysis, uint32_t process_id) {
std::vector<std::pair<int, ProfileSymbol *>> counts;
Outputf("Exclusive counts:\n");
for (size_t i = 0; i < analysis->symbols_count; i++) {
ProfileSymbol *symbol = analysis->symbols + i;
if (symbol->exclusive_count && symbol->image->process->process_id == process_id) {
counts.push_back(std::make_pair(symbol->exclusive_count, symbol));
}
}
std::sort(counts.begin(), counts.end());
for (auto it = counts.rbegin(); it != counts.rend(); ++it) {
Outputf(" %5ux %s\n", it->first, it->second->name);
}
}
void OutputInclusiveTime(ProfileAnalysis *analysis, uint32_t process_id) {
std::vector<std::pair<uint64_t, ProfileSymbol *>> times;
Outputf("Inclusive time:\n");
for (size_t i = 0; i < analysis->symbols_count; i++) {
ProfileSymbol *symbol = analysis->symbols + i;
if (symbol->inclusive_time && symbol->image->process->process_id == process_id) {
times.push_back(std::make_pair(symbol->inclusive_time, symbol));
}
}
std::sort(times.begin(), times.end());
for (auto it = times.rbegin(); it != times.rend(); ++it) {
Outputf(" %5.2fs %s\n", GetProfileTimeInSeconds(analysis, it->first), it->second->name);
}
}
void OutputExclusiveTime(ProfileAnalysis *analysis, uint32_t process_id) {
std::vector<std::pair<uint64_t, ProfileSymbol *>> times;
Outputf("Exclusive time:\n");
for (size_t i = 0; i < analysis->symbols_count; i++) {
ProfileSymbol *symbol = analysis->symbols + i;
if (symbol->exclusive_time && symbol->image->process->process_id == process_id) {
times.push_back(std::make_pair(symbol->exclusive_time, symbol));
}
}
std::sort(times.begin(), times.end());
for (auto it = times.rbegin(); it != times.rend(); ++it) {
Outputf(" %5.2fs %s\n", GetProfileTimeInSeconds(analysis, it->first), it->second->name);
}
}
// Profile recording
uint32_t FindProfileId(ProfileAnalysis *analysis, char *filename) {
for (size_t i = 0; i < analysis->processes_count; i++) {
if (strcmp(analysis->processes[i].filename, filename) == 0) {
return analysis->processes[i].process_id;
}
}
return 0;
}
void StartProfileCapture(ProfileCapture *capture) {
if (capture->delete_old_capture) {
if (!DeleteFile(capture->filename)) {
Assert(GetLastError() == ERROR_SUCCESS || GetLastError() == ERROR_FILE_NOT_FOUND);
}
}
size_t filename_size = (wcslen(capture->filename) + 1) * sizeof(WCHAR);
size_t buffer_size = sizeof(EVENT_TRACE_PROPERTIES) + sizeof(KERNEL_LOGGER_NAME) + filename_size;
capture->properties = (EVENT_TRACE_PROPERTIES *)malloc(buffer_size);
Assert(capture->properties);
ZeroMemory(capture->properties, buffer_size);
capture->properties->Wnode.BufferSize = (ULONG)buffer_size;
capture->properties->Wnode.Flags = WNODE_FLAG_TRACED_GUID;
capture->properties->Wnode.ClientContext = 1; // QueryPerformanceCounter
capture->properties->Wnode.Guid = SystemTraceControlGuid;
capture->properties->EnableFlags = EVENT_TRACE_FLAG_PROFILE | EVENT_TRACE_FLAG_IMAGE_LOAD | EVENT_TRACE_FLAG_THREAD | EVENT_TRACE_FLAG_PROCESS;
capture->properties->LogFileMode = EVENT_TRACE_FILE_MODE_APPEND;
capture->properties->MaximumFileSize = capture->maximum_size_in_megabytes;
capture->properties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES);
capture->properties->LogFileNameOffset = capture->properties->LoggerNameOffset + sizeof(KERNEL_LOGGER_NAME);
CopyMemory((char *)capture->properties + capture->properties->LogFileNameOffset, capture->filename, filename_size);
ULONG status;
status = ControlTrace(0, KERNEL_LOGGER_NAME, capture->properties, EVENT_TRACE_CONTROL_STOP);
Assert(status == ERROR_SUCCESS || status == ERROR_WMI_INSTANCE_NOT_FOUND);
status = StartTrace((PTRACEHANDLE)&capture->trace_handle, KERNEL_LOGGER_NAME, capture->properties);
Assert(status == ERROR_SUCCESS);
CLASSIC_EVENT_ID event_id = {PerfInfoGuid, EVENT_SAMPLED_PROFILE};
status = TraceSetInformation(capture->trace_handle, TraceStackTracingInfo, &event_id, sizeof(event_id));
Assert(status == ERROR_SUCCESS);
}
void StopProfileCapture(ProfileCapture *capture) {
ULONG status = ControlTrace(capture->trace_handle, KERNEL_LOGGER_NAME, capture->properties, EVENT_TRACE_CONTROL_STOP);
Assert(status == ERROR_SUCCESS);
}
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) {
QueryPerformanceFrequency((LARGE_INTEGER *)&timer_frequency);
ElevatePrivilege();
ProfileCapture capture;
capture.filename = L"d:\\temp\\temp.etl";
capture.delete_old_capture = true;
capture.maximum_size_in_megabytes = 100;
output_file = CreateFileA("zzz_output.txt", GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
#if 0
StartProfileCapture(&capture);
MessageBoxA(0, "Press enter to stop trace.", "zzz", MB_OK);
StopProfileCapture(&capture);
return 0;
#endif
uint64_t timer;
ProfileAnalysis analysis;
InitializeProfileAnalysis(&analysis);
ResetTimer(&timer);
ParseProfileCapture(&analysis, &capture);
Outputf("Parsing time: %llums\n", GetTimerMilliseconds(&timer));
uint32_t process_id = FindProfileId(&analysis, "BTree.exe");
Assert(process_id);
ResetTimer(&timer);
SymbolizeProfileAnalysisProcess(&analysis, 0);
SymbolizeProfileAnalysisProcess(&analysis, process_id);
Outputf("Symbolization time: %llums\n", GetTimerMilliseconds(&timer));
OutputExclusiveTime(&analysis, process_id);
OutputInclusiveTime(&analysis, process_id);
OutputExclusiveCounts(&analysis, process_id);
OutputInclusiveCounts(&analysis, process_id);
if (output_file != INVALID_HANDLE_VALUE) {
CloseHandle(output_file);
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment