Home | History | Annotate | Download | only in debug
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "base/debug/profiler.h"
      6 
      7 #include <string>
      8 
      9 #include "base/process/process_handle.h"
     10 #include "base/strings/string_util.h"
     11 #include "base/strings/stringprintf.h"
     12 
     13 #if defined(OS_WIN)
     14 #include "base/win/pe_image.h"
     15 #endif  // defined(OS_WIN)
     16 
     17 #if defined(ENABLE_PROFILING) && !defined(NO_TCMALLOC)
     18 #include "third_party/tcmalloc/chromium/src/gperftools/profiler.h"
     19 #endif
     20 
     21 namespace base {
     22 namespace debug {
     23 
     24 #if defined(ENABLE_PROFILING) && !defined(NO_TCMALLOC)
     25 
     26 static int profile_count = 0;
     27 
     28 void StartProfiling(const std::string& name) {
     29   ++profile_count;
     30   std::string full_name(name);
     31   std::string pid = StringPrintf("%d", GetCurrentProcId());
     32   std::string count = StringPrintf("%d", profile_count);
     33   ReplaceSubstringsAfterOffset(&full_name, 0, "{pid}", pid);
     34   ReplaceSubstringsAfterOffset(&full_name, 0, "{count}", count);
     35   ProfilerStart(full_name.c_str());
     36 }
     37 
     38 void StopProfiling() {
     39   ProfilerFlush();
     40   ProfilerStop();
     41 }
     42 
     43 void FlushProfiling() {
     44   ProfilerFlush();
     45 }
     46 
     47 bool BeingProfiled() {
     48   return ProfilingIsEnabledForAllThreads();
     49 }
     50 
     51 void RestartProfilingAfterFork() {
     52   ProfilerRegisterThread();
     53 }
     54 
     55 #else
     56 
     57 void StartProfiling(const std::string& name) {
     58 }
     59 
     60 void StopProfiling() {
     61 }
     62 
     63 void FlushProfiling() {
     64 }
     65 
     66 bool BeingProfiled() {
     67   return false;
     68 }
     69 
     70 void RestartProfilingAfterFork() {
     71 }
     72 
     73 #endif
     74 
     75 #if !defined(OS_WIN)
     76 
     77 bool IsBinaryInstrumented() {
     78   return false;
     79 }
     80 
     81 ReturnAddressLocationResolver GetProfilerReturnAddrResolutionFunc() {
     82   return NULL;
     83 }
     84 
     85 DynamicFunctionEntryHook GetProfilerDynamicFunctionEntryHookFunc() {
     86   return NULL;
     87 }
     88 
     89 AddDynamicSymbol GetProfilerAddDynamicSymbolFunc() {
     90   return NULL;
     91 }
     92 
     93 MoveDynamicSymbol GetProfilerMoveDynamicSymbolFunc() {
     94   return NULL;
     95 }
     96 
     97 #else  // defined(OS_WIN)
     98 
     99 // http://blogs.msdn.com/oldnewthing/archive/2004/10/25/247180.aspx
    100 extern "C" IMAGE_DOS_HEADER __ImageBase;
    101 
    102 bool IsBinaryInstrumented() {
    103   enum InstrumentationCheckState {
    104     UNINITIALIZED,
    105     INSTRUMENTED_IMAGE,
    106     NON_INSTRUMENTED_IMAGE,
    107   };
    108 
    109   static InstrumentationCheckState state = UNINITIALIZED;
    110 
    111   if (state == UNINITIALIZED) {
    112     HMODULE this_module = reinterpret_cast<HMODULE>(&__ImageBase);
    113     base::win::PEImage image(this_module);
    114 
    115     // Check to be sure our image is structured as we'd expect.
    116     DCHECK(image.VerifyMagic());
    117 
    118     // Syzygy-instrumented binaries contain a PE image section named ".thunks",
    119     // and all Syzygy-modified binaries contain the ".syzygy" image section.
    120     // This is a very fast check, as it only looks at the image header.
    121     if ((image.GetImageSectionHeaderByName(".thunks") != NULL) &&
    122         (image.GetImageSectionHeaderByName(".syzygy") != NULL)) {
    123       state = INSTRUMENTED_IMAGE;
    124     } else {
    125       state = NON_INSTRUMENTED_IMAGE;
    126     }
    127   }
    128   DCHECK(state != UNINITIALIZED);
    129 
    130   return state == INSTRUMENTED_IMAGE;
    131 }
    132 
    133 namespace {
    134 
    135 struct FunctionSearchContext {
    136   const char* name;
    137   FARPROC function;
    138 };
    139 
    140 // Callback function to PEImage::EnumImportChunks.
    141 bool FindResolutionFunctionInImports(
    142     const base::win::PEImage &image, const char* module_name,
    143     PIMAGE_THUNK_DATA unused_name_table, PIMAGE_THUNK_DATA import_address_table,
    144     PVOID cookie) {
    145   FunctionSearchContext* context =
    146       reinterpret_cast<FunctionSearchContext*>(cookie);
    147 
    148   DCHECK_NE(static_cast<FunctionSearchContext*>(NULL), context);
    149   DCHECK_EQ(static_cast<FARPROC>(NULL), context->function);
    150 
    151   // Our import address table contains pointers to the functions we import
    152   // at this point. Let's retrieve the first such function and use it to
    153   // find the module this import was resolved to by the loader.
    154   const wchar_t* function_in_module =
    155       reinterpret_cast<const wchar_t*>(import_address_table->u1.Function);
    156 
    157   // Retrieve the module by a function in the module.
    158   const DWORD kFlags = GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
    159                        GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT;
    160   HMODULE module = NULL;
    161   if (!::GetModuleHandleEx(kFlags, function_in_module, &module)) {
    162     // This can happen if someone IAT patches us to a thunk.
    163     return true;
    164   }
    165 
    166   // See whether this module exports the function we're looking for.
    167   FARPROC exported_func = ::GetProcAddress(module, context->name);
    168   if (exported_func != NULL) {
    169     // We found it, return the function and terminate the enumeration.
    170     context->function = exported_func;
    171     return false;
    172   }
    173 
    174   // Keep going.
    175   return true;
    176 }
    177 
    178 template <typename FunctionType>
    179 FunctionType FindFunctionInImports(const char* function_name) {
    180   if (!IsBinaryInstrumented())
    181     return NULL;
    182 
    183   HMODULE this_module = reinterpret_cast<HMODULE>(&__ImageBase);
    184   base::win::PEImage image(this_module);
    185 
    186   FunctionSearchContext ctx = { function_name, NULL };
    187   image.EnumImportChunks(FindResolutionFunctionInImports, &ctx);
    188 
    189   return reinterpret_cast<FunctionType>(ctx.function);
    190 }
    191 
    192 }  // namespace
    193 
    194 ReturnAddressLocationResolver GetProfilerReturnAddrResolutionFunc() {
    195   return FindFunctionInImports<ReturnAddressLocationResolver>(
    196       "ResolveReturnAddressLocation");
    197 }
    198 
    199 DynamicFunctionEntryHook GetProfilerDynamicFunctionEntryHookFunc() {
    200   return FindFunctionInImports<DynamicFunctionEntryHook>(
    201       "OnDynamicFunctionEntry");
    202 }
    203 
    204 AddDynamicSymbol GetProfilerAddDynamicSymbolFunc() {
    205   return FindFunctionInImports<AddDynamicSymbol>(
    206       "AddDynamicSymbol");
    207 }
    208 
    209 MoveDynamicSymbol GetProfilerMoveDynamicSymbolFunc() {
    210   return FindFunctionInImports<MoveDynamicSymbol>(
    211       "MoveDynamicSymbol");
    212 }
    213 
    214 #endif  // defined(OS_WIN)
    215 
    216 }  // namespace debug
    217 }  // namespace base
    218