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