15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/debug/profiler.h" 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <string> 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 958e6fbe4ee35d65e14b626c557d37565bf8ad179Ben Murdoch#include "base/process/process_handle.h" 105e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)#include "base/strings/string_util.h" 115e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)#include "base/strings/stringprintf.h" 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_WIN) 145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/win/pe_image.h" 155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif // defined(OS_WIN) 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 17a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// TODO(peria): Enable profiling on Windows. 18a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#if defined(ENABLE_PROFILING) && !defined(NO_TCMALLOC) && !defined(OS_WIN) 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "third_party/tcmalloc/chromium/src/gperftools/profiler.h" 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace base { 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace debug { 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 25a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// TODO(peria): Enable profiling on Windows. 26a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#if defined(ENABLE_PROFILING) && !defined(NO_TCMALLOC) && !defined(OS_WIN) 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static int profile_count = 0; 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StartProfiling(const std::string& name) { 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ++profile_count; 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::string full_name(name); 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::string pid = StringPrintf("%d", GetCurrentProcId()); 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::string count = StringPrintf("%d", profile_count); 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ReplaceSubstringsAfterOffset(&full_name, 0, "{pid}", pid); 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ReplaceSubstringsAfterOffset(&full_name, 0, "{count}", count); 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ProfilerStart(full_name.c_str()); 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StopProfiling() { 415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ProfilerFlush(); 425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ProfilerStop(); 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void FlushProfiling() { 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ProfilerFlush(); 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool BeingProfiled() { 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return ProfilingIsEnabledForAllThreads(); 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void RestartProfilingAfterFork() { 545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ProfilerRegisterThread(); 555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#else 585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StartProfiling(const std::string& name) { 605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StopProfiling() { 635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void FlushProfiling() { 665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool BeingProfiled() { 695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void RestartProfilingAfterFork() { 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif 765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if !defined(OS_WIN) 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool IsBinaryInstrumented() { 805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ReturnAddressLocationResolver GetProfilerReturnAddrResolutionFunc() { 845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return NULL; 855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 877dbb3d5cf0c15f500944d211057644d6a2f37371Ben MurdochDynamicFunctionEntryHook GetProfilerDynamicFunctionEntryHookFunc() { 887dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch return NULL; 897dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch} 907dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 917dbb3d5cf0c15f500944d211057644d6a2f37371Ben MurdochAddDynamicSymbol GetProfilerAddDynamicSymbolFunc() { 927dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch return NULL; 937dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch} 947dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 957dbb3d5cf0c15f500944d211057644d6a2f37371Ben MurdochMoveDynamicSymbol GetProfilerMoveDynamicSymbolFunc() { 967dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch return NULL; 977dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch} 987dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#else // defined(OS_WIN) 1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// http://blogs.msdn.com/oldnewthing/archive/2004/10/25/247180.aspx 1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)extern "C" IMAGE_DOS_HEADER __ImageBase; 1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool IsBinaryInstrumented() { 1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) enum InstrumentationCheckState { 1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) UNINITIALIZED, 1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) INSTRUMENTED_IMAGE, 1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NON_INSTRUMENTED_IMAGE, 1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) }; 1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) static InstrumentationCheckState state = UNINITIALIZED; 1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (state == UNINITIALIZED) { 1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) HMODULE this_module = reinterpret_cast<HMODULE>(&__ImageBase); 1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::win::PEImage image(this_module); 1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Check to be sure our image is structured as we'd expect. 1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(image.VerifyMagic()); 1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Syzygy-instrumented binaries contain a PE image section named ".thunks", 1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // and all Syzygy-modified binaries contain the ".syzygy" image section. 1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // This is a very fast check, as it only looks at the image header. 1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if ((image.GetImageSectionHeaderByName(".thunks") != NULL) && 1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) (image.GetImageSectionHeaderByName(".syzygy") != NULL)) { 1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) state = INSTRUMENTED_IMAGE; 1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) state = NON_INSTRUMENTED_IMAGE; 1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(state != UNINITIALIZED); 1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return state == INSTRUMENTED_IMAGE; 1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1357dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochnamespace { 1367dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 1377dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochstruct FunctionSearchContext { 1387dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch const char* name; 1397dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch FARPROC function; 1407dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch}; 1417dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Callback function to PEImage::EnumImportChunks. 1437dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochbool FindResolutionFunctionInImports( 1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const base::win::PEImage &image, const char* module_name, 1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) PIMAGE_THUNK_DATA unused_name_table, PIMAGE_THUNK_DATA import_address_table, 1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) PVOID cookie) { 1477dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch FunctionSearchContext* context = 1487dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch reinterpret_cast<FunctionSearchContext*>(cookie); 1497dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 1507dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch DCHECK_NE(static_cast<FunctionSearchContext*>(NULL), context); 1517dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch DCHECK_EQ(static_cast<FARPROC>(NULL), context->function); 1527dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Our import address table contains pointers to the functions we import 1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // at this point. Let's retrieve the first such function and use it to 1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // find the module this import was resolved to by the loader. 1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const wchar_t* function_in_module = 1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) reinterpret_cast<const wchar_t*>(import_address_table->u1.Function); 1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Retrieve the module by a function in the module. 1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const DWORD kFlags = GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | 1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT; 1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) HMODULE module = NULL; 1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!::GetModuleHandleEx(kFlags, function_in_module, &module)) { 1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // This can happen if someone IAT patches us to a thunk. 1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // See whether this module exports the function we're looking for. 1697dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch FARPROC exported_func = ::GetProcAddress(module, context->name); 1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (exported_func != NULL) { 1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // We found it, return the function and terminate the enumeration. 1727dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch context->function = exported_func; 1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Keep going. 1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1807dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochtemplate <typename FunctionType> 1817dbb3d5cf0c15f500944d211057644d6a2f37371Ben MurdochFunctionType FindFunctionInImports(const char* function_name) { 1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!IsBinaryInstrumented()) 1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return NULL; 1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) HMODULE this_module = reinterpret_cast<HMODULE>(&__ImageBase); 1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::win::PEImage image(this_module); 1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1887dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch FunctionSearchContext ctx = { function_name, NULL }; 1897dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch image.EnumImportChunks(FindResolutionFunctionInImports, &ctx); 1907dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 1917dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch return reinterpret_cast<FunctionType>(ctx.function); 1927dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch} 1937dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 1947dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch} // namespace 1957dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 1967dbb3d5cf0c15f500944d211057644d6a2f37371Ben MurdochReturnAddressLocationResolver GetProfilerReturnAddrResolutionFunc() { 1977dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch return FindFunctionInImports<ReturnAddressLocationResolver>( 1987dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch "ResolveReturnAddressLocation"); 1997dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch} 2007dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 2017dbb3d5cf0c15f500944d211057644d6a2f37371Ben MurdochDynamicFunctionEntryHook GetProfilerDynamicFunctionEntryHookFunc() { 2027dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch return FindFunctionInImports<DynamicFunctionEntryHook>( 2037dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch "OnDynamicFunctionEntry"); 2047dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch} 2057dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 2067dbb3d5cf0c15f500944d211057644d6a2f37371Ben MurdochAddDynamicSymbol GetProfilerAddDynamicSymbolFunc() { 2077dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch return FindFunctionInImports<AddDynamicSymbol>( 2087dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch "AddDynamicSymbol"); 2097dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch} 2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2117dbb3d5cf0c15f500944d211057644d6a2f37371Ben MurdochMoveDynamicSymbol GetProfilerMoveDynamicSymbolFunc() { 2127dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch return FindFunctionInImports<MoveDynamicSymbol>( 2137dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch "MoveDynamicSymbol"); 2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif // defined(OS_WIN) 2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // namespace debug 2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // namespace base 220