Index: include/lldb/API/SBThread.h =================================================================== --- include/lldb/API/SBThread.h +++ include/lldb/API/SBThread.h @@ -78,7 +78,10 @@ //-------------------------------------------------------------------------- uint64_t GetStopReasonDataAtIndex(uint32_t idx); - + + bool + GetStopReasonExtendedInfoAsJSON (lldb::SBStream &stream); + size_t GetStopDescription (char *dst, size_t dst_len); Index: include/lldb/Core/PluginManager.h =================================================================== --- include/lldb/Core/PluginManager.h +++ include/lldb/Core/PluginManager.h @@ -361,6 +361,28 @@ GetMemoryHistoryCreateCallbackForPluginName (const ConstString &name); //------------------------------------------------------------------ + // InstrumentationRuntime + //------------------------------------------------------------------ + static bool + RegisterPlugin (const ConstString &name, + const char *description, + InstrumentationRuntimeCreateInstance create_callback, + InstrumentationRuntimeGetType get_type_callback); + + static bool + UnregisterPlugin (InstrumentationRuntimeCreateInstance create_callback); + + static InstrumentationRuntimeGetType + GetInstrumentationRuntimeGetTypeCallbackAtIndex (uint32_t idx); + + static InstrumentationRuntimeCreateInstance + GetInstrumentationRuntimeCreateCallbackAtIndex (uint32_t idx); + + static InstrumentationRuntimeCreateInstance + GetInstrumentationRuntimeCreateCallbackForPluginName (const ConstString &name); + + + //------------------------------------------------------------------ // Some plug-ins might register a DebuggerInitializeCallback // callback when registering the plug-in. After a new Debugger // instance is created, this DebuggerInitialize function will get Index: include/lldb/Target/InstrumentationRuntime.h =================================================================== --- include/lldb/Target/InstrumentationRuntime.h +++ include/lldb/Target/InstrumentationRuntime.h @@ -0,0 +1,47 @@ +//===-- InstrumentationRuntime.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_InstrumentationRuntime_h_ +#define liblldb_InstrumentationRuntime_h_ + +// C Includes +// C++ Includes +#include +#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::map InstrumentationRuntimeCollection; + +class InstrumentationRuntime : + public std::enable_shared_from_this, + public PluginInterface +{ +public: + + static void + ModulesDidLoad(lldb_private::ModuleList &module_list, Process *process, InstrumentationRuntimeCollection &runtimes); + + virtual void + ModulesDidLoad(lldb_private::ModuleList &module_list); + + virtual bool + IsActive(); + +}; + +} // namespace lldb_private + +#endif // liblldb_InstrumentationRuntime_h_ Index: include/lldb/Target/InstrumentationRuntimeStopInfo.h =================================================================== --- include/lldb/Target/InstrumentationRuntimeStopInfo.h +++ include/lldb/Target/InstrumentationRuntimeStopInfo.h @@ -0,0 +1,52 @@ +//===-- InstrumentationRuntimeStopInfo.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_InstrumentationRuntimeStopInfo_h_ +#define liblldb_InstrumentationRuntimeStopInfo_h_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/Target/StopInfo.h" +#include "lldb/Core/StructuredData.h" + +namespace lldb_private { + +class InstrumentationRuntimeStopInfo : public StopInfo +{ +public: + + virtual ~InstrumentationRuntimeStopInfo() + { + } + + virtual lldb::StopReason + GetStopReason () const + { + return lldb::eStopReasonInstrumentation; + } + + virtual const char * + GetDescription (); + + static lldb::StopInfoSP + CreateStopReasonWithInstrumentationData (Thread &thread, std::string description, StructuredData::ObjectSP additional_data); + +private: + + InstrumentationRuntimeStopInfo(Thread &thread, std::string description, StructuredData::ObjectSP additional_data); + +}; + +} // namespace lldb_private + +#endif // liblldb_InstrumentationRuntimeStopInfo_h_ Index: include/lldb/Target/Process.h =================================================================== --- include/lldb/Target/Process.h +++ include/lldb/Target/Process.h @@ -52,6 +52,7 @@ #include "lldb/Target/ThreadList.h" #include "lldb/Target/UnixSignals.h" #include "lldb/Utility/PseudoTerminal.h" +#include "lldb/Target/InstrumentationRuntime.h" namespace lldb_private { @@ -3082,6 +3083,7 @@ AllocatedMemoryCache m_allocated_memory_cache; bool m_should_detach; /// Should we detach if the process object goes away with an explicit call to Kill or Detach? LanguageRuntimeCollection m_language_runtimes; + InstrumentationRuntimeCollection m_instrumentation_runtimes; std::unique_ptr m_next_event_action_ap; std::vector m_pre_resume_actions; ProcessRunLock m_public_run_lock; Index: include/lldb/Target/StopInfo.h =================================================================== --- include/lldb/Target/StopInfo.h +++ include/lldb/Target/StopInfo.h @@ -18,6 +18,7 @@ // Project includes #include "lldb/lldb-public.h" #include "lldb/Target/Process.h" +#include "lldb/Core/StructuredData.h" namespace lldb_private { @@ -140,6 +141,12 @@ return m_override_should_stop == eLazyBoolYes; } + StructuredData::ObjectSP + GetExtendedInfo () + { + return m_extended_info; + } + static lldb::StopInfoSP CreateStopReasonWithBreakpointSiteID (Thread &thread, lldb::break_id_t break_id); @@ -211,6 +218,8 @@ LazyBool m_override_should_notify; LazyBool m_override_should_stop; + StructuredData::ObjectSP m_extended_info; // The extended info for this stop info + // This determines whether the target has run since this stop info. // N.B. running to evaluate a user expression does not count. bool HasTargetRunSinceMe (); Index: include/lldb/Target/Thread.h =================================================================== --- include/lldb/Target/Thread.h +++ include/lldb/Target/Thread.h @@ -535,7 +535,7 @@ DumpUsingSettingsFormat (Stream &strm, uint32_t frame_idx); bool - GetDescription (Stream &s, lldb::DescriptionLevel level, bool json_output); + GetDescription (Stream &s, lldb::DescriptionLevel level, bool print_json_thread, bool print_json_stopinfo); //------------------------------------------------------------------ /// Default implementation for stepping into. Index: include/lldb/lldb-enumerations.h =================================================================== --- include/lldb/lldb-enumerations.h +++ include/lldb/lldb-enumerations.h @@ -185,7 +185,8 @@ eStopReasonException, eStopReasonExec, // Program was re-exec'ed eStopReasonPlanComplete, - eStopReasonThreadExiting + eStopReasonThreadExiting, + eStopReasonInstrumentation } StopReason; //---------------------------------------------------------------------- @@ -387,6 +388,11 @@ eLanguageTypeDylan = 0x0020, ///< Dylan. eNumLanguageTypes } LanguageType; + + typedef enum InstrumentationRuntimeType { + eInstrumentationRuntimeTypeAddressSanitizer = 0x0000, + eNumInstrumentationRuntimeTypes + } InstrumentationRuntimeType; typedef enum DynamicValueType { Index: include/lldb/lldb-forward.h =================================================================== --- include/lldb/lldb-forward.h +++ include/lldb/lldb-forward.h @@ -104,6 +104,7 @@ class InlineFunctionInfo; class Instruction; class InstructionList; +class InstrumentationRuntime; class IOHandler; class IOObject; class IRExecutionUnit; @@ -312,6 +313,7 @@ typedef std::shared_ptr FuncUnwindersSP; typedef std::shared_ptr InlineFunctionInfoSP; typedef std::shared_ptr InstructionSP; + typedef std::shared_ptr InstrumentationRuntimeSP; typedef std::shared_ptr IOHandlerSP; typedef std::shared_ptr IOObjectSP; typedef std::shared_ptr JITLoaderSP; Index: include/lldb/lldb-private-interfaces.h =================================================================== --- include/lldb/lldb-private-interfaces.h +++ include/lldb/lldb-private-interfaces.h @@ -40,6 +40,8 @@ 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 lldb::InstrumentationRuntimeType (*InstrumentationRuntimeGetType) (); + typedef lldb::InstrumentationRuntimeSP (*InstrumentationRuntimeCreateInstance) (const lldb::ProcessSP &process_sp); typedef int (*ComparisonFunction)(const void *, const void *); typedef void (*DebuggerInitializeCallback)(Debugger &debugger); Index: lldb.xcodeproj/project.pbxproj =================================================================== --- lldb.xcodeproj/project.pbxproj +++ lldb.xcodeproj/project.pbxproj @@ -638,6 +638,9 @@ 8CCB017E19BA28A80009FD44 /* ThreadCollection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CCB017A19BA283D0009FD44 /* ThreadCollection.cpp */; }; 8CCB018219BA4E270009FD44 /* SBThreadCollection.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CCB018119BA4E210009FD44 /* SBThreadCollection.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8CCB018319BA51BF0009FD44 /* SBThreadCollection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CCB017F19BA4DD00009FD44 /* SBThreadCollection.cpp */; }; + 8CF02AE919DCC01900B14BE0 /* InstrumentationRuntime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CF02ADF19DCBF3B00B14BE0 /* InstrumentationRuntime.cpp */; }; + 8CF02AEA19DCC02100B14BE0 /* AddressSanitizerRuntime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CF02AE519DCBF8400B14BE0 /* AddressSanitizerRuntime.cpp */; }; + 8CF02AEF19DD16B100B14BE0 /* InstrumentationRuntimeStopInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CF02AED19DD15CF00B14BE0 /* InstrumentationRuntimeStopInfo.cpp */; }; 94094C6B163B6F840083A547 /* ValueObjectCast.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94094C69163B6CD90083A547 /* ValueObjectCast.cpp */; }; 940B02F619DC96E700AD0F52 /* SBExecutionContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 940B02F519DC96E700AD0F52 /* SBExecutionContext.cpp */; }; 94145431175E63B500284436 /* lldb-versioning.h in Headers */ = {isa = PBXBuildFile; fileRef = 94145430175D7FDE00284436 /* lldb-versioning.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -1967,6 +1970,12 @@ 8CCB017F19BA4DD00009FD44 /* SBThreadCollection.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = SBThreadCollection.cpp; path = source/API/SBThreadCollection.cpp; sourceTree = ""; }; 8CCB018119BA4E210009FD44 /* SBThreadCollection.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SBThreadCollection.h; path = include/lldb/API/SBThreadCollection.h; sourceTree = ""; }; 8CCB018419BA54930009FD44 /* SBThreadCollection.i */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBThreadCollection.i; sourceTree = ""; }; + 8CF02ADF19DCBF3B00B14BE0 /* InstrumentationRuntime.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = InstrumentationRuntime.cpp; path = source/Target/InstrumentationRuntime.cpp; sourceTree = ""; }; + 8CF02AE019DCBF3B00B14BE0 /* InstrumentationRuntime.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = InstrumentationRuntime.h; path = include/lldb/Target/InstrumentationRuntime.h; sourceTree = ""; }; + 8CF02AE519DCBF8400B14BE0 /* AddressSanitizerRuntime.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AddressSanitizerRuntime.cpp; sourceTree = ""; }; + 8CF02AE619DCBF8400B14BE0 /* AddressSanitizerRuntime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AddressSanitizerRuntime.h; sourceTree = ""; }; + 8CF02AED19DD15CF00B14BE0 /* InstrumentationRuntimeStopInfo.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = InstrumentationRuntimeStopInfo.cpp; path = source/Target/InstrumentationRuntimeStopInfo.cpp; sourceTree = ""; }; + 8CF02AEE19DD15CF00B14BE0 /* InstrumentationRuntimeStopInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = InstrumentationRuntimeStopInfo.h; path = include/lldb/Target/InstrumentationRuntimeStopInfo.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 = ""; }; @@ -2424,6 +2433,7 @@ 260C897110F57C5600BB2B04 /* Plugins */ = { isa = PBXGroup; children = ( + 8CF02ADD19DCBEC200B14BE0 /* InstrumentationRuntime */, 8C2D6A58197A1FB9006989C9 /* MemoryHistory */, 26DB3E051379E7AD0080DC73 /* ABI */, 260C897210F57C5600BB2B04 /* Disassembler */, @@ -3749,6 +3759,10 @@ 26BC7DEF10F1B80200F91463 /* Target */ = { isa = PBXGroup; children = ( + 8CF02AE019DCBF3B00B14BE0 /* InstrumentationRuntime.h */, + 8CF02ADF19DCBF3B00B14BE0 /* InstrumentationRuntime.cpp */, + 8CF02AEE19DD15CF00B14BE0 /* InstrumentationRuntimeStopInfo.h */, + 8CF02AED19DD15CF00B14BE0 /* InstrumentationRuntimeStopInfo.cpp */, 3FDFD6C3199C396E009756A7 /* FileAction.h */, 3FDFDDBC199C3A06009756A7 /* FileAction.cpp */, 23EDE3311926843600F6A132 /* NativeRegisterContext.h */, @@ -4265,6 +4279,23 @@ path = asan; sourceTree = ""; }; + 8CF02ADD19DCBEC200B14BE0 /* InstrumentationRuntime */ = { + isa = PBXGroup; + children = ( + 8CF02ADE19DCBEE600B14BE0 /* AddressSanitizer */, + ); + path = InstrumentationRuntime; + sourceTree = ""; + }; + 8CF02ADE19DCBEE600B14BE0 /* AddressSanitizer */ = { + isa = PBXGroup; + children = ( + 8CF02AE519DCBF8400B14BE0 /* AddressSanitizerRuntime.cpp */, + 8CF02AE619DCBF8400B14BE0 /* AddressSanitizerRuntime.h */, + ); + path = AddressSanitizer; + sourceTree = ""; + }; 9457596415349416005A9070 /* POSIX */ = { isa = PBXGroup; children = ( @@ -4966,6 +4997,7 @@ 2689000713353DB600698AC0 /* BreakpointSite.cpp in Sources */, 2689000913353DB600698AC0 /* BreakpointSiteList.cpp in Sources */, 26474CC918D0CB5B0073DEBA /* RegisterContextMemory.cpp in Sources */, + 8CF02AEA19DCC02100B14BE0 /* AddressSanitizerRuntime.cpp in Sources */, 26474CB218D0CB180073DEBA /* RegisterContextLinux_i386.cpp in Sources */, 26474CCB18D0CB5B0073DEBA /* RegisterContextPOSIX_mips64.cpp in Sources */, 2689000B13353DB600698AC0 /* Stoppoint.cpp in Sources */, @@ -5003,6 +5035,7 @@ 2689002813353DDE00698AC0 /* CommandObjectThread.cpp in Sources */, 2689002913353DDE00698AC0 /* CommandObjectVersion.cpp in Sources */, 2689002A13353E0400698AC0 /* Address.cpp in Sources */, + 8CF02AE919DCC01900B14BE0 /* InstrumentationRuntime.cpp in Sources */, 2689002B13353E0400698AC0 /* AddressRange.cpp in Sources */, 236124A41986B4E2004EFC37 /* IOObject.cpp in Sources */, 2689002C13353E0400698AC0 /* AddressResolver.cpp in Sources */, @@ -5226,6 +5259,7 @@ 268900FA13353E6F00698AC0 /* ThreadList.cpp in Sources */, 268900FB13353E6F00698AC0 /* ThreadPlan.cpp in Sources */, 232CB619191E00CD00EF39FC /* NativeProcessProtocol.cpp in Sources */, + 8CF02AEF19DD16B100B14BE0 /* InstrumentationRuntimeStopInfo.cpp in Sources */, 268900FC13353E6F00698AC0 /* ThreadPlanBase.cpp in Sources */, 268900FD13353E6F00698AC0 /* ThreadPlanCallFunction.cpp in Sources */, 268900FE13353E6F00698AC0 /* ThreadPlanCallUserExpression.cpp in Sources */, Index: scripts/Python/interface/SBThread.i =================================================================== --- scripts/Python/interface/SBThread.i +++ scripts/Python/interface/SBThread.i @@ -110,6 +110,14 @@ ") GetStopReasonDataAtIndex; uint64_t GetStopReasonDataAtIndex(uint32_t idx); + + %feature("autodoc", " + Collects a thread's stop reason extended information dictionary and prints it + into the SBStream in a JSON format. The format of this JSON dictionary depends + on the stop reason and is currently used only for instrumentation plugins. + ") GetStopReasonExtendedInfoAsJSON; + bool + GetStopReasonExtendedInfoAsJSON (lldb::SBStream &stream); %feature("autodoc", " Pass only an (int)length and expect to get a Python string describing the Index: source/API/SBThread.cpp =================================================================== --- source/API/SBThread.cpp +++ source/API/SBThread.cpp @@ -195,6 +195,7 @@ case eStopReasonExec: case eStopReasonPlanComplete: case eStopReasonThreadExiting: + case eStopReasonInstrumentation: // There is no data for these stop reasons. return 0; @@ -255,6 +256,7 @@ case eStopReasonExec: case eStopReasonPlanComplete: case eStopReasonThreadExiting: + case eStopReasonInstrumentation: // There is no data for these stop reasons. return 0; @@ -306,6 +308,26 @@ return 0; } +bool +SBThread::GetStopReasonExtendedInfoAsJSON (lldb::SBStream &stream) +{ + Stream &strm = stream.ref(); + + ExecutionContext exe_ctx (m_opaque_sp.get()); + if (! exe_ctx.HasThreadScope()) + return false; + + + StopInfoSP stop_info = exe_ctx.GetThreadPtr()->GetStopInfo(); + StructuredData::ObjectSP info = stop_info->GetExtendedInfo(); + if (! info) + return false; + + info->Dump(strm); + + return true; +} + size_t SBThread::GetStopDescription (char *dst, size_t dst_len) { Index: source/Commands/CommandObjectThread.cpp =================================================================== --- source/Commands/CommandObjectThread.cpp +++ source/Commands/CommandObjectThread.cpp @@ -1431,7 +1431,8 @@ void OptionParsingStarting () { - m_json = false; + m_json_thread = false; + m_json_stopinfo = false; } virtual @@ -1448,10 +1449,14 @@ switch (short_option) { case 'j': - m_json = true; + m_json_thread = true; + break; + + case 's': + m_json_stopinfo = true; break; - default: + default: return Error("invalid short option character '%c'", short_option); } @@ -1464,7 +1469,8 @@ return g_option_table; } - bool m_json; + bool m_json_thread; + bool m_json_stopinfo; static OptionDefinition g_option_table[]; }; @@ -1486,7 +1492,7 @@ HandleOneThread (Thread &thread, CommandReturnObject &result) { Stream &strm = result.GetOutputStream(); - if (!thread.GetDescription (strm, eDescriptionLevelFull, m_options.m_json)) + if (!thread.GetDescription (strm, eDescriptionLevelFull, m_options.m_json_thread, m_options.m_json_stopinfo)) { result.AppendErrorWithFormat ("error displaying info for thread: \"%d\"\n", thread.GetIndexID()); result.SetStatus (eReturnStatusFailed); @@ -1503,6 +1509,7 @@ CommandObjectThreadInfo::CommandOptions::g_option_table[] = { { LLDB_OPT_SET_ALL, false, "json",'j', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Display the thread info in JSON format."}, + { LLDB_OPT_SET_ALL, false, "stop-info",'s', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Display the extended stop info in JSON format."}, { 0, false, NULL, 0, 0, NULL, NULL, 0, eArgTypeNone, NULL } }; Index: source/Core/Debugger.cpp =================================================================== --- source/Core/Debugger.cpp +++ source/Core/Debugger.cpp @@ -3087,6 +3087,7 @@ case eStopReasonException: case eStopReasonExec: case eStopReasonThreadExiting: + case eStopReasonInstrumentation: if (!other_thread) other_thread = thread; break; Index: source/Core/PluginManager.cpp =================================================================== --- source/Core/PluginManager.cpp +++ source/Core/PluginManager.cpp @@ -2172,6 +2172,125 @@ return NULL; } +#pragma mark InstrumentationRuntime + +struct InstrumentationRuntimeInstance +{ + InstrumentationRuntimeInstance() : + name(), + description(), + create_callback(NULL) + { + } + + ConstString name; + std::string description; + InstrumentationRuntimeCreateInstance create_callback; + InstrumentationRuntimeGetType get_type_callback; +}; + +typedef std::vector InstrumentationRuntimeInstances; + +static Mutex & +GetInstrumentationRuntimeMutex () +{ + static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive); + return g_instances_mutex; +} + +static InstrumentationRuntimeInstances & +GetInstrumentationRuntimeInstances () +{ + static InstrumentationRuntimeInstances g_instances; + return g_instances; +} + +bool +PluginManager::RegisterPlugin +( + const ConstString &name, + const char *description, + InstrumentationRuntimeCreateInstance create_callback, + InstrumentationRuntimeGetType get_type_callback + ) +{ + if (create_callback) + { + InstrumentationRuntimeInstance instance; + assert ((bool)name); + instance.name = name; + if (description && description[0]) + instance.description = description; + instance.create_callback = create_callback; + instance.get_type_callback = get_type_callback; + Mutex::Locker locker (GetInstrumentationRuntimeMutex ()); + GetInstrumentationRuntimeInstances ().push_back (instance); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (InstrumentationRuntimeCreateInstance create_callback) +{ + if (create_callback) + { + Mutex::Locker locker (GetInstrumentationRuntimeMutex ()); + InstrumentationRuntimeInstances &instances = GetInstrumentationRuntimeInstances (); + + InstrumentationRuntimeInstances::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; +} + +InstrumentationRuntimeGetType +PluginManager::GetInstrumentationRuntimeGetTypeCallbackAtIndex (uint32_t idx) +{ + Mutex::Locker locker (GetInstrumentationRuntimeMutex ()); + InstrumentationRuntimeInstances &instances = GetInstrumentationRuntimeInstances (); + if (idx < instances.size()) + return instances[idx].get_type_callback; + return NULL; +} + +InstrumentationRuntimeCreateInstance +PluginManager::GetInstrumentationRuntimeCreateCallbackAtIndex (uint32_t idx) +{ + Mutex::Locker locker (GetInstrumentationRuntimeMutex ()); + InstrumentationRuntimeInstances &instances = GetInstrumentationRuntimeInstances (); + if (idx < instances.size()) + return instances[idx].create_callback; + return NULL; +} + + +InstrumentationRuntimeCreateInstance +PluginManager::GetInstrumentationRuntimeCreateCallbackForPluginName (const ConstString &name) +{ + if (name) + { + Mutex::Locker locker (GetInstrumentationRuntimeMutex ()); + InstrumentationRuntimeInstances &instances = GetInstrumentationRuntimeInstances (); + + InstrumentationRuntimeInstances::iterator pos, end = instances.end(); + for (pos = instances.begin(); pos != end; ++ pos) + { + if (name == pos->name) + return pos->create_callback; + } + } + return NULL; +} + +#pragma mark PluginManager + void PluginManager::DebuggerInitialize (Debugger &debugger) { Index: source/Plugins/InstrumentationRuntime/AddressSanitizer/AddressSanitizerRuntime.h =================================================================== --- source/Plugins/InstrumentationRuntime/AddressSanitizer/AddressSanitizerRuntime.h +++ source/Plugins/InstrumentationRuntime/AddressSanitizer/AddressSanitizerRuntime.h @@ -0,0 +1,86 @@ +//===-- AddressSanitizerRuntime.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_AddressSanitizerRuntime_h_ +#define liblldb_AddressSanitizerRuntime_h_ + +#include "lldb/lldb-private.h" +#include "lldb/Target/ABI.h" +#include "lldb/Target/InstrumentationRuntime.h" +#include "lldb/Target/Process.h" +#include "lldb/Core/StructuredData.h" + +namespace lldb_private { + +class AddressSanitizerRuntime : public lldb_private::InstrumentationRuntime +{ +public: + + static lldb::InstrumentationRuntimeSP + CreateInstance (const lldb::ProcessSP &process_sp); + + static void + Initialize(); + + static void + Terminate(); + + static lldb_private::ConstString + GetPluginNameStatic(); + + static lldb::InstrumentationRuntimeType + GetTypeStatic(); + + virtual + ~AddressSanitizerRuntime(); + + virtual lldb_private::ConstString + GetPluginName() { return GetPluginNameStatic(); } + + virtual lldb::InstrumentationRuntimeType + GetType() { return GetTypeStatic(); } + + virtual uint32_t + GetPluginVersion() { return 1; } + + virtual void + ModulesDidLoad(lldb_private::ModuleList &module_list); + + virtual bool + IsActive(); + +private: + + AddressSanitizerRuntime(const lldb::ProcessSP &process_sp); + + void + Activate(); + + void + Deactivate(); + + static bool + NotifyBreakpointHit(void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id, lldb::user_id_t break_loc_id); + + StructuredData::ObjectSP + RetrieveReportData(); + + std::string + FormatDescription(StructuredData::ObjectSP report); + + bool m_is_active; + lldb::ModuleSP m_runtime_module; + lldb::ProcessSP m_process; + lldb::user_id_t m_breakpoint_id; + +}; + +} // namespace lldb_private + +#endif // liblldb_InstrumentationRuntime_h_ Index: source/Plugins/InstrumentationRuntime/AddressSanitizer/AddressSanitizerRuntime.cpp =================================================================== --- source/Plugins/InstrumentationRuntime/AddressSanitizer/AddressSanitizerRuntime.cpp +++ source/Plugins/InstrumentationRuntime/AddressSanitizer/AddressSanitizerRuntime.cpp @@ -0,0 +1,300 @@ +//===-- AddressSanitizerRuntime.cpp -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "AddressSanitizerRuntime.h" + +#include "lldb/Core/PluginInterface.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Core/Module.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Target/InstrumentationRuntimeStopInfo.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamFile.h" + +using namespace lldb; +using namespace lldb_private; + +lldb::InstrumentationRuntimeSP +AddressSanitizerRuntime::CreateInstance (const lldb::ProcessSP &process_sp) +{ + return InstrumentationRuntimeSP(new AddressSanitizerRuntime(process_sp)); +} + +void +AddressSanitizerRuntime::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + "AddressSanitizer instrumentation runtime plugin.", + CreateInstance, + GetTypeStatic); +} + +void +AddressSanitizerRuntime::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + +lldb_private::ConstString +AddressSanitizerRuntime::GetPluginNameStatic() +{ + return ConstString("AddressSanitizer"); +} + +lldb::InstrumentationRuntimeType +AddressSanitizerRuntime::GetTypeStatic() +{ + return eInstrumentationRuntimeTypeAddressSanitizer; +} + +AddressSanitizerRuntime::AddressSanitizerRuntime(const ProcessSP &process_sp) : + m_is_active(false), + m_runtime_module(), + m_process(process_sp), + m_breakpoint_id(0) +{ +} + +AddressSanitizerRuntime::~AddressSanitizerRuntime() +{ + Deactivate(); +} + +bool ModuleContainsASanRuntime(Module * module) +{ + SymbolContextList sc_list; + const bool include_symbols = true; + const bool append = true; + const bool include_inlines = true; + + size_t num_matches = module->FindFunctions(ConstString("__asan_get_alloc_stack"), NULL, eFunctionNameTypeAuto, include_symbols, include_inlines, append, sc_list); + + return num_matches > 0; +} + +void +AddressSanitizerRuntime::ModulesDidLoad(lldb_private::ModuleList &module_list) +{ + if (IsActive()) + return; + + if (m_runtime_module) { + Activate(); + return; + } + + Mutex::Locker modules_locker(module_list.GetMutex()); + const size_t num_modules = module_list.GetSize(); + for (size_t i = 0; i < num_modules; ++i) + { + Module *module_pointer = module_list.GetModulePointerAtIndexUnlocked(i); + const FileSpec & file_spec = module_pointer->GetFileSpec(); + if (! file_spec) + continue; + + static RegularExpression g_asan_runtime_regex("libclang_rt.asan_(.*)_dynamic\\.dylib"); + if (g_asan_runtime_regex.Execute (file_spec.GetFilename().GetCString()) || module_pointer->IsExecutable()) + { + if (ModuleContainsASanRuntime(module_pointer)) + { + m_runtime_module = module_pointer->shared_from_this(); + Activate(); + return; + } + } + } +} + +bool +AddressSanitizerRuntime::IsActive() +{ + return m_is_active; +} + +#define RETRIEVE_REPORT_DATA_FUNCTION_TIMEOUT_USEC 2*1000*1000 + +const char * +address_sanitizer_retrieve_report_data_command = R"( + struct { + int present; + void *pc, *bp, *sp, *address; + int access_type; + size_t access_size; + const char *description; + } t; + + t.present = ((int (*) ())__asan_report_present)(); + t.pc = ((void * (*) ())__asan_get_report_pc)(); + /* commented out because rdar://problem/18533301 + t.bp = ((void * (*) ())__asan_get_report_bp)(); + t.sp = ((void * (*) ())__asan_get_report_sp)(); + */ + t.address = ((void * (*) ())__asan_get_report_address)(); + t.description = ((const char * (*) ())__asan_get_report_description)(); + t.access_type = ((int (*) ())__asan_get_report_access_type)(); + t.access_size = ((size_t (*) ())__asan_get_report_access_size)(); + + t; +)"; + +StructuredData::ObjectSP +AddressSanitizerRuntime::RetrieveReportData() +{ + ThreadSP thread_sp = m_process->GetThreadList().GetSelectedThread(); + StackFrameSP frame_sp = thread_sp->GetSelectedFrame(); + + if (!frame_sp) + return StructuredData::ObjectSP(); + + EvaluateExpressionOptions options; + options.SetUnwindOnError(true); + options.SetTryAllThreads(true); + options.SetStopOthers(true); + options.SetIgnoreBreakpoints(true); + options.SetTimeoutUsec(RETRIEVE_REPORT_DATA_FUNCTION_TIMEOUT_USEC); + + ValueObjectSP return_value_sp; + if (m_process->GetTarget().EvaluateExpression(address_sanitizer_retrieve_report_data_command, frame_sp.get(), return_value_sp, options) != eExpressionCompleted) + return StructuredData::ObjectSP(); + + int present = return_value_sp->GetValueForExpressionPath(".present")->GetValueAsUnsigned(0); + if (present != 1) + return StructuredData::ObjectSP(); + + addr_t pc = return_value_sp->GetValueForExpressionPath(".pc")->GetValueAsUnsigned(0); + addr_t bp = return_value_sp->GetValueForExpressionPath(".bp")->GetValueAsUnsigned(0); + addr_t sp = return_value_sp->GetValueForExpressionPath(".sp")->GetValueAsUnsigned(0); + addr_t address = return_value_sp->GetValueForExpressionPath(".address")->GetValueAsUnsigned(0); + addr_t access_type = return_value_sp->GetValueForExpressionPath(".access_type")->GetValueAsUnsigned(0); + addr_t access_size = return_value_sp->GetValueForExpressionPath(".access_size")->GetValueAsUnsigned(0); + addr_t description_ptr = return_value_sp->GetValueForExpressionPath(".description")->GetValueAsUnsigned(0); + std::string description; + Error error; + m_process->ReadCStringFromMemory(description_ptr, description, error); + + StructuredData::Dictionary *dict = new StructuredData::Dictionary(); + dict->AddStringItem("instrumentation_class", "AddressSanitizer"); + dict->AddStringItem("stop_type", "fatal_error"); + dict->AddIntegerItem("pc", pc); + dict->AddIntegerItem("bp", bp); + dict->AddIntegerItem("sp", sp); + dict->AddIntegerItem("address", address); + dict->AddIntegerItem("access_type", access_type); + dict->AddIntegerItem("access_size", access_size); + dict->AddStringItem("description", description); + + return StructuredData::ObjectSP(dict); +} + +std::string +AddressSanitizerRuntime::FormatDescription(StructuredData::ObjectSP report) +{ + std::string description = report->GetAsDictionary()->GetValueForKey("description")->GetAsString()->GetValue(); + if (description == "heap-use-after-free") { + return "Use of deallocated memory detected"; + } else if (description == "heap-buffer-overflow") { + return "Heap buffer overflow detected"; + } else if (description == "stack-buffer-underflow") { + return "Stack buffer underflow detected"; + } else if (description == "initialization-order-fiasco") { + return "Initialization order problem detected"; + } else if (description == "stack-buffer-overflow") { + return "Stack buffer overflow detected"; + } else if (description == "stack-use-after-return") { + return "Use of returned stack memory detected"; + } else if (description == "use-after-poison") { + return "Use of poisoned memory detected"; + } else if (description == "container-overflow") { + return "Container overflow detected"; + } else if (description == "stack-use-after-scope") { + return "Use of out-of-scope stack memory detected"; + } else if (description == "global-buffer-overflow") { + return "Global buffer overflow detected"; + } else if (description == "unknown-crash") { + return "Invalid memory access detected"; + } + + // for unknown report codes just show the code + return description; +} + +bool +AddressSanitizerRuntime::NotifyBreakpointHit(void *baton, StoppointCallbackContext *context, user_id_t break_id, user_id_t break_loc_id) +{ + assert (baton && "null baton"); + if (!baton) + return false; + + AddressSanitizerRuntime *const instance = static_cast(baton); + + StructuredData::ObjectSP report = instance->RetrieveReportData(); + std::string description; + if (report) { + description = instance->FormatDescription(report); + } + ThreadSP thread = context->exe_ctx_ref.GetThreadSP(); + thread->SetStopInfo(InstrumentationRuntimeStopInfo::CreateStopReasonWithInstrumentationData(*thread, description.c_str(), report)); + + instance->m_runtime_module->ReportWarning("AddressSanitizer report breakpoint hit. Use 'thread info -s' to get extended information about the report.\n"); + + // Return true to stop the target, false to just let the target run. + return true; +} + +void +AddressSanitizerRuntime::Activate() +{ + if (m_is_active) + return; + + ConstString symbol_name ("__asan::AsanDie()"); + const Symbol *symbol = m_runtime_module->FindFirstSymbolWithNameAndType (symbol_name, eSymbolTypeCode); + + if (symbol == NULL) + return; + + if (!symbol->GetAddress().IsValid()) + return; + + Target &target = m_process->GetTarget(); + addr_t symbol_address = symbol->GetAddress().GetOpcodeLoadAddress(&target); + + if (symbol_address == LLDB_INVALID_ADDRESS) + return; + + bool internal = true; + bool hardware = false; + Breakpoint *breakpoint = m_process->GetTarget().CreateBreakpoint(symbol_address, internal, hardware).get(); + breakpoint->SetCallback (AddressSanitizerRuntime::NotifyBreakpointHit, this, true); + breakpoint->SetBreakpointKind ("address-sanitizer-report"); + m_breakpoint_id = breakpoint->GetID(); + + m_runtime_module->ReportWarning("AddressSanitizer debugger support is active. Memory error breakpoint has been installed and you can now use the 'memory history' command.\n"); + + m_is_active = true; +} + +void +AddressSanitizerRuntime::Deactivate() +{ + if (m_breakpoint_id != LLDB_INVALID_BREAK_ID) + { + m_process->GetTarget().RemoveBreakpointByID(m_breakpoint_id); + m_breakpoint_id = LLDB_INVALID_BREAK_ID; + } + m_is_active = false; +} Index: source/Plugins/InstrumentationRuntime/AddressSanitizer/CMakeLists.txt =================================================================== --- source/Plugins/InstrumentationRuntime/AddressSanitizer/CMakeLists.txt +++ source/Plugins/InstrumentationRuntime/AddressSanitizer/CMakeLists.txt @@ -0,0 +1,5 @@ +set(LLVM_NO_RTTI 1) + +add_lldb_library(lldbPluginInstrumentationRuntimeAddressSanitizer + AddressSanitizerRuntime.cpp + ) Index: source/Plugins/InstrumentationRuntime/AddressSanitizer/Makefile =================================================================== --- source/Plugins/InstrumentationRuntime/AddressSanitizer/Makefile +++ source/Plugins/InstrumentationRuntime/AddressSanitizer/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/InstrumentationRuntime/AddressSanitizer 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 := lldbPluginInstrumentationRuntimeAddressSanitizer +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile Index: source/Plugins/InstrumentationRuntime/CMakeLists.txt =================================================================== --- source/Plugins/InstrumentationRuntime/CMakeLists.txt +++ source/Plugins/InstrumentationRuntime/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(AddressSanitizer) Index: source/Target/CMakeLists.txt =================================================================== --- source/Target/CMakeLists.txt +++ source/Target/CMakeLists.txt @@ -9,6 +9,8 @@ FileAction.cpp JITLoader.cpp JITLoaderList.cpp + InstrumentationRuntime.cpp + InstrumentationRuntimeStopInfo.cpp LanguageRuntime.cpp Memory.cpp MemoryHistory.cpp Index: source/Target/InstrumentationRuntime.cpp =================================================================== --- source/Target/InstrumentationRuntime.cpp +++ source/Target/InstrumentationRuntime.cpp @@ -0,0 +1,48 @@ +//===-- InstrumentationRuntime.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/lldb-private.h" +#include "lldb/Target/Process.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Target/InstrumentationRuntime.h" + +using namespace lldb; +using namespace lldb_private; + +void +InstrumentationRuntime::ModulesDidLoad(lldb_private::ModuleList &module_list, lldb_private::Process *process, InstrumentationRuntimeCollection &runtimes) +{ + InstrumentationRuntimeCreateInstance create_callback = NULL; + InstrumentationRuntimeGetType get_type_callback; + for (uint32_t idx = 0; ; ++idx) + { + create_callback = PluginManager::GetInstrumentationRuntimeCreateCallbackAtIndex(idx); + if (create_callback == NULL) + break; + get_type_callback = PluginManager::GetInstrumentationRuntimeGetTypeCallbackAtIndex(idx); + InstrumentationRuntimeType type = get_type_callback(); + + InstrumentationRuntimeCollection::iterator pos; + pos = runtimes.find (type); + if (pos == runtimes.end()) { + runtimes[type] = create_callback(process->shared_from_this()); + } + } +} + +void +InstrumentationRuntime::ModulesDidLoad(lldb_private::ModuleList &module_list) +{ +} + +bool +InstrumentationRuntime::IsActive() +{ + return false; +} Index: source/Target/InstrumentationRuntimeStopInfo.cpp =================================================================== --- source/Target/InstrumentationRuntimeStopInfo.cpp +++ source/Target/InstrumentationRuntimeStopInfo.cpp @@ -0,0 +1,36 @@ +//===-- InstrumentationRuntimeStopInfo.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/InstrumentationRuntimeStopInfo.h" + +#include "lldb/lldb-private.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/InstrumentationRuntime.h" + +using namespace lldb; +using namespace lldb_private; + +InstrumentationRuntimeStopInfo::InstrumentationRuntimeStopInfo(Thread &thread, std::string description, StructuredData::ObjectSP additional_data) : + StopInfo(thread, 0) +{ + m_extended_info = additional_data; + m_description = description; +} + +const char * +InstrumentationRuntimeStopInfo::GetDescription () +{ + return m_description.c_str(); +} + +StopInfoSP +InstrumentationRuntimeStopInfo::CreateStopReasonWithInstrumentationData (Thread &thread, std::string description, StructuredData::ObjectSP additionalData) +{ + return StopInfoSP(new InstrumentationRuntimeStopInfo(thread, description, additionalData)); +} Index: source/Target/Process.cpp =================================================================== --- source/Target/Process.cpp +++ source/Target/Process.cpp @@ -48,6 +48,7 @@ #include "lldb/Target/Thread.h" #include "lldb/Target/ThreadPlan.h" #include "lldb/Target/ThreadPlanBase.h" +#include "lldb/Target/InstrumentationRuntime.h" #include "Plugins/Process/Utility/InferiorCallPOSIX.h" using namespace lldb; @@ -843,6 +844,7 @@ m_memory_cache.Clear(); m_allocated_memory_cache.Clear(); m_language_runtimes.clear(); + m_instrumentation_runtimes.clear(); m_next_event_action_ap.reset(); //#ifdef LLDB_CONFIGURATION_DEBUG // StreamFile s(stdout, false); @@ -5979,6 +5981,7 @@ m_image_tokens.clear(); m_allocated_memory_cache.Clear(); m_language_runtimes.clear(); + m_instrumentation_runtimes.clear(); m_thread_list.DiscardThreadPlans(); m_memory_cache.Clear(true); DoDidExec(); @@ -6029,13 +6032,24 @@ void Process::ModulesDidLoad (ModuleList &module_list) { - SystemRuntime *sys_runtime = GetSystemRuntime(); - if (sys_runtime) - { - sys_runtime->ModulesDidLoad (module_list); - } + SystemRuntime *sys_runtime = GetSystemRuntime(); + if (sys_runtime) + { + sys_runtime->ModulesDidLoad (module_list); + } + + GetJITLoaders().ModulesDidLoad (module_list); + + // Give runtimes a chance to be created. + InstrumentationRuntime::ModulesDidLoad(module_list, this, m_instrumentation_runtimes); + + // Tell runtimes about new modules. + for (auto pos = m_instrumentation_runtimes.begin(); pos != m_instrumentation_runtimes.end(); ++pos) + { + InstrumentationRuntimeSP runtime = pos->second; + runtime->ModulesDidLoad(module_list); + } - GetJITLoaders().ModulesDidLoad (module_list); } ThreadCollectionSP Index: source/Target/StopInfo.cpp =================================================================== --- source/Target/StopInfo.cpp +++ source/Target/StopInfo.cpp @@ -40,7 +40,8 @@ m_resume_id (thread.GetProcess()->GetResumeID()), m_value (value), m_override_should_notify (eLazyBoolCalculate), - m_override_should_stop (eLazyBoolCalculate) + m_override_should_stop (eLazyBoolCalculate), + m_extended_info() { } Index: source/Target/Thread.cpp =================================================================== --- source/Target/Thread.cpp +++ source/Target/Thread.cpp @@ -2086,6 +2086,7 @@ case eStopReasonExec: return "exec"; case eStopReasonPlanComplete: return "plan complete"; case eStopReasonThreadExiting: return "thread exiting"; + case eStopReasonInstrumentation: return "instrumentation break"; } @@ -2165,17 +2166,28 @@ } bool -Thread::GetDescription (Stream &strm, lldb::DescriptionLevel level, bool print_json) +Thread::GetDescription (Stream &strm, lldb::DescriptionLevel level, bool print_json_thread, bool print_json_stopinfo) { DumpUsingSettingsFormat (strm, 0); strm.Printf("\n"); StructuredData::ObjectSP thread_info = GetExtendedInfo(); - - if (thread_info && print_json) + StructuredData::ObjectSP stop_info = m_stop_info_sp->GetExtendedInfo(); + + if (print_json_thread || print_json_stopinfo) { - thread_info->Dump (strm); - strm.Printf("\n"); + if (thread_info && print_json_thread) + { + thread_info->Dump (strm); + strm.Printf("\n"); + } + + if (stop_info && print_json_stopinfo) + { + stop_info->Dump (strm); + strm.Printf("\n"); + } + return true; } Index: source/lldb.cpp =================================================================== --- source/lldb.cpp +++ source/lldb.cpp @@ -93,6 +93,7 @@ #include "Plugins/Process/gdb-remote/ProcessGDBRemote.h" #include "Plugins/DynamicLoader/Static/DynamicLoaderStatic.h" #include "Plugins/MemoryHistory/asan/MemoryHistoryASan.h" +#include "Plugins/InstrumentationRuntime/AddressSanitizer/AddressSanitizerRuntime.h" using namespace lldb; using namespace lldb_private; @@ -156,6 +157,7 @@ JITLoaderGDB::Initialize(); ProcessElfCore::Initialize(); MemoryHistoryASan::Initialize(); + AddressSanitizerRuntime::Initialize(); #if defined (__APPLE__) //---------------------------------------------------------------------- @@ -247,6 +249,7 @@ JITLoaderGDB::Terminate(); ProcessElfCore::Terminate(); MemoryHistoryASan::Terminate(); + AddressSanitizerRuntime::Terminate(); #if defined (__APPLE__) DynamicLoaderMacOSXDYLD::Terminate(); Index: test/functionalities/asan/TestAsan.py =================================================================== --- test/functionalities/asan/TestAsan.py +++ test/functionalities/asan/TestAsan.py @@ -1,117 +0,0 @@ -""" -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") - @skipIfRemote - @dsym_test - def test_with_dsym (self): - compiler = self.findBuiltClang () - self.buildDsym (None, compiler) - self.asan_tests () - - @expectedFailureFreeBSD("llvm.org/pr21136") # runtimes not yet available by default - @skipIfRemote - @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]) - - # do the same using SB API - process = self.dbg.GetSelectedTarget().process - val = process.GetSelectedThread().GetSelectedFrame().EvaluateExpression("pointer") - addr = val.GetValueAsUnsigned() - threads = process.GetHistoryThreads(addr); - self.assertEqual(threads.GetSize(), 2) - - history_thread = threads.GetThreadAtIndex(0) - self.assertTrue(history_thread.num_frames >= 2) - self.assertEqual(history_thread.frames[1].GetLineEntry().GetFileSpec().GetFilename(), "main.c") - self.assertEqual(history_thread.frames[1].GetLineEntry().GetLine(), self.line_malloc) - - history_thread = threads.GetThreadAtIndex(1) - self.assertTrue(history_thread.num_frames >= 2) - self.assertEqual(history_thread.frames[1].GetLineEntry().GetFileSpec().GetFilename(), "main.c") - self.assertEqual(history_thread.frames[1].GetLineEntry().GetLine(), self.line_free) - - # let's free the container (SBThreadCollection) and see if the SBThreads still live - threads = None - self.assertTrue(history_thread.num_frames >= 2) - self.assertEqual(history_thread.frames[1].GetLineEntry().GetFileSpec().GetFilename(), "main.c") - self.assertEqual(history_thread.frames[1].GetLineEntry().GetLine(), self.line_free) - - # now let's break when an ASan report occurs and try the API then - 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/TestReportData.py =================================================================== --- test/functionalities/asan/TestReportData.py +++ test/functionalities/asan/TestReportData.py @@ -0,0 +1,92 @@ +""" +Test the AddressSanitizer runtime support for report breakpoint and data extraction. +""" + +import os, time +import unittest2 +import lldb +from lldbtest import * +import lldbutil +import json + +class AsanTestReportDataCase(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") + @skipIfRemote + @dsym_test + def test_with_dsym (self): + compiler = self.findBuiltClang () + self.buildDsym (None, compiler) + self.asan_tests () + + @skipIfRemote + @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') + self.line_crash = line_number('main.c', '// BOOM 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("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']) + + # no extended info when we have no ASan report + thread = self.dbg.GetSelectedTarget().process.GetSelectedThread() + s = lldb.SBStream() + self.assertFalse(thread.GetStopReasonExtendedInfoAsJSON(s)) + + self.runCmd("continue") + + self.expect("thread list", "Process should be stopped due to ASan report", + substrs = ['stopped', 'stop reason = Use of deallocated memory detected']) + + self.assertEqual(self.dbg.GetSelectedTarget().process.GetSelectedThread().GetStopReason(), lldb.eStopReasonInstrumentation) + + self.expect("bt", "The backtrace should show the crashing line", + substrs = ['main.c:%d' % self.line_crash]) + + self.expect("thread info -s", "The extended stop info should contain the ASan provided fields", + substrs = ["access_size", "access_type", "address", "pc", "description", "heap-use-after-free"]) + + output_lines = self.res.GetOutput().split('\n') + json_line = output_lines[2] + data = json.loads(json_line) + self.assertEqual(data["description"], "heap-use-after-free") + self.assertEqual(data["instrumentation_class"], "AddressSanitizer") + self.assertEqual(data["stop_type"], "fatal_error") + + # now let's try the SB API + process = self.dbg.GetSelectedTarget().process + thread = process.GetSelectedThread() + + s = lldb.SBStream() + self.assertTrue(thread.GetStopReasonExtendedInfoAsJSON(s)) + s = s.GetData() + data2 = json.loads(s) + self.assertEqual(data, data2) + +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 @@ -28,7 +28,7 @@ printf("Hello world!\n"); // break line - pointer[0] = 'A'; // BOOM + pointer[0] = 'A'; // BOOM line return 0; }