218 lines
5.6 KiB
C++
218 lines
5.6 KiB
C++
|
// 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)
|
||
|
|
||
|
#if defined(ENABLE_PROFILING) && !defined(NO_TCMALLOC)
|
||
|
#include "third_party/tcmalloc/chromium/src/gperftools/profiler.h"
|
||
|
#endif
|
||
|
|
||
|
namespace base {
|
||
|
namespace debug {
|
||
|
|
||
|
#if defined(ENABLE_PROFILING) && !defined(NO_TCMALLOC)
|
||
|
|
||
|
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
|