| // 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/allocator/buildflags.h" | |
| #include "base/debug/debugging_buildflags.h" | |
| #include "base/process/process_handle.h" | |
| #include "base/strings/string_number_conversions.h" | |
| #include "base/strings/string_util.h" | |
| #include "build/build_config.h" | |
| #if defined(OS_WIN) | |
| #include "base/win/current_module.h" | |
| #include "base/win/pe_image.h" | |
| #endif // defined(OS_WIN) | |
| // TODO(peria): Enable profiling on Windows. | |
| #if BUILDFLAG(ENABLE_PROFILING) && !defined(NO_TCMALLOC) && !defined(OS_WIN) | |
| #if BUILDFLAG(USE_NEW_TCMALLOC) | |
| #include "third_party/tcmalloc/chromium/src/gperftools/profiler.h" | |
| #else | |
| #include "third_party/tcmalloc/gperftools-2.0/chromium/src/gperftools/profiler.h" | |
| #endif | |
| #endif | |
| namespace base { | |
| namespace debug { | |
| // TODO(peria): Enable profiling on Windows. | |
| #if BUILDFLAG(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 = IntToString(GetCurrentProcId()); | |
| std::string count = IntToString(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(); | |
| } | |
| bool IsProfilingSupported() { | |
| return true; | |
| } | |
| #else | |
| void StartProfiling(const std::string& name) { | |
| } | |
| void StopProfiling() { | |
| } | |
| void FlushProfiling() { | |
| } | |
| bool BeingProfiled() { | |
| return false; | |
| } | |
| void RestartProfilingAfterFork() { | |
| } | |
| bool IsProfilingSupported() { | |
| return false; | |
| } | |
| #endif | |
| #if !defined(OS_WIN) | |
| ReturnAddressLocationResolver GetProfilerReturnAddrResolutionFunc() { | |
| return nullptr; | |
| } | |
| DynamicFunctionEntryHook GetProfilerDynamicFunctionEntryHookFunc() { | |
| return nullptr; | |
| } | |
| AddDynamicSymbol GetProfilerAddDynamicSymbolFunc() { | |
| return nullptr; | |
| } | |
| MoveDynamicSymbol GetProfilerMoveDynamicSymbolFunc() { | |
| return nullptr; | |
| } | |
| #else // defined(OS_WIN) | |
| 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(context); | |
| DCHECK(!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) { | |
| base::win::PEImage image(CURRENT_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 |