Index: include/lldb/Core/PluginManager.h =================================================================== --- include/lldb/Core/PluginManager.h +++ include/lldb/Core/PluginManager.h @@ -342,6 +342,23 @@ static UnwindAssemblyCreateInstance GetUnwindAssemblyCreateCallbackForPluginName (const ConstString &name); + + //------------------------------------------------------------------ + // MemoryHistory + //------------------------------------------------------------------ + static bool + RegisterPlugin (const ConstString &name, + const char *description, + MemoryHistoryCreateInstance create_callback); + + static bool + UnregisterPlugin (MemoryHistoryCreateInstance create_callback); + + static MemoryHistoryCreateInstance + GetMemoryHistoryCreateCallbackAtIndex (uint32_t idx); + + static MemoryHistoryCreateInstance + GetMemoryHistoryCreateCallbackForPluginName (const ConstString &name); //------------------------------------------------------------------ // Some plug-ins might register a DebuggerInitializeCallback Index: include/lldb/Target/MemoryHistory.h =================================================================== --- include/lldb/Target/MemoryHistory.h +++ include/lldb/Target/MemoryHistory.h @@ -0,0 +1,42 @@ +//===-- MemoryHistory.h ---------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_MemoryHistory_h_ +#define liblldb_MemoryHistory_h_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/lldb-types.h" +#include "lldb/Core/PluginInterface.h" + +namespace lldb_private { + +typedef std::vector HistoryThreads; + +class MemoryHistory : + public std::enable_shared_from_this, + public PluginInterface +{ +public: + + static lldb::MemoryHistorySP + FindPlugin (const lldb::ProcessSP process); + + virtual HistoryThreads + GetHistoryThreads(lldb::addr_t address) = 0; +}; + +} // namespace lldb_private + +#endif // liblldb_MemoryHistory_h_ Index: include/lldb/lldb-forward.h =================================================================== --- include/lldb/lldb-forward.h +++ include/lldb/lldb-forward.h @@ -116,6 +116,7 @@ class LogChannel; class Mangled; class Materializer; +class MemoryHistory; class Module; class ModuleList; class ModuleSpec; @@ -316,6 +317,7 @@ typedef std::shared_ptr LineTableSP; typedef std::shared_ptr ListenerSP; typedef std::shared_ptr LogChannelSP; + typedef std::shared_ptr MemoryHistorySP; typedef std::shared_ptr ModuleSP; typedef std::weak_ptr ModuleWP; typedef std::shared_ptr ObjectFileSP; Index: include/lldb/lldb-private-interfaces.h =================================================================== --- include/lldb/lldb-private-interfaces.h +++ include/lldb/lldb-private-interfaces.h @@ -39,6 +39,7 @@ typedef bool (*ThreadPlanShouldStopHereCallback) (ThreadPlan *current_plan, Flags &flags, lldb::FrameComparison operation, void *baton); typedef lldb::ThreadPlanSP (*ThreadPlanStepFromHereCallback) (ThreadPlan *current_plan, Flags &flags, lldb::FrameComparison operation, void *baton); typedef UnwindAssembly* (*UnwindAssemblyCreateInstance) (const ArchSpec &arch); + typedef MemoryHistory* (*MemoryHistoryCreateInstance) (const lldb::ProcessSP &process_sp); typedef int (*ComparisonFunction)(const void *, const void *); typedef bool (*CommandOverrideCallback)(void *baton, const char **argv); typedef void (*DebuggerInitializeCallback)(Debugger &debugger); Index: lldb.xcodeproj/project.pbxproj =================================================================== --- lldb.xcodeproj/project.pbxproj +++ lldb.xcodeproj/project.pbxproj @@ -614,6 +614,8 @@ 4CF3D80C15AF4DC800845BF3 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EDB919B414F6F10D008FF64B /* Security.framework */; }; 4CF52AF51428291E0051E832 /* SBFileSpecList.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CF52AF41428291E0051E832 /* SBFileSpecList.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4CF52AF8142829390051E832 /* SBFileSpecList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CF52AF7142829390051E832 /* SBFileSpecList.cpp */; }; + 8C2D6A53197A1EAF006989C9 /* MemoryHistory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8C2D6A52197A1EAF006989C9 /* MemoryHistory.cpp */; }; + 8C2D6A5E197A250F006989C9 /* MemoryHistoryASan.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8C2D6A5A197A1FDC006989C9 /* MemoryHistoryASan.cpp */; }; 94094C6B163B6F840083A547 /* ValueObjectCast.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94094C69163B6CD90083A547 /* ValueObjectCast.cpp */; }; 94145431175E63B500284436 /* lldb-versioning.h in Headers */ = {isa = PBXBuildFile; fileRef = 94145430175D7FDE00284436 /* lldb-versioning.h */; settings = {ATTRIBUTES = (Public, ); }; }; 941BCC7F14E48C4000BB969C /* SBTypeFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 9461568614E355F2003A195C /* SBTypeFilter.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -1838,6 +1840,10 @@ 69A01E1E1236C5D400C660B5 /* Mutex.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Mutex.cpp; sourceTree = ""; }; 69A01E1F1236C5D400C660B5 /* Symbols.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Symbols.cpp; sourceTree = ""; }; 69A01E201236C5D400C660B5 /* TimeValue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TimeValue.cpp; sourceTree = ""; }; + 8C2D6A52197A1EAF006989C9 /* MemoryHistory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MemoryHistory.cpp; path = source/Target/MemoryHistory.cpp; sourceTree = ""; }; + 8C2D6A54197A1EBE006989C9 /* MemoryHistory.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = MemoryHistory.h; path = include/lldb/Target/MemoryHistory.h; sourceTree = ""; }; + 8C2D6A5A197A1FDC006989C9 /* MemoryHistoryASan.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MemoryHistoryASan.cpp; sourceTree = ""; }; + 8C2D6A5B197A1FDC006989C9 /* MemoryHistoryASan.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MemoryHistoryASan.h; sourceTree = ""; }; 94005E0313F438DF001EF42D /* python-wrapper.swig */ = {isa = PBXFileReference; lastKnownFileType = text; path = "python-wrapper.swig"; sourceTree = ""; }; 94005E0513F45A1B001EF42D /* embedded_interpreter.py */ = {isa = PBXFileReference; lastKnownFileType = text.script.python; name = embedded_interpreter.py; path = source/Interpreter/embedded_interpreter.py; sourceTree = ""; }; 94031A9F13CF5B3D00DCFF3C /* PriorityPointerPair.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = PriorityPointerPair.h; path = include/lldb/Utility/PriorityPointerPair.h; sourceTree = ""; }; @@ -2254,6 +2260,7 @@ 260C897110F57C5600BB2B04 /* Plugins */ = { isa = PBXGroup; children = ( + 8C2D6A58197A1FB9006989C9 /* MemoryHistory */, 26DB3E051379E7AD0080DC73 /* ABI */, 260C897210F57C5600BB2B04 /* Disassembler */, 260C897810F57C5600BB2B04 /* DynamicLoader */, @@ -3562,6 +3569,8 @@ 4CB4430A12491DDA00C13DC2 /* LanguageRuntime.cpp */, 2690B36F1381D5B600ECFBAE /* Memory.h */, 2690B3701381D5C300ECFBAE /* Memory.cpp */, + 8C2D6A54197A1EBE006989C9 /* MemoryHistory.h */, + 8C2D6A52197A1EAF006989C9 /* MemoryHistory.cpp */, 2360092C193FB21500189DB1 /* MemoryRegionInfo.h */, 4CB443F612499B6E00C13DC2 /* ObjCLanguageRuntime.h */, 4CB443F212499B5000C13DC2 /* ObjCLanguageRuntime.cpp */, @@ -3966,6 +3975,23 @@ path = source/Host/common; sourceTree = ""; }; + 8C2D6A58197A1FB9006989C9 /* MemoryHistory */ = { + isa = PBXGroup; + children = ( + 8C2D6A59197A1FCD006989C9 /* asan */, + ); + path = MemoryHistory; + sourceTree = ""; + }; + 8C2D6A59197A1FCD006989C9 /* asan */ = { + isa = PBXGroup; + children = ( + 8C2D6A5A197A1FDC006989C9 /* MemoryHistoryASan.cpp */, + 8C2D6A5B197A1FDC006989C9 /* MemoryHistoryASan.h */, + ); + path = asan; + sourceTree = ""; + }; 9457596415349416005A9070 /* POSIX */ = { isa = PBXGroup; children = ( @@ -4749,6 +4775,7 @@ 2689005A13353E0400698AC0 /* ValueObjectList.cpp in Sources */, 2689005B13353E0400698AC0 /* ValueObjectRegister.cpp in Sources */, 2689005C13353E0400698AC0 /* ValueObjectVariable.cpp in Sources */, + 8C2D6A53197A1EAF006989C9 /* MemoryHistory.cpp in Sources */, 2689005D13353E0400698AC0 /* VMRange.cpp in Sources */, 2689005E13353E0E00698AC0 /* ClangASTSource.cpp in Sources */, 2689005F13353E0E00698AC0 /* ClangFunction.cpp in Sources */, @@ -4966,6 +4993,7 @@ 264A1300137252C700875C42 /* ARM64_DWARF_Registers.cpp in Sources */, 26DB3E161379E7AD0080DC73 /* ABIMacOSX_arm.cpp in Sources */, 26DB3E191379E7AD0080DC73 /* ABIMacOSX_arm64.cpp in Sources */, + 8C2D6A5E197A250F006989C9 /* MemoryHistoryASan.cpp in Sources */, 26DB3E1C1379E7AD0080DC73 /* ABIMacOSX_i386.cpp in Sources */, 26DB3E1F1379E7AD0080DC73 /* ABISysV_x86_64.cpp in Sources */, 232CB61D191E00CD00EF39FC /* SoftwareBreakpoint.cpp in Sources */, Index: source/CMakeLists.txt =================================================================== --- source/CMakeLists.txt +++ source/CMakeLists.txt @@ -81,6 +81,7 @@ lldbPluginInstructionARM64 lldbPluginObjectFilePECOFF lldbPluginOSPython + lldbPluginMemoryHistoryASan ) # Need to export the API in the liblldb.dll for Windows Index: source/Commands/CommandObjectMemory.cpp =================================================================== --- source/Commands/CommandObjectMemory.cpp +++ source/Commands/CommandObjectMemory.cpp @@ -33,8 +33,10 @@ #include "lldb/Interpreter/OptionGroupValueObjectDisplay.h" #include "lldb/Interpreter/OptionValueString.h" #include "lldb/Symbol/TypeList.h" +#include "lldb/Target/MemoryHistory.h" #include "lldb/Target/Process.h" #include "lldb/Target/StackFrame.h" +#include "lldb/Target/Thread.h" using namespace lldb; using namespace lldb_private; @@ -1667,7 +1669,97 @@ OptionGroupWriteMemory m_memory_options; }; +//---------------------------------------------------------------------- +// Get malloc/free history of a memory address. +//---------------------------------------------------------------------- +class CommandObjectMemoryHistory : public CommandObjectParsed +{ +public: + + CommandObjectMemoryHistory (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "memory history", + "Prints out the recorded stack traces for allocation/deallocation of a memory address.", + NULL, + eFlagRequiresTarget | eFlagRequiresProcess | eFlagProcessMustBePaused | eFlagProcessMustBeLaunched) + { + CommandArgumentEntry arg1; + CommandArgumentData addr_arg; + + // Define the first (and only) variant of this arg. + addr_arg.arg_type = eArgTypeAddress; + addr_arg.arg_repetition = eArgRepeatPlain; + + // There is only one variant this argument could be; put it into the argument entry. + arg1.push_back (addr_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg1); + } + + virtual + ~CommandObjectMemoryHistory () + { + } + + virtual const char *GetRepeatCommand (Args ¤t_command_args, uint32_t index) + { + return m_cmd_name.c_str(); + } + +protected: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + const size_t argc = command.GetArgumentCount(); + + if (argc == 0 || argc > 1) + { + result.AppendErrorWithFormat ("%s takes an address expression", m_cmd_name.c_str()); + result.SetStatus(eReturnStatusFailed); + return false; + } + + Error error; + lldb::addr_t addr = Args::StringToAddress (&m_exe_ctx, + command.GetArgumentAtIndex(0), + LLDB_INVALID_ADDRESS, + &error); + + if (addr == LLDB_INVALID_ADDRESS) + { + result.AppendError("invalid address expression"); + result.AppendError(error.AsCString()); + result.SetStatus(eReturnStatusFailed); + return false; + } + + Stream *output_stream = &result.GetOutputStream(); + + const ProcessSP &process_sp = m_exe_ctx.GetProcessSP(); + const MemoryHistorySP &memory_history = MemoryHistory::FindPlugin(process_sp); + + if (! memory_history.get()) + { + result.AppendError("no available memory history provider"); + result.SetStatus(eReturnStatusFailed); + return false; + } + + HistoryThreads thread_list = memory_history->GetHistoryThreads(addr); + + for (auto thread : thread_list) { + thread->GetStatus(*output_stream, 0, UINT32_MAX, 0); + } + + result.SetStatus(eReturnStatusSuccessFinishResult); + + return true; + } + +}; + //------------------------------------------------------------------------- // CommandObjectMemory //------------------------------------------------------------------------- @@ -1681,6 +1773,7 @@ LoadSubCommand ("find", CommandObjectSP (new CommandObjectMemoryFind (interpreter))); LoadSubCommand ("read", CommandObjectSP (new CommandObjectMemoryRead (interpreter))); LoadSubCommand ("write", CommandObjectSP (new CommandObjectMemoryWrite (interpreter))); + LoadSubCommand ("history", CommandObjectSP (new CommandObjectMemoryHistory (interpreter))); } CommandObjectMemory::~CommandObjectMemory () Index: source/Core/PluginManager.cpp =================================================================== --- source/Core/PluginManager.cpp +++ source/Core/PluginManager.cpp @@ -2058,6 +2058,110 @@ return NULL; } +#pragma mark MemoryHistory + +struct MemoryHistoryInstance +{ + MemoryHistoryInstance() : + name(), + description(), + create_callback(NULL) + { + } + + ConstString name; + std::string description; + MemoryHistoryCreateInstance create_callback; +}; + +typedef std::vector MemoryHistoryInstances; + +static Mutex & +GetMemoryHistoryMutex () +{ + static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive); + return g_instances_mutex; +} + +static MemoryHistoryInstances & +GetMemoryHistoryInstances () +{ + static MemoryHistoryInstances g_instances; + return g_instances; +} + +bool +PluginManager::RegisterPlugin +( + const ConstString &name, + const char *description, + MemoryHistoryCreateInstance create_callback + ) +{ + if (create_callback) + { + MemoryHistoryInstance instance; + assert ((bool)name); + instance.name = name; + if (description && description[0]) + instance.description = description; + instance.create_callback = create_callback; + Mutex::Locker locker (GetMemoryHistoryMutex ()); + GetMemoryHistoryInstances ().push_back (instance); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (MemoryHistoryCreateInstance create_callback) +{ + if (create_callback) + { + Mutex::Locker locker (GetMemoryHistoryMutex ()); + MemoryHistoryInstances &instances = GetMemoryHistoryInstances (); + + MemoryHistoryInstances::iterator pos, end = instances.end(); + for (pos = instances.begin(); pos != end; ++ pos) + { + if (pos->create_callback == create_callback) + { + instances.erase(pos); + return true; + } + } + } + return false; +} + +MemoryHistoryCreateInstance +PluginManager::GetMemoryHistoryCreateCallbackAtIndex (uint32_t idx) +{ + Mutex::Locker locker (GetMemoryHistoryMutex ()); + MemoryHistoryInstances &instances = GetMemoryHistoryInstances (); + if (idx < instances.size()) + return instances[idx].create_callback; + return NULL; +} + + +MemoryHistoryCreateInstance +PluginManager::GetMemoryHistoryCreateCallbackForPluginName (const ConstString &name) +{ + if (name) + { + Mutex::Locker locker (GetMemoryHistoryMutex ()); + MemoryHistoryInstances &instances = GetMemoryHistoryInstances (); + + MemoryHistoryInstances::iterator pos, end = instances.end(); + for (pos = instances.begin(); pos != end; ++ pos) + { + if (name == pos->name) + return pos->create_callback; + } + } + return NULL; +} + void PluginManager::DebuggerInitialize (Debugger &debugger) { Index: source/Plugins/CMakeLists.txt =================================================================== --- source/Plugins/CMakeLists.txt +++ source/Plugins/CMakeLists.txt @@ -4,6 +4,7 @@ add_subdirectory(Instruction) add_subdirectory(JITLoader) add_subdirectory(LanguageRuntime) +add_subdirectory(MemoryHistory) add_subdirectory(ObjectContainer) add_subdirectory(ObjectFile) add_subdirectory(OperatingSystem) Index: source/Plugins/Makefile =================================================================== --- source/Plugins/Makefile +++ source/Plugins/Makefile @@ -23,7 +23,8 @@ LanguageRuntime/ObjC/AppleObjCRuntime \ DynamicLoader/POSIX-DYLD \ OperatingSystem/Python \ - SymbolVendor/ELF + SymbolVendor/ELF \ + MemoryHistory/asan ifeq ($(HOST_OS),Darwin) DIRS += Process/MacOSX-Kernel Index: source/Plugins/MemoryHistory/CMakeLists.txt =================================================================== --- source/Plugins/MemoryHistory/CMakeLists.txt +++ source/Plugins/MemoryHistory/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(asan) Index: source/Plugins/MemoryHistory/asan/CMakeLists.txt =================================================================== --- source/Plugins/MemoryHistory/asan/CMakeLists.txt +++ source/Plugins/MemoryHistory/asan/CMakeLists.txt @@ -0,0 +1,5 @@ +set(LLVM_NO_RTTI 1) + +add_lldb_library(lldbPluginMemoryHistoryASan + MemoryHistoryASan.cpp + ) Index: source/Plugins/MemoryHistory/asan/Makefile =================================================================== --- source/Plugins/MemoryHistory/asan/Makefile +++ source/Plugins/MemoryHistory/asan/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/MemoryHistory/asan/Makefile -------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginMemoryHistoryASan +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile Index: source/Plugins/MemoryHistory/asan/MemoryHistoryASan.h =================================================================== --- source/Plugins/MemoryHistory/asan/MemoryHistoryASan.h +++ source/Plugins/MemoryHistory/asan/MemoryHistoryASan.h @@ -0,0 +1,62 @@ +//===-- MemoryHistoryASan.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_MemoryHistoryASan_h_ +#define liblldb_MemoryHistoryASan_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Target/ABI.h" +#include "lldb/Target/MemoryHistory.h" +#include "lldb/Target/Process.h" + +namespace lldb_private { + +class MemoryHistoryASan : public lldb_private::MemoryHistory +{ +public: + + static MemoryHistory * + CreateInstance (const lldb::ProcessSP &process_sp); + + static void + Initialize(); + + static void + Terminate(); + + static lldb_private::ConstString + GetPluginNameStatic(); + + virtual + ~MemoryHistoryASan () {} + + virtual lldb_private::ConstString + GetPluginName() { return GetPluginNameStatic(); } + + virtual uint32_t + GetPluginVersion() { return 1; } + + virtual lldb_private::HistoryThreads + GetHistoryThreads(lldb::addr_t address); + +private: + + MemoryHistoryASan(const lldb::ProcessSP &process_sp); + + lldb::ProcessSP m_process_sp; + +}; + +} // namespace lldb_private + +#endif // liblldb_MemoryHistoryASan_h_ Index: source/Plugins/MemoryHistory/asan/MemoryHistoryASan.cpp =================================================================== --- source/Plugins/MemoryHistory/asan/MemoryHistoryASan.cpp +++ source/Plugins/MemoryHistory/asan/MemoryHistoryASan.cpp @@ -0,0 +1,185 @@ +//===-- MemoryHistoryASan.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MemoryHistoryASan.h" + +#include "lldb/Target/MemoryHistory.h" + +#include "lldb/lldb-private.h" +#include "lldb/Core/PluginInterface.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Target/ThreadList.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Core/Module.h" +#include "Plugins/Process/Utility/HistoryThread.h" +#include "lldb/Core/ValueObject.h" + +using namespace lldb; +using namespace lldb_private; + +MemoryHistory * +MemoryHistoryASan::CreateInstance (const ProcessSP &process_sp) +{ + if (!process_sp.get()) + return NULL; + + Target & target = process_sp->GetTarget(); + + bool found_asan_runtime = false; + + const ModuleList &target_modules = target.GetImages(); + Mutex::Locker modules_locker(target_modules.GetMutex()); + const size_t num_modules = target_modules.GetSize(); + for (size_t i = 0; i < num_modules; ++i) + { + Module *module_pointer = target_modules.GetModulePointerAtIndexUnlocked(i); + + SymbolContextList sc_list; + const bool include_symbols = true; + const bool append = true; + const bool include_inlines = true; + + size_t num_matches = module_pointer->FindFunctions(ConstString("__asan_get_alloc_stack"), NULL, eFunctionNameTypeAuto, include_symbols, include_inlines, append, sc_list); + + if (num_matches) + { + found_asan_runtime = true; + break; + } + } + + if (! found_asan_runtime) + return NULL; + + return new MemoryHistoryASan(process_sp); +} + +void +MemoryHistoryASan::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + "ASan memory history provider.", + CreateInstance); +} + +void +MemoryHistoryASan::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + + +ConstString +MemoryHistoryASan::GetPluginNameStatic() +{ + static ConstString g_name("asan"); + return g_name; +} + +MemoryHistoryASan::MemoryHistoryASan(const ProcessSP &process_sp) +{ + this->m_process_sp = process_sp; +} + +const char * +memory_history_asan_command_format = R"( + struct t { + void *alloc_trace[256]; + size_t alloc_count; + int alloc_tid; + + void *free_trace[256]; + size_t free_count; + int free_tid; + } t; + + t.alloc_count = ((size_t (*) (void *, void **, size_t, int *))__asan_get_alloc_stack)((void *)0x%)" PRIx64 R"(, t.alloc_trace, 256, &t.alloc_tid); + t.free_count = ((size_t (*) (void *, void **, size_t, int *))__asan_get_free_stack)((void *)0x%)" PRIx64 R"(, t.free_trace, 256, &t.free_tid); + + t; +)"; + +#define GET_STACK_FUNCTION_TIMEOUT_USEC 2*1000*1000 + +HistoryThreads +MemoryHistoryASan::GetHistoryThreads(lldb::addr_t address) +{ + ProcessSP process_sp = m_process_sp; + ThreadSP thread_sp = m_process_sp->GetThreadList().GetSelectedThread(); + StackFrameSP frame_sp = thread_sp->GetSelectedFrame(); + + if (!frame_sp) + { + return HistoryThreads(); + } + + ExecutionContext exe_ctx (frame_sp); + ValueObjectSP return_value_sp; + StreamString expr; + expr.Printf(memory_history_asan_command_format, address, address); + + EvaluateExpressionOptions options; + options.SetUnwindOnError(true); + options.SetTryAllThreads(true); + options.SetStopOthers(true); + options.SetIgnoreBreakpoints(true); + options.SetTimeoutUsec(GET_STACK_FUNCTION_TIMEOUT_USEC); + + if (m_process_sp->GetTarget().EvaluateExpression(expr.GetData(), frame_sp.get(), return_value_sp, options) != eExpressionCompleted) + { + return HistoryThreads(); + } + if (!return_value_sp) + { + return HistoryThreads(); + } + + HistoryThreads result; + + int alloc_count = return_value_sp->GetValueForExpressionPath(".alloc_count")->GetValueAsUnsigned(0); + int free_count = return_value_sp->GetValueForExpressionPath(".free_count")->GetValueAsUnsigned(0); + tid_t alloc_tid = return_value_sp->GetValueForExpressionPath(".alloc_tid")->GetValueAsUnsigned(0); + tid_t free_tid = return_value_sp->GetValueForExpressionPath(".free_tid")->GetValueAsUnsigned(0); + + if (alloc_count > 0) + { + std::vector pcs; + ValueObjectSP trace_sp = return_value_sp->GetValueForExpressionPath(".alloc_trace"); + for (int i = 0; i < alloc_count; i++) { + addr_t pc = trace_sp->GetChildAtIndex(i, true)->GetValueAsUnsigned(0); + pcs.push_back(pc); + } + + HistoryThread *history_thread = new HistoryThread(*process_sp, alloc_tid, pcs, 0, false); + ThreadSP new_thread_sp(history_thread); + // let's use thread name for the type of history thread, since history threads don't have names anyway + history_thread->SetThreadName("Memory allocated at"); + result.push_back(new_thread_sp); + } + + if (free_count > 0) + { + std::vector pcs; + ValueObjectSP trace_sp = return_value_sp->GetValueForExpressionPath(".free_trace"); + for (int i = 0; i < free_count; i++) { + addr_t pc = trace_sp->GetChildAtIndex(i, true)->GetValueAsUnsigned(0); + pcs.push_back(pc); + } + + HistoryThread *history_thread = new HistoryThread(*process_sp, free_tid, pcs, 0, false); + ThreadSP new_thread_sp(history_thread); + // let's use thread name for the type of history thread, since history threads don't have names anyway + history_thread->SetThreadName("Memory deallocated at"); + result.push_back(new_thread_sp); + } + + return result; +} Index: source/Plugins/Process/Utility/HistoryThread.h =================================================================== --- source/Plugins/Process/Utility/HistoryThread.h +++ source/Plugins/Process/Utility/HistoryThread.h @@ -101,6 +101,18 @@ { m_thread_name = name; } + + virtual const char * + GetName () + { + return m_thread_name.c_str(); + } + + virtual void + SetName(const char *name) + { + m_thread_name = name; + } protected: virtual lldb::StackFrameListSP Index: source/Target/CMakeLists.txt =================================================================== --- source/Target/CMakeLists.txt +++ source/Target/CMakeLists.txt @@ -10,6 +10,7 @@ JITLoaderList.cpp LanguageRuntime.cpp Memory.cpp + MemoryHistory.cpp NativeRegisterContext.cpp NativeRegisterContextRegisterInfo.cpp ObjCLanguageRuntime.cpp Index: source/Target/MemoryHistory.cpp =================================================================== --- source/Target/MemoryHistory.cpp +++ source/Target/MemoryHistory.cpp @@ -0,0 +1,31 @@ +//===-- MemoryHistory.cpp -------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Target/MemoryHistory.h" + +#include "lldb/Core/PluginManager.h" + +using namespace lldb; +using namespace lldb_private; + +lldb::MemoryHistorySP +MemoryHistory::FindPlugin (const ProcessSP process) +{ + MemoryHistoryCreateInstance create_callback = NULL; + + for (uint32_t idx = 0; (create_callback = PluginManager::GetMemoryHistoryCreateCallbackAtIndex(idx)) != NULL; ++idx) + { + MemoryHistorySP memory_history_sp(create_callback(process)); + + if (memory_history_sp.get()) + return memory_history_sp; + } + + return MemoryHistorySP(); +} Index: source/lldb.cpp =================================================================== --- source/lldb.cpp +++ source/lldb.cpp @@ -85,6 +85,7 @@ #include "Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h" #include "Plugins/Process/gdb-remote/ProcessGDBRemote.h" #include "Plugins/DynamicLoader/Static/DynamicLoaderStatic.h" +#include "Plugins/MemoryHistory/asan/MemoryHistoryASan.h" using namespace lldb; using namespace lldb_private; @@ -131,6 +132,7 @@ #endif JITLoaderGDB::Initialize(); ProcessElfCore::Initialize(); + MemoryHistoryASan::Initialize(); #if defined (__APPLE__) //---------------------------------------------------------------------- @@ -217,6 +219,7 @@ #endif JITLoaderGDB::Terminate(); ProcessElfCore::Terminate(); + MemoryHistoryASan::Terminate(); #if defined (__APPLE__) DynamicLoaderMacOSXDYLD::Terminate(); Index: test/functionalities/asan/Makefile =================================================================== --- test/functionalities/asan/Makefile +++ test/functionalities/asan/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../make + +C_SOURCES := main.c +CFLAGS := $(CFLAGS) -fsanitize=address -g + +include $(LEVEL)/Makefile.rules Index: test/functionalities/asan/TestAsan.py =================================================================== --- test/functionalities/asan/TestAsan.py +++ test/functionalities/asan/TestAsan.py @@ -0,0 +1,90 @@ +""" +Test that ASan memory history provider returns correct stack traces +""" + +import os, time +import unittest2 +import lldb +from lldbtest import * +import lldbutil + +class AsanTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + # The default compiler ("clang") may not support Address Sanitizer or it + # may not have the debugging API which was recently added, so we're calling + # self.useBuiltClang() to use clang from the llvm-build directory instead + + @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") + @dsym_test + def test_with_dsym (self): + self.useBuiltClang () + self.buildDsym () + self.asan_tests () + + @dwarf_test + def test_with_dwarf (self): + self.useBuiltClang () + self.buildDwarf () + self.asan_tests () + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + self.line_malloc = line_number('main.c', '// malloc line') + self.line_malloc2 = line_number('main.c', '// malloc2 line') + self.line_free = line_number('main.c', '// free line') + self.line_breakpoint = line_number('main.c', '// break line') + + def asan_tests (self): + exe = os.path.join (os.getcwd(), "a.out") + self.expect("file " + exe, patterns = [ "Current executable set to .*a.out" ]) + + self.runCmd("breakpoint set -f main.c -l %d" % self.line_breakpoint) + + # "memory history" command should not work without a process + self.expect("memory history 0", + error = True, + substrs = ["invalid process"]) + + self.runCmd("run") + + # ASan will relaunch the process to insert its library. + self.expect("thread list", "Process should be stopped due to exec.", + substrs = ['stopped', 'stop reason = exec']) + + self.runCmd("continue") + + # the stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', 'stop reason = breakpoint']) + + # test that the ASan dylib is present + self.expect("image lookup -n __asan_describe_address", "__asan_describe_address should be present", + substrs = ['1 match found']) + + # test the 'memory history' command + self.expect("memory history 'pointer'", + substrs = [ + 'Memory allocated at', 'a.out`f1', 'main.c:%d' % self.line_malloc, + 'Memory deallocated at', 'a.out`f2', 'main.c:%d' % self.line_free]) + + self.runCmd("breakpoint set -n __asan_report_error") + + self.runCmd("continue") + + # the stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', 'stop reason = breakpoint']) + + # make sure the 'memory history' command still works even when we're generating a report now + self.expect("memory history 'another_pointer'", + substrs = [ + 'Memory allocated at', 'a.out`f1', 'main.c:%d' % self.line_malloc2]) + +if __name__ == '__main__': + import atexit + lldb.SBDebugger.Initialize() + atexit.register(lambda: lldb.SBDebugger.Terminate()) + unittest2.main() Index: test/functionalities/asan/main.c =================================================================== --- test/functionalities/asan/main.c +++ test/functionalities/asan/main.c @@ -0,0 +1,34 @@ +//===-- main.c --------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include +#include + +char *pointer; +char *another_pointer; + +void f1() { + pointer = malloc(10); // malloc line + another_pointer = malloc(20); // malloc2 line +} + +void f2() { + free(pointer); // free line +} + +int main (int argc, char const *argv[]) +{ + f1(); + f2(); + + printf("Hello world!\n"); // break line + + pointer[0] = 'A'; // BOOM + + return 0; +} Index: test/lldbtest.py =================================================================== --- test/lldbtest.py +++ test/lldbtest.py @@ -1303,6 +1303,23 @@ if not module.buildDwarf(self, architecture, compiler, dictionary, clean): raise Exception("Don't know how to build binary with dwarf") + def useBuiltClang(self): + """Tries to find and use Clang from the build directory as the compiler (instead of the system compiler).""" + if not os.environ["CC"] == "clang": + return + paths_to_try = [ + "llvm-build/Release+Asserts/x86_64/Release+Asserts/bin/clang", + "llvm-build/Debug+Asserts/x86_64/Debug+Asserts/bin/clang", + "llvm-build/Release/x86_64/Release/bin/clang", + "llvm-build/Debug/x86_64/Debug/bin/clang", + ] + lldb_root_path = os.path.join(os.path.dirname(__file__), "..") + for p in paths_to_try: + path = os.path.join(lldb_root_path, p) + if os.path.exists(path): + os.environ["CC"] = path + return + def getBuildFlags(self, use_cpp11=True, use_libcxx=False, use_libstdcxx=False, use_pthreads=True): """ Returns a dictionary (which can be provided to build* functions above) which contains OS-specific build flags.