| // 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_util.h" | 
 | #include "base/string_util.h" | 
 | #include "base/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; | 
 | } | 
 |  | 
 | #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; | 
 | } | 
 |  | 
 | // Callback function to PEImage::EnumImportChunks. | 
 | static 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) { | 
 |   // 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. | 
 |   ReturnAddressLocationResolver exported_func = | 
 |       reinterpret_cast<ReturnAddressLocationResolver>( | 
 |           ::GetProcAddress(module, "ResolveReturnAddressLocation")); | 
 |  | 
 |   if (exported_func != NULL) { | 
 |     ReturnAddressLocationResolver* resolver_func = | 
 |         reinterpret_cast<ReturnAddressLocationResolver*>(cookie); | 
 |     DCHECK(resolver_func != NULL); | 
 |     DCHECK(*resolver_func == NULL); | 
 |  | 
 |     // We found it, return the function and terminate the enumeration. | 
 |     *resolver_func = exported_func; | 
 |     return false; | 
 |   } | 
 |  | 
 |   // Keep going. | 
 |   return true; | 
 | } | 
 |  | 
 | ReturnAddressLocationResolver GetProfilerReturnAddrResolutionFunc() { | 
 |   if (!IsBinaryInstrumented()) | 
 |     return NULL; | 
 |  | 
 |   HMODULE this_module = reinterpret_cast<HMODULE>(&__ImageBase); | 
 |   base::win::PEImage image(this_module); | 
 |  | 
 |   ReturnAddressLocationResolver resolver_func = NULL; | 
 |   image.EnumImportChunks(FindResolutionFunctionInImports, &resolver_func); | 
 |  | 
 |   return resolver_func; | 
 | } | 
 |  | 
 | #endif  // defined(OS_WIN) | 
 |  | 
 | }  // namespace debug | 
 | }  // namespace base |