Hi,
I'm sending a patch that is a first take on implementing the ASan debugging
facilities into LLDB, namely retrieving malloc/free recorded stack traces for a
given address. This has been somewhat discussed in
http://lists.cs.uiuc.edu/pipermail/lldb-dev/2014-July/004491.html. What this
patch does is:
* Creates a new plugin type, MemoryHistory, and its implementation,
MemoryHistoryASan. The reason for a plugin is that we might want to reuse it
for other sources of recorded stack frames, like malloc_history (Darwin only).
* The plugin can only be instantiated for a process that has the ASan runtime
library loaded. Right now, I'm detecting the presence of the library by
checking that a specific symbol exists. Is there a better way?
* Extended the "memory" command and added "memory history [address]" which will
call the plugin's GetHistoryThreads function and print out the HistoryThread
objects.
* A test case that uses a simple ASanified binary and issues the "memory
history" command, validates that the returned stack trace contains the correct
line number of where a memory was allocated and freed. The test case requires
your compiler to understand -fsanitize=address and basically it should be a
very recently build clang (so it has the debugging API from this week).
There's a couple of things that I still need to do. I haven't yet exposed this
to the SB API, because the patch is already getting quite big. The instance of
MemoryHistory should probably be tied to the Process object (same way
LanguageRuntime is), so it doesn't get created for every command.
http://reviews.llvm.org/D4596
Files:
include/lldb/Core/PluginManager.h
include/lldb/Target/MemoryHistory.h
include/lldb/lldb-forward.h
include/lldb/lldb-private-interfaces.h
lldb.xcodeproj/project.pbxproj
source/CMakeLists.txt
source/Commands/CommandObjectMemory.cpp
source/Core/PluginManager.cpp
source/Plugins/CMakeLists.txt
source/Plugins/Makefile
source/Plugins/MemoryHistory/CMakeLists.txt
source/Plugins/MemoryHistory/asan/CMakeLists.txt
source/Plugins/MemoryHistory/asan/Makefile
source/Plugins/MemoryHistory/asan/MemoryHistoryASan.cpp
source/Plugins/MemoryHistory/asan/MemoryHistoryASan.h
source/Plugins/Process/Utility/HistoryThread.h
source/Target/CMakeLists.txt
source/Target/MemoryHistory.cpp
source/lldb.cpp
test/functionalities/asan/Makefile
test/functionalities/asan/TestAsan.py
test/functionalities/asan/main.c
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 <vector>
+
+// 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<lldb::ThreadSP> HistoryThreads;
+
+class MemoryHistory :
+ public std::enable_shared_from_this<MemoryHistory>,
+ 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<lldb_private::LineTable> LineTableSP;
typedef std::shared_ptr<lldb_private::Listener> ListenerSP;
typedef std::shared_ptr<lldb_private::LogChannel> LogChannelSP;
+ typedef std::shared_ptr<lldb_private::MemoryHistory> MemoryHistorySP;
typedef std::shared_ptr<lldb_private::Module> ModuleSP;
typedef std::weak_ptr<lldb_private::Module> ModuleWP;
typedef std::shared_ptr<lldb_private::ObjectFile> 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 = "<group>"; };
69A01E1F1236C5D400C660B5 /* Symbols.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Symbols.cpp; sourceTree = "<group>"; };
69A01E201236C5D400C660B5 /* TimeValue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TimeValue.cpp; sourceTree = "<group>"; };
+ 8C2D6A52197A1EAF006989C9 /* MemoryHistory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MemoryHistory.cpp; path = source/Target/MemoryHistory.cpp; sourceTree = "<group>"; };
+ 8C2D6A54197A1EBE006989C9 /* MemoryHistory.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = MemoryHistory.h; path = include/lldb/Target/MemoryHistory.h; sourceTree = "<group>"; };
+ 8C2D6A5A197A1FDC006989C9 /* MemoryHistoryASan.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MemoryHistoryASan.cpp; sourceTree = "<group>"; };
+ 8C2D6A5B197A1FDC006989C9 /* MemoryHistoryASan.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MemoryHistoryASan.h; sourceTree = "<group>"; };
94005E0313F438DF001EF42D /* python-wrapper.swig */ = {isa = PBXFileReference; lastKnownFileType = text; path = "python-wrapper.swig"; sourceTree = "<group>"; };
94005E0513F45A1B001EF42D /* embedded_interpreter.py */ = {isa = PBXFileReference; lastKnownFileType = text.script.python; name = embedded_interpreter.py; path = source/Interpreter/embedded_interpreter.py; sourceTree = "<group>"; };
94031A9F13CF5B3D00DCFF3C /* PriorityPointerPair.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = PriorityPointerPair.h; path = include/lldb/Utility/PriorityPointerPair.h; sourceTree = "<group>"; };
@@ -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 = "<group>";
};
+ 8C2D6A58197A1FB9006989C9 /* MemoryHistory */ = {
+ isa = PBXGroup;
+ children = (
+ 8C2D6A59197A1FCD006989C9 /* asan */,
+ );
+ path = MemoryHistory;
+ sourceTree = "<group>";
+ };
+ 8C2D6A59197A1FCD006989C9 /* asan */ = {
+ isa = PBXGroup;
+ children = (
+ 8C2D6A5A197A1FDC006989C9 /* MemoryHistoryASan.cpp */,
+ 8C2D6A5B197A1FDC006989C9 /* MemoryHistoryASan.h */,
+ );
+ path = asan;
+ sourceTree = "<group>";
+ };
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<MemoryHistoryInstance> 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.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("memory-history-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<lldb::addr_t> 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<lldb::addr_t> 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/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/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,84 @@
+"""
+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__)
+
+ @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
+ @dsym_test
+ def test_with_dsym (self):
+ self.buildDsym ()
+ self.asan_tests ()
+
+ @dwarf_test
+ def test_with_dwarf (self):
+ 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 <stdio.h>
+#include <stdlib.h>
+
+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;
+}
_______________________________________________
lldb-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/lldb-commits