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