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