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