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