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_buildflags.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/gperftools-2.0/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
GetProfilerReturnAddrResolutionFunc()90 ReturnAddressLocationResolver GetProfilerReturnAddrResolutionFunc() {
91 return nullptr;
92 }
93
GetProfilerDynamicFunctionEntryHookFunc()94 DynamicFunctionEntryHook GetProfilerDynamicFunctionEntryHookFunc() {
95 return nullptr;
96 }
97
GetProfilerAddDynamicSymbolFunc()98 AddDynamicSymbol GetProfilerAddDynamicSymbolFunc() {
99 return nullptr;
100 }
101
GetProfilerMoveDynamicSymbolFunc()102 MoveDynamicSymbol GetProfilerMoveDynamicSymbolFunc() {
103 return nullptr;
104 }
105
106 #else // defined(OS_WIN)
107
108 namespace {
109
110 struct FunctionSearchContext {
111 const char* name;
112 FARPROC function;
113 };
114
115 // 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)116 bool FindResolutionFunctionInImports(
117 const base::win::PEImage &image, const char* module_name,
118 PIMAGE_THUNK_DATA unused_name_table, PIMAGE_THUNK_DATA import_address_table,
119 PVOID cookie) {
120 FunctionSearchContext* context =
121 reinterpret_cast<FunctionSearchContext*>(cookie);
122
123 DCHECK(context);
124 DCHECK(!context->function);
125
126 // Our import address table contains pointers to the functions we import
127 // at this point. Let's retrieve the first such function and use it to
128 // find the module this import was resolved to by the loader.
129 const wchar_t* function_in_module =
130 reinterpret_cast<const wchar_t*>(import_address_table->u1.Function);
131
132 // Retrieve the module by a function in the module.
133 const DWORD kFlags = GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
134 GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT;
135 HMODULE module = NULL;
136 if (!::GetModuleHandleEx(kFlags, function_in_module, &module)) {
137 // This can happen if someone IAT patches us to a thunk.
138 return true;
139 }
140
141 // See whether this module exports the function we're looking for.
142 FARPROC exported_func = ::GetProcAddress(module, context->name);
143 if (exported_func != NULL) {
144 // We found it, return the function and terminate the enumeration.
145 context->function = exported_func;
146 return false;
147 }
148
149 // Keep going.
150 return true;
151 }
152
153 template <typename FunctionType>
FindFunctionInImports(const char * function_name)154 FunctionType FindFunctionInImports(const char* function_name) {
155 base::win::PEImage image(CURRENT_MODULE());
156
157 FunctionSearchContext ctx = { function_name, NULL };
158 image.EnumImportChunks(FindResolutionFunctionInImports, &ctx);
159
160 return reinterpret_cast<FunctionType>(ctx.function);
161 }
162
163 } // namespace
164
GetProfilerReturnAddrResolutionFunc()165 ReturnAddressLocationResolver GetProfilerReturnAddrResolutionFunc() {
166 return FindFunctionInImports<ReturnAddressLocationResolver>(
167 "ResolveReturnAddressLocation");
168 }
169
GetProfilerDynamicFunctionEntryHookFunc()170 DynamicFunctionEntryHook GetProfilerDynamicFunctionEntryHookFunc() {
171 return FindFunctionInImports<DynamicFunctionEntryHook>(
172 "OnDynamicFunctionEntry");
173 }
174
GetProfilerAddDynamicSymbolFunc()175 AddDynamicSymbol GetProfilerAddDynamicSymbolFunc() {
176 return FindFunctionInImports<AddDynamicSymbol>(
177 "AddDynamicSymbol");
178 }
179
GetProfilerMoveDynamicSymbolFunc()180 MoveDynamicSymbol GetProfilerMoveDynamicSymbolFunc() {
181 return FindFunctionInImports<MoveDynamicSymbol>(
182 "MoveDynamicSymbol");
183 }
184
185 #endif // defined(OS_WIN)
186
187 } // namespace debug
188 } // namespace base
189