129 lines
3.0 KiB
C
129 lines
3.0 KiB
C
////////////////////////////////
|
|
// NOTE(allen): Profiling by Spall
|
|
|
|
// includes
|
|
|
|
#if ENABLE_MANUAL_PROFILE || ENABLE_AUTO_PROFILE
|
|
# error cannot build profiling-by-spall with profiling enabled
|
|
#endif
|
|
|
|
#define PROFILER_SPALL 1
|
|
#define ENABLE_MANUAL_PROFILE 1
|
|
|
|
#include "base/base_context.h"
|
|
#include "base/base_macros.h"
|
|
#include "base/base_basic_types.h"
|
|
#include "base/base_intrinsic.h"
|
|
#include "base_profiling.h"
|
|
|
|
#include "base_profiling_auto.c"
|
|
|
|
#define SPALL_BUFFER_CAP MB(1)
|
|
|
|
// linkable global/thread variables
|
|
|
|
SpallProfile spall_ctx = {0};
|
|
threadvar SpallBuffer spall_buffer = {0};
|
|
|
|
// windows implementation
|
|
#if OS_WINDOWS
|
|
|
|
#undef function
|
|
#include <Windows.h>
|
|
#define function static
|
|
|
|
function U64
|
|
spallprof_tick_per_usecond(void){
|
|
typedef int QueryInfoType(S32, void*, U32, U32*);
|
|
|
|
U64 result = Billion(3);
|
|
B32 accurate = 0;
|
|
|
|
HMODULE ntdll = LoadLibrary("ntdll.dll");
|
|
if (ntdll != 0){
|
|
QueryInfoType *NtQuerySystemInformation =
|
|
(QueryInfoType*)GetProcAddress(ntdll, "NtQuerySystemInformation");
|
|
if (NtQuerySystemInformation != 0){
|
|
// TODO(allen): audit
|
|
volatile U64 *hsuv = 0;
|
|
U32 size = 0;
|
|
S32 query_result =
|
|
NtQuerySystemInformation(0xc5, (void**)&hsuv, sizeof(hsuv), &size);
|
|
if (size == sizeof(hsuv) && query_result >= 0){
|
|
result = (Million(10) << 32)/(hsuv[1] >> 32);
|
|
accurate = 1;
|
|
}
|
|
}
|
|
FreeLibrary(ntdll);
|
|
}
|
|
|
|
if (!accurate){
|
|
// TODO(allen): This is forcing any program that is built with profiling
|
|
// by spall to also link as a 'graphical' program. How else could I deal
|
|
// with this bit?
|
|
MessageBoxA(0, "Could not acquire accurate rdtsc/sec value",
|
|
"Profiling by Spall: Error", MB_OK);
|
|
}
|
|
|
|
return(result);
|
|
}
|
|
|
|
function void*
|
|
spallprof_alloc(U64 size){
|
|
void *result = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
|
|
Assert(result != 0);
|
|
return(result);
|
|
}
|
|
|
|
function void
|
|
spallprof_free(void *ptr, U64 size){
|
|
VirtualFree(ptr, 0, MEM_RELEASE);
|
|
}
|
|
|
|
#else
|
|
# error need no implementation for profiling by spall on this os
|
|
#endif
|
|
|
|
// profiling interface implementation
|
|
|
|
link_function void
|
|
prof_open(char *name){
|
|
U64 tick_per_usecond = spallprof_tick_per_usecond();
|
|
spall_ctx = spall_init_file(name, 1000000./tick_per_usecond);
|
|
}
|
|
|
|
link_function void
|
|
prof_close(void){
|
|
spall_quit(&spall_ctx);
|
|
}
|
|
|
|
link_function void
|
|
prof_thread_begin(void){
|
|
void *buffer = spallprof_alloc(SPALL_BUFFER_CAP);
|
|
spall_buffer.length = SPALL_BUFFER_CAP;
|
|
spall_buffer.data = buffer;
|
|
spall_buffer_init(&spall_ctx, &spall_buffer);
|
|
}
|
|
|
|
link_function void
|
|
prof_thread_end(void){
|
|
spall_buffer_quit(&spall_ctx, &spall_buffer);
|
|
spallprof_free(spall_buffer.data, spall_buffer.length);
|
|
MemoryZeroStruct(&spall_buffer);
|
|
}
|
|
|
|
link_function void
|
|
prof_thread_flush(void){
|
|
spall_buffer_flush(&spall_ctx, &spall_buffer);
|
|
}
|
|
|
|
link_function void
|
|
prof_begin(char *name, U32 len){
|
|
spall_buffer_begin(&spall_ctx, &spall_buffer, name, len, intrinsic_rdtsc());
|
|
}
|
|
|
|
link_function void
|
|
prof_end(void){
|
|
spall_buffer_end(&spall_ctx, &spall_buffer, intrinsic_rdtsc());
|
|
}
|