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 22namespace base { 23namespace debug { 24 25// TODO(peria): Enable profiling on Windows. 26#if defined(ENABLE_PROFILING) && !defined(NO_TCMALLOC) && !defined(OS_WIN) 27 28static int profile_count = 0; 29 30void 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 40void StopProfiling() { 41 ProfilerFlush(); 42 ProfilerStop(); 43} 44 45void FlushProfiling() { 46 ProfilerFlush(); 47} 48 49bool BeingProfiled() { 50 return ProfilingIsEnabledForAllThreads(); 51} 52 53void RestartProfilingAfterFork() { 54 ProfilerRegisterThread(); 55} 56 57#else 58 59void StartProfiling(const std::string& name) { 60} 61 62void StopProfiling() { 63} 64 65void FlushProfiling() { 66} 67 68bool BeingProfiled() { 69 return false; 70} 71 72void RestartProfilingAfterFork() { 73} 74 75#endif 76 77#if !defined(OS_WIN) 78 79bool IsBinaryInstrumented() { 80 return false; 81} 82 83ReturnAddressLocationResolver GetProfilerReturnAddrResolutionFunc() { 84 return NULL; 85} 86 87DynamicFunctionEntryHook GetProfilerDynamicFunctionEntryHookFunc() { 88 return NULL; 89} 90 91AddDynamicSymbol GetProfilerAddDynamicSymbolFunc() { 92 return NULL; 93} 94 95MoveDynamicSymbol GetProfilerMoveDynamicSymbolFunc() { 96 return NULL; 97} 98 99#else // defined(OS_WIN) 100 101// http://blogs.msdn.com/oldnewthing/archive/2004/10/25/247180.aspx 102extern "C" IMAGE_DOS_HEADER __ImageBase; 103 104bool 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 135namespace { 136 137struct FunctionSearchContext { 138 const char* name; 139 FARPROC function; 140}; 141 142// Callback function to PEImage::EnumImportChunks. 143bool 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 180template <typename FunctionType> 181FunctionType 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 196ReturnAddressLocationResolver GetProfilerReturnAddrResolutionFunc() { 197 return FindFunctionInImports<ReturnAddressLocationResolver>( 198 "ResolveReturnAddressLocation"); 199} 200 201DynamicFunctionEntryHook GetProfilerDynamicFunctionEntryHookFunc() { 202 return FindFunctionInImports<DynamicFunctionEntryHook>( 203 "OnDynamicFunctionEntry"); 204} 205 206AddDynamicSymbol GetProfilerAddDynamicSymbolFunc() { 207 return FindFunctionInImports<AddDynamicSymbol>( 208 "AddDynamicSymbol"); 209} 210 211MoveDynamicSymbol GetProfilerMoveDynamicSymbolFunc() { 212 return FindFunctionInImports<MoveDynamicSymbol>( 213 "MoveDynamicSymbol"); 214} 215 216#endif // defined(OS_WIN) 217 218} // namespace debug 219} // namespace base 220