forked from OSchip/llvm-project
ASan malloc/free history threads
Reviewed at http://reviews.llvm.org/D4596 llvm-svn: 217116
This commit is contained in:
parent
13046deef3
commit
beed821ffb
|
@ -342,6 +342,23 @@ public:
|
|||
|
||||
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
|
||||
|
|
|
@ -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_
|
|
@ -116,6 +116,7 @@ class Log;
|
|||
class LogChannel;
|
||||
class Mangled;
|
||||
class Materializer;
|
||||
class MemoryHistory;
|
||||
class Module;
|
||||
class ModuleList;
|
||||
class ModuleSpec;
|
||||
|
@ -316,6 +317,7 @@ namespace lldb {
|
|||
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;
|
||||
|
|
|
@ -39,6 +39,7 @@ namespace lldb_private
|
|||
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 lldb::MemoryHistorySP (*MemoryHistoryCreateInstance) (const lldb::ProcessSP &process_sp);
|
||||
typedef int (*ComparisonFunction)(const void *, const void *);
|
||||
typedef void (*DebuggerInitializeCallback)(Debugger &debugger);
|
||||
|
||||
|
|
|
@ -618,6 +618,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, ); }; };
|
||||
|
@ -1894,6 +1896,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>"; };
|
||||
|
@ -2338,6 +2344,7 @@
|
|||
260C897110F57C5600BB2B04 /* Plugins */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8C2D6A58197A1FB9006989C9 /* MemoryHistory */,
|
||||
26DB3E051379E7AD0080DC73 /* ABI */,
|
||||
260C897210F57C5600BB2B04 /* Disassembler */,
|
||||
260C897810F57C5600BB2B04 /* DynamicLoader */,
|
||||
|
@ -3661,6 +3668,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 */,
|
||||
|
@ -4114,6 +4123,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 = (
|
||||
|
@ -4903,6 +4929,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 */,
|
||||
|
@ -5126,6 +5153,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 */,
|
||||
|
|
|
@ -84,6 +84,7 @@ set( LLDB_USED_LIBS
|
|||
lldbPluginInstructionARM64
|
||||
lldbPluginObjectFilePECOFF
|
||||
lldbPluginOSPython
|
||||
lldbPluginMemoryHistoryASan
|
||||
)
|
||||
|
||||
# Need to export the API in the liblldb.dll for Windows
|
||||
|
|
|
@ -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,6 +1669,96 @@ protected:
|
|||
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 @@ CommandObjectMemory::CommandObjectMemory (CommandInterpreter &interpreter) :
|
|||
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 ()
|
||||
|
|
|
@ -2068,6 +2068,110 @@ PluginManager::GetUnwindAssemblyCreateCallbackForPluginName (const ConstString &
|
|||
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)
|
||||
{
|
||||
|
|
|
@ -4,6 +4,7 @@ add_subdirectory(DynamicLoader)
|
|||
add_subdirectory(Instruction)
|
||||
add_subdirectory(JITLoader)
|
||||
add_subdirectory(LanguageRuntime)
|
||||
add_subdirectory(MemoryHistory)
|
||||
add_subdirectory(ObjectContainer)
|
||||
add_subdirectory(ObjectFile)
|
||||
add_subdirectory(OperatingSystem)
|
||||
|
|
|
@ -24,7 +24,8 @@ DIRS := ABI/MacOSX-arm ABI/MacOSX-arm64 ABI/MacOSX-i386 ABI/SysV-x86_64 ABI/SysV
|
|||
DynamicLoader/POSIX-DYLD \
|
||||
DynamicLoader/Hexagon-DYLD \
|
||||
OperatingSystem/Python \
|
||||
SymbolVendor/ELF
|
||||
SymbolVendor/ELF \
|
||||
MemoryHistory/asan
|
||||
|
||||
ifeq ($(HOST_OS),Darwin)
|
||||
DIRS += Process/MacOSX-Kernel
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
add_subdirectory(asan)
|
|
@ -0,0 +1,5 @@
|
|||
set(LLVM_NO_RTTI 1)
|
||||
|
||||
add_lldb_library(lldbPluginMemoryHistoryASan
|
||||
MemoryHistoryASan.cpp
|
||||
)
|
|
@ -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
|
|
@ -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;
|
||||
|
||||
MemoryHistorySP
|
||||
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 MemoryHistorySP();
|
||||
|
||||
return MemoryHistorySP(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<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;
|
||||
}
|
|
@ -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 lldb::MemoryHistorySP
|
||||
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_
|
|
@ -101,6 +101,18 @@ public:
|
|||
{
|
||||
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
|
||||
|
|
|
@ -11,6 +11,7 @@ add_lldb_library(lldbTarget
|
|||
JITLoaderList.cpp
|
||||
LanguageRuntime.cpp
|
||||
Memory.cpp
|
||||
MemoryHistory.cpp
|
||||
NativeRegisterContext.cpp
|
||||
NativeRegisterContextRegisterInfo.cpp
|
||||
ObjCLanguageRuntime.cpp
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
//===-- 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)
|
||||
{
|
||||
return create_callback(process);
|
||||
}
|
||||
|
||||
return MemoryHistorySP();
|
||||
}
|
|
@ -92,6 +92,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;
|
||||
|
@ -154,6 +155,7 @@ lldb_private::Initialize ()
|
|||
#endif
|
||||
JITLoaderGDB::Initialize();
|
||||
ProcessElfCore::Initialize();
|
||||
MemoryHistoryASan::Initialize();
|
||||
|
||||
#if defined (__APPLE__)
|
||||
//----------------------------------------------------------------------
|
||||
|
@ -244,6 +246,7 @@ lldb_private::Terminate ()
|
|||
#endif
|
||||
JITLoaderGDB::Terminate();
|
||||
ProcessElfCore::Terminate();
|
||||
MemoryHistoryASan::Terminate();
|
||||
|
||||
#if defined (__APPLE__)
|
||||
DynamicLoaderMacOSXDYLD::Terminate();
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
LEVEL = ../../make
|
||||
|
||||
C_SOURCES := main.c
|
||||
CFLAGS := $(CFLAGS) -fsanitize=address -g
|
||||
|
||||
include $(LEVEL)/Makefile.rules
|
|
@ -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):
|
||||
compiler = self.findBuiltClang ()
|
||||
self.buildDsym (None, compiler)
|
||||
self.asan_tests ()
|
||||
|
||||
@dwarf_test
|
||||
def test_with_dwarf (self):
|
||||
compiler = self.findBuiltClang ()
|
||||
self.buildDwarf (None, compiler)
|
||||
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()
|
|
@ -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;
|
||||
}
|
|
@ -1325,6 +1325,22 @@ class Base(unittest2.TestCase):
|
|||
if not module.buildDwarf(self, architecture, compiler, dictionary, clean):
|
||||
raise Exception("Don't know how to build binary with dwarf")
|
||||
|
||||
def findBuiltClang(self):
|
||||
"""Tries to find and use Clang from the build directory as the compiler (instead of the system compiler)."""
|
||||
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):
|
||||
return path
|
||||
|
||||
return os.environ["CC"]
|
||||
|
||||
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.
|
||||
|
|
Loading…
Reference in New Issue