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