// Copyright (c) 2017 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include #include #include #include // Needs to be included before the others. #include #include namespace { // The main purpose of the order file is to optimize startup time, // so capturing the first N function calls is enough. static constexpr int kSamplesCapacity = 25 * 1024 * 1024; void* samples[kSamplesCapacity]; std::atomic_int num_samples; std::atomic_int done; // Path to the dump file. %lu will be substituted by the process id. static const char kDumpFile[] = "/src/tmp/cygprofile_%lu.txt"; // Symbolize the samples and write them to disk. void dump(void*) { HMODULE dbghelp = LoadLibraryA("dbghelp.dll"); auto sym_from_addr = reinterpret_cast( ::GetProcAddress(dbghelp, "SymFromAddr")); auto sym_initialize = reinterpret_cast( ::GetProcAddress(dbghelp, "SymInitialize")); auto sym_set_options = reinterpret_cast( ::GetProcAddress(dbghelp, "SymSetOptions")); char filename[MAX_PATH]; snprintf(filename, sizeof(filename), kDumpFile, ::GetCurrentProcessId()); FILE* f = fopen(filename, "w"); if (!f) return; sym_initialize(::GetCurrentProcess(), NULL, TRUE); sym_set_options(SYMOPT_DEFERRED_LOADS | SYMOPT_PUBLICS_ONLY); char sym_buf[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)]; std::unordered_set seen; std::unordered_set seen_names; for (void* sample : samples) { // Only print the first call of a function. if (seen.count(sample)) continue; seen.insert(sample); SYMBOL_INFO* symbol = reinterpret_cast(sym_buf); symbol->SizeOfStruct = sizeof(SYMBOL_INFO); symbol->MaxNameLen = MAX_SYM_NAME; DWORD64 offset = 0; if (sym_from_addr(::GetCurrentProcess(), reinterpret_cast(sample), &offset, symbol)) { const char* name = symbol->Name; if (name[0] == '_') name++; if (seen_names.count(name)) continue; seen_names.insert(name); fprintf(f, "%s\n", name); } } fclose(f); } } // namespace extern "C" { void __cyg_profile_func_enter(void* this_fn, void* call_site_unused) { if (done) return; // Get our index for the samples array atomically. int n = num_samples++; if (n < kSamplesCapacity) { samples[n] = this_fn; if (n + 1 == kSamplesCapacity) { // This is the final sample; start dumping the samples to a file (on a // separate thread so as not to disturb the main program). done = 1; _beginthread(dump, 0, nullptr); } } } void __cyg_profile_func_exit(void* this_fn, void* call_site) {} } // extern "C"