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 
StartProfiling(const std::string & name)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 
StopProfiling()43 void StopProfiling() {
44   ProfilerFlush();
45   ProfilerStop();
46 }
47 
FlushProfiling()48 void FlushProfiling() {
49   ProfilerFlush();
50 }
51 
BeingProfiled()52 bool BeingProfiled() {
53   return ProfilingIsEnabledForAllThreads();
54 }
55 
RestartProfilingAfterFork()56 void RestartProfilingAfterFork() {
57   ProfilerRegisterThread();
58 }
59 
IsProfilingSupported()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 
IsBinaryInstrumented()90 bool IsBinaryInstrumented() {
91   return false;
92 }
93 
GetProfilerReturnAddrResolutionFunc()94 ReturnAddressLocationResolver GetProfilerReturnAddrResolutionFunc() {
95   return NULL;
96 }
97 
GetProfilerDynamicFunctionEntryHookFunc()98 DynamicFunctionEntryHook GetProfilerDynamicFunctionEntryHookFunc() {
99   return NULL;
100 }
101 
GetProfilerAddDynamicSymbolFunc()102 AddDynamicSymbol GetProfilerAddDynamicSymbolFunc() {
103   return NULL;
104 }
105 
GetProfilerMoveDynamicSymbolFunc()106 MoveDynamicSymbol GetProfilerMoveDynamicSymbolFunc() {
107   return NULL;
108 }
109 
110 #else  // defined(OS_WIN)
111 
IsBinaryInstrumented()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.
FindResolutionFunctionInImports(const base::win::PEImage & image,const char * module_name,PIMAGE_THUNK_DATA unused_name_table,PIMAGE_THUNK_DATA import_address_table,PVOID cookie)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>
FindFunctionInImports(const char * function_name)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 
GetProfilerReturnAddrResolutionFunc()202 ReturnAddressLocationResolver GetProfilerReturnAddrResolutionFunc() {
203   return FindFunctionInImports<ReturnAddressLocationResolver>(
204       "ResolveReturnAddressLocation");
205 }
206 
GetProfilerDynamicFunctionEntryHookFunc()207 DynamicFunctionEntryHook GetProfilerDynamicFunctionEntryHookFunc() {
208   return FindFunctionInImports<DynamicFunctionEntryHook>(
209       "OnDynamicFunctionEntry");
210 }
211 
GetProfilerAddDynamicSymbolFunc()212 AddDynamicSymbol GetProfilerAddDynamicSymbolFunc() {
213   return FindFunctionInImports<AddDynamicSymbol>(
214       "AddDynamicSymbol");
215 }
216 
GetProfilerMoveDynamicSymbolFunc()217 MoveDynamicSymbol GetProfilerMoveDynamicSymbolFunc() {
218   return FindFunctionInImports<MoveDynamicSymbol>(
219       "MoveDynamicSymbol");
220 }
221 
222 #endif  // defined(OS_WIN)
223 
224 }  // namespace debug
225 }  // namespace base
226