• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
StartProfiling(const std::string & name)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 
StopProfiling()40 void StopProfiling() {
41   ProfilerFlush();
42   ProfilerStop();
43 }
44 
FlushProfiling()45 void FlushProfiling() {
46   ProfilerFlush();
47 }
48 
BeingProfiled()49 bool BeingProfiled() {
50   return ProfilingIsEnabledForAllThreads();
51 }
52 
RestartProfilingAfterFork()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 
IsBinaryInstrumented()79 bool IsBinaryInstrumented() {
80   return false;
81 }
82 
GetProfilerReturnAddrResolutionFunc()83 ReturnAddressLocationResolver GetProfilerReturnAddrResolutionFunc() {
84   return NULL;
85 }
86 
GetProfilerDynamicFunctionEntryHookFunc()87 DynamicFunctionEntryHook GetProfilerDynamicFunctionEntryHookFunc() {
88   return NULL;
89 }
90 
GetProfilerAddDynamicSymbolFunc()91 AddDynamicSymbol GetProfilerAddDynamicSymbolFunc() {
92   return NULL;
93 }
94 
GetProfilerMoveDynamicSymbolFunc()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 
IsBinaryInstrumented()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.
FindResolutionFunctionInImports(const base::win::PEImage & image,const char * module_name,PIMAGE_THUNK_DATA unused_name_table,PIMAGE_THUNK_DATA import_address_table,PVOID cookie)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>
FindFunctionInImports(const char * function_name)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 
GetProfilerReturnAddrResolutionFunc()196 ReturnAddressLocationResolver GetProfilerReturnAddrResolutionFunc() {
197   return FindFunctionInImports<ReturnAddressLocationResolver>(
198       "ResolveReturnAddressLocation");
199 }
200 
GetProfilerDynamicFunctionEntryHookFunc()201 DynamicFunctionEntryHook GetProfilerDynamicFunctionEntryHookFunc() {
202   return FindFunctionInImports<DynamicFunctionEntryHook>(
203       "OnDynamicFunctionEntry");
204 }
205 
GetProfilerAddDynamicSymbolFunc()206 AddDynamicSymbol GetProfilerAddDynamicSymbolFunc() {
207   return FindFunctionInImports<AddDynamicSymbol>(
208       "AddDynamicSymbol");
209 }
210 
GetProfilerMoveDynamicSymbolFunc()211 MoveDynamicSymbol GetProfilerMoveDynamicSymbolFunc() {
212   return FindFunctionInImports<MoveDynamicSymbol>(
213       "MoveDynamicSymbol");
214 }
215 
216 #endif  // defined(OS_WIN)
217 
218 }  // namespace debug
219 }  // namespace base
220