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