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
22namespace base {
23namespace debug {
24
25// TODO(peria): Enable profiling on Windows.
26#if defined(ENABLE_PROFILING) && !defined(NO_TCMALLOC) && !defined(OS_WIN)
27
28static int profile_count = 0;
29
30void 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
40void StopProfiling() {
41  ProfilerFlush();
42  ProfilerStop();
43}
44
45void FlushProfiling() {
46  ProfilerFlush();
47}
48
49bool BeingProfiled() {
50  return ProfilingIsEnabledForAllThreads();
51}
52
53void RestartProfilingAfterFork() {
54  ProfilerRegisterThread();
55}
56
57#else
58
59void StartProfiling(const std::string& name) {
60}
61
62void StopProfiling() {
63}
64
65void FlushProfiling() {
66}
67
68bool BeingProfiled() {
69  return false;
70}
71
72void RestartProfilingAfterFork() {
73}
74
75#endif
76
77#if !defined(OS_WIN)
78
79bool IsBinaryInstrumented() {
80  return false;
81}
82
83ReturnAddressLocationResolver GetProfilerReturnAddrResolutionFunc() {
84  return NULL;
85}
86
87DynamicFunctionEntryHook GetProfilerDynamicFunctionEntryHookFunc() {
88  return NULL;
89}
90
91AddDynamicSymbol GetProfilerAddDynamicSymbolFunc() {
92  return NULL;
93}
94
95MoveDynamicSymbol GetProfilerMoveDynamicSymbolFunc() {
96  return NULL;
97}
98
99#else  // defined(OS_WIN)
100
101// http://blogs.msdn.com/oldnewthing/archive/2004/10/25/247180.aspx
102extern "C" IMAGE_DOS_HEADER __ImageBase;
103
104bool 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
135namespace {
136
137struct FunctionSearchContext {
138  const char* name;
139  FARPROC function;
140};
141
142// Callback function to PEImage::EnumImportChunks.
143bool 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
180template <typename FunctionType>
181FunctionType 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
196ReturnAddressLocationResolver GetProfilerReturnAddrResolutionFunc() {
197  return FindFunctionInImports<ReturnAddressLocationResolver>(
198      "ResolveReturnAddressLocation");
199}
200
201DynamicFunctionEntryHook GetProfilerDynamicFunctionEntryHookFunc() {
202  return FindFunctionInImports<DynamicFunctionEntryHook>(
203      "OnDynamicFunctionEntry");
204}
205
206AddDynamicSymbol GetProfilerAddDynamicSymbolFunc() {
207  return FindFunctionInImports<AddDynamicSymbol>(
208      "AddDynamicSymbol");
209}
210
211MoveDynamicSymbol GetProfilerMoveDynamicSymbolFunc() {
212  return FindFunctionInImports<MoveDynamicSymbol>(
213      "MoveDynamicSymbol");
214}
215
216#endif  // defined(OS_WIN)
217
218}  // namespace debug
219}  // namespace base
220