michael@0: // Copyright (c) 2012 The Chromium Authors. All rights reserved. michael@0: // Use of this source code is governed by a BSD-style license that can be michael@0: // found in the LICENSE file. michael@0: michael@0: #include "base/debug/profiler.h" michael@0: michael@0: #include michael@0: michael@0: #include "base/process/process_handle.h" michael@0: #include "base/strings/string_util.h" michael@0: #include "base/strings/stringprintf.h" michael@0: michael@0: #if defined(OS_WIN) michael@0: #include "base/win/pe_image.h" michael@0: #endif // defined(OS_WIN) michael@0: michael@0: #if defined(ENABLE_PROFILING) && !defined(NO_TCMALLOC) michael@0: #include "third_party/tcmalloc/chromium/src/gperftools/profiler.h" michael@0: #endif michael@0: michael@0: namespace base { michael@0: namespace debug { michael@0: michael@0: #if defined(ENABLE_PROFILING) && !defined(NO_TCMALLOC) michael@0: michael@0: static int profile_count = 0; michael@0: michael@0: void StartProfiling(const std::string& name) { michael@0: ++profile_count; michael@0: std::string full_name(name); michael@0: std::string pid = StringPrintf("%d", GetCurrentProcId()); michael@0: std::string count = StringPrintf("%d", profile_count); michael@0: ReplaceSubstringsAfterOffset(&full_name, 0, "{pid}", pid); michael@0: ReplaceSubstringsAfterOffset(&full_name, 0, "{count}", count); michael@0: ProfilerStart(full_name.c_str()); michael@0: } michael@0: michael@0: void StopProfiling() { michael@0: ProfilerFlush(); michael@0: ProfilerStop(); michael@0: } michael@0: michael@0: void FlushProfiling() { michael@0: ProfilerFlush(); michael@0: } michael@0: michael@0: bool BeingProfiled() { michael@0: return ProfilingIsEnabledForAllThreads(); michael@0: } michael@0: michael@0: void RestartProfilingAfterFork() { michael@0: ProfilerRegisterThread(); michael@0: } michael@0: michael@0: #else michael@0: michael@0: void StartProfiling(const std::string& name) { michael@0: } michael@0: michael@0: void StopProfiling() { michael@0: } michael@0: michael@0: void FlushProfiling() { michael@0: } michael@0: michael@0: bool BeingProfiled() { michael@0: return false; michael@0: } michael@0: michael@0: void RestartProfilingAfterFork() { michael@0: } michael@0: michael@0: #endif michael@0: michael@0: #if !defined(OS_WIN) michael@0: michael@0: bool IsBinaryInstrumented() { michael@0: return false; michael@0: } michael@0: michael@0: ReturnAddressLocationResolver GetProfilerReturnAddrResolutionFunc() { michael@0: return NULL; michael@0: } michael@0: michael@0: DynamicFunctionEntryHook GetProfilerDynamicFunctionEntryHookFunc() { michael@0: return NULL; michael@0: } michael@0: michael@0: AddDynamicSymbol GetProfilerAddDynamicSymbolFunc() { michael@0: return NULL; michael@0: } michael@0: michael@0: MoveDynamicSymbol GetProfilerMoveDynamicSymbolFunc() { michael@0: return NULL; michael@0: } michael@0: michael@0: #else // defined(OS_WIN) michael@0: michael@0: // http://blogs.msdn.com/oldnewthing/archive/2004/10/25/247180.aspx michael@0: extern "C" IMAGE_DOS_HEADER __ImageBase; michael@0: michael@0: bool IsBinaryInstrumented() { michael@0: enum InstrumentationCheckState { michael@0: UNINITIALIZED, michael@0: INSTRUMENTED_IMAGE, michael@0: NON_INSTRUMENTED_IMAGE, michael@0: }; michael@0: michael@0: static InstrumentationCheckState state = UNINITIALIZED; michael@0: michael@0: if (state == UNINITIALIZED) { michael@0: HMODULE this_module = reinterpret_cast(&__ImageBase); michael@0: base::win::PEImage image(this_module); michael@0: michael@0: // Check to be sure our image is structured as we'd expect. michael@0: DCHECK(image.VerifyMagic()); michael@0: michael@0: // Syzygy-instrumented binaries contain a PE image section named ".thunks", michael@0: // and all Syzygy-modified binaries contain the ".syzygy" image section. michael@0: // This is a very fast check, as it only looks at the image header. michael@0: if ((image.GetImageSectionHeaderByName(".thunks") != NULL) && michael@0: (image.GetImageSectionHeaderByName(".syzygy") != NULL)) { michael@0: state = INSTRUMENTED_IMAGE; michael@0: } else { michael@0: state = NON_INSTRUMENTED_IMAGE; michael@0: } michael@0: } michael@0: DCHECK(state != UNINITIALIZED); michael@0: michael@0: return state == INSTRUMENTED_IMAGE; michael@0: } michael@0: michael@0: namespace { michael@0: michael@0: struct FunctionSearchContext { michael@0: const char* name; michael@0: FARPROC function; michael@0: }; michael@0: michael@0: // Callback function to PEImage::EnumImportChunks. michael@0: bool FindResolutionFunctionInImports( michael@0: const base::win::PEImage &image, const char* module_name, michael@0: PIMAGE_THUNK_DATA unused_name_table, PIMAGE_THUNK_DATA import_address_table, michael@0: PVOID cookie) { michael@0: FunctionSearchContext* context = michael@0: reinterpret_cast(cookie); michael@0: michael@0: DCHECK_NE(static_cast(NULL), context); michael@0: DCHECK_EQ(static_cast(NULL), context->function); michael@0: michael@0: // Our import address table contains pointers to the functions we import michael@0: // at this point. Let's retrieve the first such function and use it to michael@0: // find the module this import was resolved to by the loader. michael@0: const wchar_t* function_in_module = michael@0: reinterpret_cast(import_address_table->u1.Function); michael@0: michael@0: // Retrieve the module by a function in the module. michael@0: const DWORD kFlags = GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | michael@0: GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT; michael@0: HMODULE module = NULL; michael@0: if (!::GetModuleHandleEx(kFlags, function_in_module, &module)) { michael@0: // This can happen if someone IAT patches us to a thunk. michael@0: return true; michael@0: } michael@0: michael@0: // See whether this module exports the function we're looking for. michael@0: FARPROC exported_func = ::GetProcAddress(module, context->name); michael@0: if (exported_func != NULL) { michael@0: // We found it, return the function and terminate the enumeration. michael@0: context->function = exported_func; michael@0: return false; michael@0: } michael@0: michael@0: // Keep going. michael@0: return true; michael@0: } michael@0: michael@0: template michael@0: FunctionType FindFunctionInImports(const char* function_name) { michael@0: if (!IsBinaryInstrumented()) michael@0: return NULL; michael@0: michael@0: HMODULE this_module = reinterpret_cast(&__ImageBase); michael@0: base::win::PEImage image(this_module); michael@0: michael@0: FunctionSearchContext ctx = { function_name, NULL }; michael@0: image.EnumImportChunks(FindResolutionFunctionInImports, &ctx); michael@0: michael@0: return reinterpret_cast(ctx.function); michael@0: } michael@0: michael@0: } // namespace michael@0: michael@0: ReturnAddressLocationResolver GetProfilerReturnAddrResolutionFunc() { michael@0: return FindFunctionInImports( michael@0: "ResolveReturnAddressLocation"); michael@0: } michael@0: michael@0: DynamicFunctionEntryHook GetProfilerDynamicFunctionEntryHookFunc() { michael@0: return FindFunctionInImports( michael@0: "OnDynamicFunctionEntry"); michael@0: } michael@0: michael@0: AddDynamicSymbol GetProfilerAddDynamicSymbolFunc() { michael@0: return FindFunctionInImports( michael@0: "AddDynamicSymbol"); michael@0: } michael@0: michael@0: MoveDynamicSymbol GetProfilerMoveDynamicSymbolFunc() { michael@0: return FindFunctionInImports( michael@0: "MoveDynamicSymbol"); michael@0: } michael@0: michael@0: #endif // defined(OS_WIN) michael@0: michael@0: } // namespace debug michael@0: } // namespace base