// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/debug/profiler.h"
#include <string>
#include "base/process/process_handle.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#if defined(OS_WIN)
#include "base/win/pe_image.h"
#endif // defined(OS_WIN)
// TODO(peria): Enable profiling on Windows.
#if defined(ENABLE_PROFILING) && !defined(NO_TCMALLOC) && !defined(OS_WIN)
#include "third_party/tcmalloc/chromium/src/gperftools/profiler.h"
#endif
namespace base {
namespace debug {
// TODO(peria): Enable profiling on Windows.
#if defined(ENABLE_PROFILING) && !defined(NO_TCMALLOC) && !defined(OS_WIN)
static int profile_count = 0;
void StartProfiling(const std::string& name) {
++profile_count;
std::string full_name(name);
std::string pid = StringPrintf("%d", GetCurrentProcId());
std::string count = StringPrintf("%d", profile_count);
ReplaceSubstringsAfterOffset(&full_name, 0, "{pid}", pid);
ReplaceSubstringsAfterOffset(&full_name, 0, "{count}", count);
ProfilerStart(full_name.c_str());
}
void StopProfiling() {
ProfilerFlush();
ProfilerStop();
}
void FlushProfiling() {
ProfilerFlush();
}
bool BeingProfiled() {
return ProfilingIsEnabledForAllThreads();
}
void RestartProfilingAfterFork() {
ProfilerRegisterThread();
}
#else
void StartProfiling(const std::string& name) {
}
void StopProfiling() {
}
void FlushProfiling() {
}
bool BeingProfiled() {
return false;
}
void RestartProfilingAfterFork() {
}
#endif
#if !defined(OS_WIN)
bool IsBinaryInstrumented() {
return false;
}
ReturnAddressLocationResolver GetProfilerReturnAddrResolutionFunc() {
return NULL;
}
DynamicFunctionEntryHook GetProfilerDynamicFunctionEntryHookFunc() {
return NULL;
}
AddDynamicSymbol GetProfilerAddDynamicSymbolFunc() {
return NULL;
}
MoveDynamicSymbol GetProfilerMoveDynamicSymbolFunc() {
return NULL;
}
#else // defined(OS_WIN)
// http://blogs.msdn.com/oldnewthing/archive/2004/10/25/247180.aspx
extern "C" IMAGE_DOS_HEADER __ImageBase;
bool IsBinaryInstrumented() {
enum InstrumentationCheckState {
UNINITIALIZED,
INSTRUMENTED_IMAGE,
NON_INSTRUMENTED_IMAGE,
};
static InstrumentationCheckState state = UNINITIALIZED;
if (state == UNINITIALIZED) {
HMODULE this_module = reinterpret_cast<HMODULE>(&__ImageBase);
base::win::PEImage image(this_module);
// Check to be sure our image is structured as we'd expect.
DCHECK(image.VerifyMagic());
// Syzygy-instrumented binaries contain a PE image section named ".thunks",
// and all Syzygy-modified binaries contain the ".syzygy" image section.
// This is a very fast check, as it only looks at the image header.
if ((image.GetImageSectionHeaderByName(".thunks") != NULL) &&
(image.GetImageSectionHeaderByName(".syzygy") != NULL)) {
state = INSTRUMENTED_IMAGE;
} else {
state = NON_INSTRUMENTED_IMAGE;
}
}
DCHECK(state != UNINITIALIZED);
return state == INSTRUMENTED_IMAGE;
}
namespace {
struct FunctionSearchContext {
const char* name;
FARPROC function;
};
// Callback function to PEImage::EnumImportChunks.
bool FindResolutionFunctionInImports(
const base::win::PEImage &image, const char* module_name,
PIMAGE_THUNK_DATA unused_name_table, PIMAGE_THUNK_DATA import_address_table,
PVOID cookie) {
FunctionSearchContext* context =
reinterpret_cast<FunctionSearchContext*>(cookie);
DCHECK_NE(static_cast<FunctionSearchContext*>(NULL), context);
DCHECK_EQ(static_cast<FARPROC>(NULL), context->function);
// Our import address table contains pointers to the functions we import
// at this point. Let's retrieve the first such function and use it to
// find the module this import was resolved to by the loader.
const wchar_t* function_in_module =
reinterpret_cast<const wchar_t*>(import_address_table->u1.Function);
// Retrieve the module by a function in the module.
const DWORD kFlags = GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT;
HMODULE module = NULL;
if (!::GetModuleHandleEx(kFlags, function_in_module, &module)) {
// This can happen if someone IAT patches us to a thunk.
return true;
}
// See whether this module exports the function we're looking for.
FARPROC exported_func = ::GetProcAddress(module, context->name);
if (exported_func != NULL) {
// We found it, return the function and terminate the enumeration.
context->function = exported_func;
return false;
}
// Keep going.
return true;
}
template <typename FunctionType>
FunctionType FindFunctionInImports(const char* function_name) {
if (!IsBinaryInstrumented())
return NULL;
HMODULE this_module = reinterpret_cast<HMODULE>(&__ImageBase);
base::win::PEImage image(this_module);
FunctionSearchContext ctx = { function_name, NULL };
image.EnumImportChunks(FindResolutionFunctionInImports, &ctx);
return reinterpret_cast<FunctionType>(ctx.function);
}
} // namespace
ReturnAddressLocationResolver GetProfilerReturnAddrResolutionFunc() {
return FindFunctionInImports<ReturnAddressLocationResolver>(
"ResolveReturnAddressLocation");
}
DynamicFunctionEntryHook GetProfilerDynamicFunctionEntryHookFunc() {
return FindFunctionInImports<DynamicFunctionEntryHook>(
"OnDynamicFunctionEntry");
}
AddDynamicSymbol GetProfilerAddDynamicSymbolFunc() {
return FindFunctionInImports<AddDynamicSymbol>(
"AddDynamicSymbol");
}
MoveDynamicSymbol GetProfilerMoveDynamicSymbolFunc() {
return FindFunctionInImports<MoveDynamicSymbol>(
"MoveDynamicSymbol");
}
#endif // defined(OS_WIN)
} // namespace debug
} // namespace base