diff options
author | Aaron Giles <aaron@aarongiles.com> | 2010-06-30 20:29:12 +0000 |
---|---|---|
committer | Aaron Giles <aaron@aarongiles.com> | 2010-06-30 20:29:12 +0000 |
commit | 77f2ea0e3edd9054a9cb13a61c35c3fad2c6d0e4 (patch) | |
tree | a3e68124ea4515621aba032e4e041ca20cc398af /src/osd/windows/winmain.c | |
parent | 7f53ab90fe508853ee11ce672a3c8316661fe6c2 (diff) |
Resurrect the old sampling profiler, and improve it to be useful:
- always available now, just specify -profile <n> to enable it
- supports stack walking to uniquely identify call chains; the <n> parameter
to the -profile option specifies how deep to go
- automatically turns off throttling and multithreading, and sets the number of
processors available to 1 (since we only sample the main thread)
- output now uses the common symbol lookup, which actually uses the PDB for
MSVC builds and gives excellent results (may eventually figure out how to
extract gcc symbols someday)
- the top 30 unique call chains are output
Diffstat (limited to 'src/osd/windows/winmain.c')
-rw-r--r-- | src/osd/windows/winmain.c | 706 |
1 files changed, 423 insertions, 283 deletions
diff --git a/src/osd/windows/winmain.c b/src/osd/windows/winmain.c index f3a0964cdfd..13ce34acb7c 100644 --- a/src/osd/windows/winmain.c +++ b/src/osd/windows/winmain.c @@ -71,13 +71,12 @@ #include "winutil.h" #include "debugger.h" -#define ENABLE_PROFILER 0 #define DEBUG_SLOW_LOCKS 0 -//============================================================ -// TYPE DEFINITIONS -//============================================================ +//************************************************************************** +// MACROS +//************************************************************************** #ifdef UNICODE #define UNICODE_POSTFIX "W" @@ -85,6 +84,46 @@ #define UNICODE_POSTFIX "A" #endif + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +class sampling_profiler +{ +public: + sampling_profiler(UINT32 max_seconds, UINT8 stack_depth); + ~sampling_profiler(); + + void start(); + void stop(); + +// void reset(); + void print_results(); + +private: + static DWORD WINAPI thread_entry(LPVOID lpParameter); + void thread_run(); + + static int CLIB_DECL compare_address(const void *item1, const void *item2); + static int CLIB_DECL compare_frequency(const void *item1, const void *item2); + + HANDLE m_target_thread; + + HANDLE m_thread; + DWORD m_thread_id; + volatile bool m_thread_exit; + + UINT8 m_stack_depth; + UINT8 m_entry_stride; + UINT32 m_max_seconds; + FPTR * m_buffer; + FPTR * m_buffer_ptr; + FPTR * m_buffer_end; +}; + + typedef BOOL (WINAPI *try_enter_critical_section_ptr)(LPCRITICAL_SECTION lpCriticalSection); typedef HANDLE (WINAPI *av_set_mm_thread_characteristics_ptr)(LPCTSTR TaskName, LPDWORD TaskIndex); @@ -105,17 +144,18 @@ typedef BOOL (WINAPI *get_module_information_ptr)(HANDLE hProcess, HMODULE hModu -//============================================================ +//************************************************************************** // GLOBAL VARIABLES -//============================================================ +//************************************************************************** // this line prevents globbing on the command line int _CRT_glob = 0; -//============================================================ + +//************************************************************************** // LOCAL VARIABLES -//============================================================ +//************************************************************************** static try_enter_critical_section_ptr try_enter_critical_section; @@ -153,11 +193,13 @@ static int timeresult; //static MMRESULT result; static TIMECAPS caps; +static sampling_profiler *profiler = NULL; -//============================================================ -// PROTOTYPES -//============================================================ + +//************************************************************************** +// FUNCTION PROTOTYPES +//************************************************************************** static void osd_exit(running_machine &machine); @@ -165,16 +207,14 @@ static void soft_link_functions(void); static int is_double_click_start(int argc); static DWORD WINAPI watchdog_thread_entry(LPVOID lpParameter); static LONG WINAPI exception_filter(struct _EXCEPTION_POINTERS *info); -static const char *lookup_symbol(FPTR address); - -static void start_profiler(void); -static void stop_profiler(void); +static const char *lookup_symbol(FPTR address, FPTR *symbase = NULL, bool cache = false); +static void winui_output_error(void *param, const char *format, va_list argptr); -//============================================================ +//************************************************************************** // OPTIONS -//============================================================ +//************************************************************************** // struct definitions const options_entry mame_win_options[] = @@ -191,6 +231,7 @@ const options_entry mame_win_options[] = { "priority(-15-1)", "0", 0, "thread priority for the main game thread; range from -15 to 1" }, { "multithreading;mt", "0", OPTION_BOOLEAN, "enable multithreading; this enables rendering and blitting on a separate thread" }, { "numprocessors;np", "auto", 0, "number of processors; this overrides the number the system reports" }, + { "profile", "0", 0, "enable profiling, specifying the stack depth to track" }, // video options { NULL, NULL, OPTION_HEADER, "WINDOWS VIDEO OPTIONS" }, @@ -260,22 +301,50 @@ const options_entry mame_win_options[] = }; -//============================================================ -// winui_output_error -//============================================================ -static void winui_output_error(void *param, const char *format, va_list argptr) +//************************************************************************** +// INLINE FUNCTIONS +//************************************************************************** + +#ifdef PTR64 +#define IMAGE_FILE_MACHINE_NATIVE IMAGE_FILE_MACHINE_AMD64 +inline FPTR ip_from_context(CONTEXT &context) { - char buffer[1024]; + return context.Rip; +} +inline void init_stack_frame(STACKFRAME64 &frame, CONTEXT &context) +{ + memset(&frame, 0, sizeof(frame)); + frame.AddrPC.Offset = context.Rip; + frame.AddrPC.Mode = AddrModeFlat; + frame.AddrFrame.Offset = context.Rsp; + frame.AddrFrame.Mode = AddrModeFlat; + frame.AddrStack.Offset = context.Rsp; + frame.AddrStack.Mode = AddrModeFlat; +} +#else +#define IMAGE_FILE_MACHINE_NATIVE IMAGE_FILE_MACHINE_I386 +inline FPTR ip_from_context(CONTEXT &context) +{ + return context.Eip; +} +inline void init_stack_frame(STACKFRAME64 &frame, CONTEXT &context) +{ + memset(&frame, 0, sizeof(frame)); + frame.AddrPC.Offset = context.Eip; + frame.AddrPC.Mode = AddrModeFlat; + frame.AddrFrame.Offset = context.Esp; + frame.AddrFrame.Mode = AddrModeFlat; + frame.AddrStack.Offset = context.Esp; + frame.AddrStack.Mode = AddrModeFlat; +} +#endif - // if we are in fullscreen mode, go to windowed mode - if ((video_config.windowed == 0) && (win_window_list != NULL)) - winwindow_toggle_full_screen(); - vsnprintf(buffer, ARRAY_LENGTH(buffer), format, argptr); - win_message_box_utf8(win_window_list ? win_window_list->hwnd : NULL, buffer, APPNAME, MB_OK); -} +//************************************************************************** +// MAIN ENTRY POINT +//************************************************************************** //============================================================ @@ -320,6 +389,24 @@ int main(int argc, char *argv[]) //============================================================ +// winui_output_error +//============================================================ + +static void winui_output_error(void *param, const char *format, va_list argptr) +{ + char buffer[1024]; + + // if we are in fullscreen mode, go to windowed mode + if ((video_config.windowed == 0) && (win_window_list != NULL)) + winwindow_toggle_full_screen(); + + vsnprintf(buffer, ARRAY_LENGTH(buffer), format, argptr); + win_message_box_utf8(win_window_list ? win_window_list->hwnd : NULL, buffer, APPNAME, MB_OK); +} + + + +//============================================================ // output_oslog //============================================================ @@ -335,9 +422,17 @@ static void output_oslog(running_machine &machine, const char *buffer) void osd_init(running_machine *machine) { - int watchdog = options_get_int(machine->options(), WINOPTION_WATCHDOG); const char *stemp; + // determine if we are profiling, and adjust options appropriately + int profile = options_get_int(machine->options(), WINOPTION_PROFILE); + if (profile > 0) + { + options_set_bool(machine->options(), OPTION_THROTTLE, false, OPTION_PRIORITY_MAXIMUM); + options_set_bool(machine->options(), WINOPTION_MULTITHREADING, false, OPTION_PRIORITY_MAXIMUM); + options_set_bool(machine->options(), WINOPTION_NUMPROCESSORS, 1, OPTION_PRIORITY_MAXIMUM); + } + // thread priority if (!(machine->debug_flags & DEBUG_FLAG_OSD_ENABLED)) SetThreadPriority(GetCurrentThread(), options_get_int(machine->options(), WINOPTION_PRIORITY)); @@ -380,9 +475,8 @@ void osd_init(running_machine *machine) // if (av_set_mm_thread_characteristics != NULL) // mm_task = (*av_set_mm_thread_characteristics)(TEXT("Playback"), &task_index); - start_profiler(); - // if a watchdog thread is requested, create one + int watchdog = options_get_int(machine->options(), WINOPTION_WATCHDOG); if (watchdog != 0) { watchdog_reset_event = CreateEvent(NULL, FALSE, FALSE, NULL); @@ -392,6 +486,13 @@ void osd_init(running_machine *machine) watchdog_thread = CreateThread(NULL, 0, watchdog_thread_entry, (LPVOID)(FPTR)watchdog, 0, NULL); assert_always(watchdog_thread != NULL, "Failed to create watchdog thread"); } + + // create and start the profiler + if (profile > 0) + { + profiler = global_alloc(sampling_profiler(1000, profile - 1)); + profiler->start(); + } } @@ -414,7 +515,13 @@ static void osd_exit(running_machine &machine) watchdog_thread = NULL; } - stop_profiler(); + // stop the profiler + if (profiler != NULL) + { + profiler->stop(); + profiler->print_results(); + global_free(profiler); + } // turn off our multimedia tasks // if (av_revert_mm_thread_characteristics != NULL) @@ -654,37 +761,10 @@ static LONG WINAPI exception_filter(struct _EXCEPTION_POINTERS *info) fprintf(stderr, "-----------------------------------------------------\n"); fprintf(stderr, "Stack crawl:\n"); - memset(&stackframe, 0, sizeof(stackframe)); -#ifdef PTR64 - stackframe.AddrPC.Offset = context.Rip; - stackframe.AddrFrame.Offset = context.Rsp; - stackframe.AddrStack.Offset = context.Rsp; -#else - stackframe.AddrPC.Offset = context.Eip; - stackframe.AddrFrame.Offset = context.Ebp; - stackframe.AddrStack.Offset = context.Esp; -#endif - stackframe.AddrPC.Mode = AddrModeFlat; - stackframe.AddrFrame.Mode = AddrModeFlat; - stackframe.AddrStack.Mode = AddrModeFlat; - - while (stack_walk_64( -#ifdef PTR64 - IMAGE_FILE_MACHINE_AMD64, -#else - IMAGE_FILE_MACHINE_I386, -#endif - GetCurrentProcess(), - GetCurrentThread(), - &stackframe, - &context, - NULL, - sym_function_table_access_64, - sym_get_module_base_64, - NULL)) - { + // walk the stack + init_stack_frame(stackframe, context); + while (stack_walk_64(IMAGE_FILE_MACHINE_NATIVE, GetCurrentProcess(), GetCurrentThread(), &stackframe, &context, NULL, sym_function_table_access_64, sym_get_module_base_64, NULL)) fprintf(stderr, " %p: %p%s\n", (void *)stackframe.AddrFrame.Offset, (void *)stackframe.AddrPC.Offset, lookup_symbol((FPTR)stackframe.AddrPC.Offset)); - } } // exit @@ -749,8 +829,15 @@ static const char *line_to_symbol(const char *line, FPTR &address) // lookup_symbol //============================================================ -static const char *lookup_symbol(FPTR address) +static const char *lookup_symbol(FPTR address, FPTR *symbase, bool cache) { + struct symbol_entry + { + symbol_entry *next; + FPTR address; + char name[1]; + }; + static symbol_entry *symbol_cache; static char buffer[1024]; // first try to do it formally @@ -769,294 +856,347 @@ static const char *lookup_symbol(FPTR address) // try to get source info as well; again we are returned an ANSI string if (sym_get_line_from_addr_64 != NULL && sym_get_line_from_addr_64(GetCurrentProcess(), address, &linedisp, &lineinfo)) - sprintf(buffer, " (%s+0x%04x, %s:%d)", info.Name, (UINT32)displacement, lineinfo.FileName, (int)lineinfo.LineNumber); + { + if (displacement != 0) + sprintf(buffer, " (%s+0x%04x, %s:%d)", info.Name, (UINT32)displacement, lineinfo.FileName, (int)lineinfo.LineNumber); + else + sprintf(buffer, " (%s, %s:%d)", info.Name, lineinfo.FileName, (int)lineinfo.LineNumber); + } else - sprintf(buffer, " (%s+0x%04x)", info.Name, (UINT32)displacement); + { + if (displacement != 0) + sprintf(buffer, " (%s+0x%04x)", info.Name, (UINT32)displacement); + else + sprintf(buffer, " (%s)", info.Name); + } + + // set the symbase + if (symbase != NULL) + *symbase = address - displacement; return buffer; } - - // see if we have a map file - FILE *map = fopen(mapfile_name, "r"); - if (map == NULL) - return " (no map)"; - + // reset the bests astring best_symbol; FPTR best_addr = 0; - // parse the file, looking for map entries - char line[1024]; - while (fgets(line, sizeof(line) - 1, map)) + // if we have a cache, look it up there + if (symbol_cache != NULL) + { + // scan the cache looking for the best match + for (symbol_entry *cursym = symbol_cache; cursym != NULL; cursym = cursym->next) + if (cursym->address <= address && cursym->address > best_addr) + { + best_addr = cursym->address; + best_symbol.cpy(cursym->name); + } + } + + // otherwise, walk the map file, generating a cache if we are allowed to + else { - FPTR addr; - const char *symbol = line_to_symbol(line, addr); - if (symbol != NULL && addr <= address && addr > best_addr) + symbol_entry **tailptr = &symbol_cache; + + // see if we have a map file + FILE *map = fopen(mapfile_name, "r"); + if (map == NULL) + return " (no map)"; + + // parse the file, looking for map entries + char line[1024]; + while (fgets(line, sizeof(line) - 1, map)) { - best_addr = addr; - best_symbol.cpy(symbol); + FPTR addr; + const char *symbol = line_to_symbol(line, addr); + + // if we got one, see if this is the best + if (symbol != NULL) + { + if (addr <= address && addr > best_addr) + { + best_addr = addr; + best_symbol.cpy(symbol); + } + + // also create a cache entry if we can + if (cache) + { + symbol_entry *entry = reinterpret_cast<symbol_entry *>(osd_malloc(sizeof(symbol_entry) + strlen(symbol))); + entry->next = NULL; + entry->address = addr; + strcpy(entry->name, symbol); + *tailptr = entry; + tailptr = &entry->next; + } + } } + fclose(map); } - fclose(map); - - // create the final result + + // generate the string if (address - best_addr > 0x10000) - return " (unknown)"; - sprintf(buffer, " (%s+0x%04x)", best_symbol.trimspace().cstr(), (UINT32)(address - best_addr)); + strcpy(buffer, " (unknown)");\ + else if (address != best_addr) + sprintf(buffer, " (%s+0x%04x)", best_symbol.trimspace().cstr(), (UINT32)(address - best_addr)); + else + sprintf(buffer, " (%s)", best_symbol.trimspace().cstr()); + + // set the symbase + if (symbase != NULL) + *symbase = best_addr; return buffer; } -#if ENABLE_PROFILER +//************************************************************************** +// SAMPLING PROFILER +//************************************************************************** -//============================================================ -// -// profiler.c - Sampling profiler -// -//============================================================ +//------------------------------------------------- +// sampling_profiler - constructor +//------------------------------------------------- -//============================================================ -// TYPE DEFINITIONS -//============================================================ - -#define MAX_SYMBOLS 65536 - -typedef struct _map_entry map_entry; - -struct _map_entry +sampling_profiler::sampling_profiler(UINT32 max_seconds, UINT8 stack_depth = 0) + : m_thread(NULL), + m_thread_id(0), + m_thread_exit(false), + m_stack_depth(stack_depth), + m_entry_stride(stack_depth + 3), + m_max_seconds(max_seconds), + m_buffer(global_alloc(FPTR[max_seconds * 1000 * m_entry_stride])), + m_buffer_ptr(m_buffer), + m_buffer_end(m_buffer + max_seconds * 1000 * m_entry_stride) { - FPTR start; - FPTR end; - UINT64 hits; - char *name; -}; - - - -//============================================================ -// LOCAL VARIABLES -//============================================================ - -static map_entry symbol_map[MAX_SYMBOLS]; -static int map_entries; - -static HANDLE profiler_thread; -static DWORD profiler_thread_id; -static volatile UINT8 profiler_thread_exit; - - - -//============================================================ -// compare_base -// compare_hits -- qsort callbacks to sort on -//============================================================ - -static int CLIB_DECL compare_start(const void *item1, const void *item2) -{ - return ((const map_entry *)item1)->start - ((const map_entry *)item2)->start; } -static int CLIB_DECL compare_hits(const void *item1, const void *item2) -{ - return ((const map_entry *)item2)->hits - ((const map_entry *)item1)->hits; -} +//------------------------------------------------- +// sampling_profiler - destructor +//------------------------------------------------- -//============================================================ -// add_symbol_map_entry -// parse_map_file -//============================================================ - -static void add_symbol_map_entry(UINT32 start, const char *name) +sampling_profiler::~sampling_profiler() { - if (map_entries == MAX_SYMBOLS) - fatalerror("Symbol table full"); - symbol_map[map_entries].start = start; - symbol_map[map_entries].name = core_strdup(name); - if (symbol_map[map_entries].name == NULL) - fatalerror("Out of memory"); - map_entries++; + global_free(m_buffer); } -static void parse_map_file(void) -{ - int got_text = 0; - char line[1024]; - FILE *map; - int i; - // open the map file - map = fopen(mapfile_name, "r"); - if (!map) - return; +//------------------------------------------------- +// start - begin gathering profiling information +//------------------------------------------------- - // parse out the various symbols into map entries - map_entries = 0; - while (fgets(line, sizeof(line) - 1, map)) +void sampling_profiler::start() +{ + // fail if we don't have everything we need + if (m_stack_depth > 0 && + (stack_walk_64 == NULL || + sym_initialize == NULL || + sym_function_table_access_64 == NULL || + sym_get_module_base_64 == NULL || + sym_from_addr == NULL || + get_module_information == NULL)) { - /* look for the code boundaries */ - if (!got_text && !strncmp(line, ".text 0x", 18)) - { - UINT32 base, size; - if (sscanf(line, ".text 0x%08x 0x%x", &base, &size) == 2) - { - add_symbol_map_entry(base, "Code start"); - add_symbol_map_entry(base+size, "Other"); - got_text = 1; - } - } - - /* look for symbols */ - FPTR addr; - const char *symbol = line_to_symbol(line, addr); - if (symbol != NULL) - add_symbol_map_entry(addr, symbol); + mame_printf_warning("Unable to start profiler because not all needed APIs are available\n"); + m_stack_depth = 0; + m_entry_stride = 3; } + + // do the dance to get a handle to ourself + BOOL result = DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &m_target_thread, + THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME | THREAD_QUERY_INFORMATION, FALSE, 0); + assert_always(result, "Failed to get thread handle for main thread"); - /* add a symbol for end-of-memory */ - add_symbol_map_entry(~0, "<end>"); - map_entries--; - - /* close the file */ - fclose(map); + // reset the exit flag + m_thread_exit = false; - /* sort by address */ - qsort(symbol_map, map_entries, sizeof(symbol_map[0]), compare_start); + // start the thread + m_thread = CreateThread(NULL, 0, thread_entry, (LPVOID)this, 0, &m_thread_id); + assert_always(m_thread != NULL, "Failed to create profiler thread\n"); - /* fill in the end of each bucket */ - for (i = 0; i < map_entries; i++) - symbol_map[i].end = symbol_map[i+1].start ? (symbol_map[i+1].start - 1) : 0; + // max out the priority + SetThreadPriority(m_thread, THREAD_PRIORITY_TIME_CRITICAL); } -//============================================================ -// free_symbol_map -//============================================================ +//------------------------------------------------- +// stop - stop gathering profiling information +//------------------------------------------------- -static void free_symbol_map(void) +void sampling_profiler::stop() { - int i; - - for (i = 0; i <= map_entries; i++) - { - free(symbol_map[i].name); - symbol_map[i].name = NULL; - } + // set the flag and wait a couple of seconds (max) + m_thread_exit = true; + WaitForSingleObject(m_thread, 2000); + + // regardless, close the handle + CloseHandle(m_thread); } -//============================================================ -// output_symbol_list -//============================================================ +//------------------------------------------------- +// compare_address - compare two entries by their +// bucket address +//------------------------------------------------- -static void output_symbol_list(FILE *f) +int CLIB_DECL sampling_profiler::compare_address(const void *item1, const void *item2) { - map_entry *entry; - int i; - - /* sort by hits */ - qsort(symbol_map, map_entries, sizeof(symbol_map[0]), compare_hits); - - for (i = 0, entry = symbol_map; i < map_entries; i++, entry++) - if (entry->hits > 0) - fprintf(f, "%10d %08X-%08X %s\n", entry->hits, entry->start, entry->end, entry->name); + const FPTR *entry1 = reinterpret_cast<const FPTR *>(item1); + const FPTR *entry2 = reinterpret_cast<const FPTR *>(item2); + int mincount = MIN(entry1[0], entry2[0]); + + // sort in order of: bucket, EIP, caller, caller's caller, etc. + for (int index = 1; index <= mincount; index++) + if (entry1[index] != entry2[index]) + return entry1[index] - entry2[index]; + + // if we match to the end, sort by the depth of the stack + return entry1[0] - entry2[0]; } +//------------------------------------------------- +// compare_frequency - compare two entries by +// their frequency of occurrence +//------------------------------------------------- -//============================================================ -// increment_bucket -//============================================================ - -static void increment_bucket(UINT32 addr) +int CLIB_DECL sampling_profiler::compare_frequency(const void *item1, const void *item2) { - int i; + const FPTR *entry1 = reinterpret_cast<const FPTR *>(item1); + const FPTR *entry2 = reinterpret_cast<const FPTR *>(item2); - for (i = 0; i < map_entries; i++) - if (addr <= symbol_map[i].end) - { - symbol_map[i].hits++; - return; - } + // sort by frequency, then by address + if (entry1[0] != entry2[0]) + return entry2[0] - entry1[0]; + return entry1[1] - entry2[1]; } +//------------------------------------------------- +// print_results - output the results +//------------------------------------------------- -//============================================================ -// profiler_thread -//============================================================ - -static DWORD WINAPI profiler_thread_entry(LPVOID lpParameter) +void sampling_profiler::print_results() { - HANDLE mainThread = (HANDLE)lpParameter; - CONTEXT context; - - /* loop until done */ - memset(&context, 0, sizeof(context)); - while (!profiler_thread_exit) + // step 1: find the base of each entry + for (FPTR *current = m_buffer; current < m_buffer_ptr; current += m_entry_stride) { - /* pause the main thread and get its context */ - SuspendThread(mainThread); - context.ContextFlags = CONTEXT_FULL; - GetThreadContext(mainThread, &context); - ResumeThread(mainThread); - - /* add to the bucket */ - increment_bucket(context.Eip); - - /* sleep */ - Sleep(1); + assert(current[0] >= 2 && current[0] < m_entry_stride); + assert(current[1] == 0); + + // entry[1] has the PC address; use lookup_symbol to get the base of the function + // and use that as our bucket + lookup_symbol(current[2], ¤t[1], true); + } + + // step 2: sort the results + qsort(m_buffer, (m_buffer_ptr - m_buffer) / m_entry_stride, m_entry_stride * sizeof(FPTR), compare_address); + + // step 3: count and collapse unique entries + UINT32 total_count = 0; + for (FPTR *current = m_buffer; current < m_buffer_ptr; ) + { + int count = 1; + FPTR *scan; + for (scan = current + m_entry_stride; scan < m_buffer_ptr; scan += m_entry_stride) + { + if (compare_address(current, scan) != 0) + break; + scan[0] = 0; + count++; + } + current[0] = count; + total_count += count; + current = scan; } - return 0; + // step 4: sort the results again, this time by frequency + qsort(m_buffer, (m_buffer_ptr - m_buffer) / m_entry_stride, m_entry_stride * sizeof(FPTR), compare_frequency); + + // step 5: print the results + UINT32 num_printed = 0; + for (FPTR *current = m_buffer; current < m_buffer_ptr && num_printed < 30; current += m_entry_stride) + { + // once we hit 0 frequency, we're done + if (current[0] == 0) + break; + + // output the result + printf("%4.1f%% - %6d : %p%s\n", (double)current[0] * 100.0 / (double)total_count, (UINT32)current[0], reinterpret_cast<void *>(current[1]), lookup_symbol(current[1])); + for (int index = 3; index < m_entry_stride; index++) + { + if (current[index] == 0) + break; + printf(" %p%s\n", reinterpret_cast<void *>(current[index]), lookup_symbol(current[index])); + } + printf("\n"); + num_printed++; + } } +//------------------------------------------------- +// thread_entry - thread entry stub +//------------------------------------------------- -//============================================================ -// start_profiler -//============================================================ - -static void start_profiler(void) +DWORD WINAPI sampling_profiler::thread_entry(LPVOID lpParameter) { - HANDLE currentThread; - BOOL result; - - // parse the map file, if present - parse_map_file(); - - // do the dance to get a handle to ourself - result = DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), ¤tThread, - THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME | THREAD_QUERY_INFORMATION, FALSE, 0); - assert_always(result, "Failed to get thread handle for main thread"); - - profiler_thread_exit = 0; - - // start the thread - profiler_thread = CreateThread(NULL, 0, profiler_thread_entry, (LPVOID)currentThread, 0, &profiler_thread_id); - assert_always(profiler_thread, "Failed to create profiler thread\n"); - - // max out the priority - SetThreadPriority(profiler_thread, THREAD_PRIORITY_TIME_CRITICAL); + reinterpret_cast<sampling_profiler *>(lpParameter)->thread_run(); + return 0; } +//------------------------------------------------- +// thread_run - sampling thread +//------------------------------------------------- -//============================================================ -// stop_profiler -//============================================================ - -static void stop_profiler(void) +void sampling_profiler::thread_run() { - profiler_thread_exit = 1; - WaitForSingleObject(profiler_thread, 2000); - output_symbol_list(stderr); - free_symbol_map(); -} + CONTEXT context; + memset(&context, 0, sizeof(context)); -#else + // initialize the symbol lookup + HANDLE process = GetCurrentProcess(); + sym_initialize(process, NULL, TRUE); -static void start_profiler(void) {} -static void stop_profiler(void) {} + // loop until done + while (!m_thread_exit && m_buffer_ptr < m_buffer_end) + { + // pause the main thread and get its context + SuspendThread(m_target_thread); + context.ContextFlags = CONTEXT_FULL; + GetThreadContext(m_target_thread, &context); + + // store an initial entry count plus a NULL (used later) and the EIP + FPTR *count = m_buffer_ptr; + *m_buffer_ptr++ = 2; + *m_buffer_ptr++ = 0; + *m_buffer_ptr++ = ip_from_context(context); + + // now unwind the requested number of frames + if (m_stack_depth > 0) + { + STACKFRAME64 stackframe; + init_stack_frame(stackframe, context); + + // iterate over the frames until we hit an error + int frame; + for (frame = 0; frame < m_stack_depth; frame++) + { + if (!stack_walk_64(IMAGE_FILE_MACHINE_NATIVE, process, m_target_thread, &stackframe, &context, NULL, sym_function_table_access_64, sym_get_module_base_64, NULL)) + break; + *m_buffer_ptr++ = stackframe.AddrPC.Offset; + *count += 1; + } + + // fill in any missing parts with NULLs + for (; frame < m_stack_depth; frame++) + *m_buffer_ptr++ = 0; + } + + // resume the thread + ResumeThread(m_target_thread); -#endif + // sleep for 1ms + Sleep(1); + } +} |