diff options
26 files changed, 1299 insertions, 116 deletions
diff --git a/lldb/include/lldb/API/SBValue.h b/lldb/include/lldb/API/SBValue.h index 5775fa2d93d..6170bdf4780 100644 --- a/lldb/include/lldb/API/SBValue.h +++ b/lldb/include/lldb/API/SBValue.h @@ -14,6 +14,10 @@ #include <stdio.h> +#ifndef SWIG +class lldb_private::SyntheticScriptProvider; +#endif + namespace lldb { class SBValue @@ -205,7 +209,9 @@ protected: friend class SBFrame; #ifndef SWIG - + // need this to decapsulate the SP from here + friend class lldb_private::SyntheticScriptProvider; + // Mimic shared pointer... lldb_private::ValueObject * get() const; diff --git a/lldb/include/lldb/Core/Debugger.h b/lldb/include/lldb/Core/Debugger.h index 7e374cbb2c8..3eb5b0de197 100644 --- a/lldb/include/lldb/Core/Debugger.h +++ b/lldb/include/lldb/Core/Debugger.h @@ -477,6 +477,11 @@ public: class Formatting { public: + + // use this call to force the FM to consider itself updated even when there is no apparent reason for that + static void + ForceUpdate(); + class ValueFormats { public: @@ -507,7 +512,7 @@ public: lldb::SummaryFormatSP& entry); static bool GetSyntheticFilter(ValueObject& vobj, - lldb::SyntheticFilterSP& entry); + lldb::SyntheticChildrenSP& entry); class NamedSummaryFormats { diff --git a/lldb/include/lldb/Core/FormatClasses.h b/lldb/include/lldb/Core/FormatClasses.h index c67372138a7..2bb8383fb41 100644 --- a/lldb/include/lldb/Core/FormatClasses.h +++ b/lldb/include/lldb/Core/FormatClasses.h @@ -12,6 +12,12 @@ // C Includes +#if defined (__APPLE__) +#include <Python/Python.h> +#else +#include <Python.h> +#endif + #include <stdint.h> #include <unistd.h> @@ -25,6 +31,7 @@ #include "lldb/lldb-public.h" #include "lldb/lldb-enumerations.h" +#include "lldb/API/SBValue.h" #include "lldb/Core/ValueObject.h" #include "lldb/Interpreter/ScriptInterpreterPython.h" #include "lldb/Symbol/SymbolContext.h" @@ -82,19 +89,90 @@ struct ValueFormat }; -struct SyntheticFilter +class SyntheticChildrenFrontEnd { +protected: + lldb::ValueObjectSP m_backend; +public: + + SyntheticChildrenFrontEnd(lldb::ValueObjectSP be) : + m_backend(be) + {} + + virtual + ~SyntheticChildrenFrontEnd() + { + } + + virtual uint32_t + CalculateNumChildren() = 0; + + virtual lldb::ValueObjectSP + GetChildAtIndex (uint32_t idx, bool can_create) = 0; + + virtual uint32_t + GetIndexOfChildWithName (const ConstString &name) = 0; + + typedef lldb::SharedPtr<SyntheticChildrenFrontEnd>::Type SharedPointer; + +}; + +class SyntheticChildren +{ +public: bool m_cascades; bool m_skip_pointers; bool m_skip_references; - std::vector<std::string> m_expression_paths; +public: + SyntheticChildren(bool casc = false, + bool skipptr = false, + bool skipref = false) : + m_cascades(casc), + m_skip_pointers(skipptr), + m_skip_references(skipref) + { + } + + virtual + ~SyntheticChildren() + { + } + bool + Cascades() const + { + return m_cascades; + } + bool + SkipsPointers() const + { + return m_skip_pointers; + } + bool + SkipsReferences() const + { + return m_skip_references; + } + + virtual std::string + GetDescription() = 0; + + virtual SyntheticChildrenFrontEnd::SharedPointer + GetFrontEnd(lldb::ValueObjectSP backend) = 0; + + typedef lldb::SharedPtr<SyntheticChildren>::Type SharedPointer; + typedef bool(*SyntheticChildrenCallback)(void*, const char*, const SyntheticChildren::SharedPointer&); + +}; + +class SyntheticFilter : public SyntheticChildren +{ + std::vector<std::string> m_expression_paths; +public: SyntheticFilter(bool casc = false, bool skipptr = false, bool skipref = false) : - m_cascades(casc), - m_skip_pointers(skipptr), - m_skip_references(skipref), + SyntheticChildren(casc, skipptr, skipref), m_expression_paths() { } @@ -129,10 +207,149 @@ struct SyntheticFilter std::string GetDescription(); - typedef lldb::SharedPtr<SyntheticFilter>::Type SharedPointer; - typedef bool(*SyntheticFilterCallback)(void*, const char*, const SyntheticFilter::SharedPointer&); + class FrontEnd : public SyntheticChildrenFrontEnd + { + private: + SyntheticFilter* filter; + public: + + FrontEnd(SyntheticFilter* flt, + lldb::ValueObjectSP be) : + SyntheticChildrenFrontEnd(be), + filter(flt) + {} + + virtual + ~FrontEnd() + { + } + + virtual uint32_t + CalculateNumChildren() + { + return filter->GetCount(); + } + + virtual lldb::ValueObjectSP + GetChildAtIndex (uint32_t idx, bool can_create) + { + if (idx >= filter->GetCount()) + return lldb::ValueObjectSP(); + return m_backend->GetSyntheticExpressionPathChild(filter->GetExpressionPathAtIndex(idx).c_str(), can_create); + } + + virtual uint32_t + GetIndexOfChildWithName (const ConstString &name) + { + const char* name_cstr = name.GetCString(); + for (int i = 0; i < filter->GetCount(); i++) + { + const char* expr_cstr = filter->GetExpressionPathAtIndex(i).c_str(); + if (::strcmp(name_cstr, expr_cstr)) + return i; + } + return UINT32_MAX; + } + + typedef lldb::SharedPtr<SyntheticChildrenFrontEnd>::Type SharedPointer; + + }; + + virtual SyntheticChildrenFrontEnd::SharedPointer + GetFrontEnd(lldb::ValueObjectSP backend) + { + return SyntheticChildrenFrontEnd::SharedPointer(new FrontEnd(this, backend)); + } + }; +class SyntheticScriptProvider : public SyntheticChildren +{ + std::string m_python_class; +public: + SyntheticScriptProvider(bool casc = false, + bool skipptr = false, + bool skipref = false, + std::string pclass = "") : + SyntheticChildren(casc, skipptr, skipref), + m_python_class(pclass) + { + } + + + std::string + GetPythonClassName() { return m_python_class; } + + std::string + GetDescription(); + + class FrontEnd : public SyntheticChildrenFrontEnd + { + private: + std::string m_python_class; + PyObject* m_wrapper; + ScriptInterpreter *m_interpreter; + public: + + FrontEnd(std::string pclass, + lldb::ValueObjectSP be); + + virtual + ~FrontEnd() + { + Py_XDECREF(m_wrapper); + } + + virtual uint32_t + CalculateNumChildren() + { + if (m_wrapper == NULL) + return 0; + return m_interpreter->CalculateNumChildren(m_wrapper); + } + + virtual lldb::ValueObjectSP + GetChildAtIndex (uint32_t idx, bool can_create) + { + if (m_wrapper == NULL) + return lldb::ValueObjectSP(); + + PyObject* py_return = (PyObject*)m_interpreter->GetChildAtIndex(m_wrapper, idx); + if (py_return == NULL || py_return == Py_None) + { + Py_XDECREF(py_return); + return lldb::ValueObjectSP(); + } + + lldb::SBValue *sb_ptr = m_interpreter->CastPyObjectToSBValue(py_return); + + if (py_return == NULL) + return lldb::ValueObjectSP(); + + return sb_ptr->m_opaque_sp; + } + + virtual uint32_t + GetIndexOfChildWithName (const ConstString &name) + { + if (m_wrapper == NULL) + return UINT32_MAX; + return m_interpreter->GetIndexOfChildWithName(m_wrapper, name.GetCString()); + } + + typedef lldb::SharedPtr<SyntheticChildrenFrontEnd>::Type SharedPointer; + + }; + + virtual SyntheticChildrenFrontEnd::SharedPointer + GetFrontEnd(lldb::ValueObjectSP backend) + { + return SyntheticChildrenFrontEnd::SharedPointer(new FrontEnd(m_python_class, backend)); + } + +}; + + struct SummaryFormat { bool m_cascades; diff --git a/lldb/include/lldb/Core/FormatManager.h b/lldb/include/lldb/Core/FormatManager.h index bad1702db40..b5d1a39560d 100644 --- a/lldb/include/lldb/Core/FormatManager.h +++ b/lldb/include/lldb/Core/FormatManager.h @@ -545,7 +545,7 @@ public: bool Get(ValueObject& vobj, - lldb::SyntheticFilterSP& entry, + lldb::SyntheticChildrenSP& entry, uint32_t* reason = NULL) { if (!IsEnabled()) @@ -791,7 +791,7 @@ public: bool Get(ValueObject& vobj, - lldb::SyntheticFilterSP& entry) + lldb::SyntheticChildrenSP& entry) { Mutex::Locker(m_map_mutex); @@ -803,7 +803,7 @@ public: for (begin = m_active_categories.begin(); begin != end; begin++) { FormatCategory::SharedPointer category = *begin; - lldb::SyntheticFilterSP current_format; + lldb::SyntheticChildrenSP current_format; if (!category->Get(vobj, current_format, &reason_why)) continue; if (reason_why == lldb::eFormatterDirectChoice) @@ -947,7 +947,7 @@ public: } bool Get(ValueObject& vobj, - lldb::SyntheticFilterSP& entry) + lldb::SyntheticChildrenSP& entry) { return m_categories_map.Get(vobj, entry); } diff --git a/lldb/include/lldb/Core/ValueObject.h b/lldb/include/lldb/Core/ValueObject.h index 8eb018e2113..2fb7b406938 100644 --- a/lldb/include/lldb/Core/ValueObject.h +++ b/lldb/include/lldb/Core/ValueObject.h @@ -739,13 +739,14 @@ protected: // as an independent ValueObjectConstResult, which isn't managed by us. ValueObject *m_deref_valobj; - lldb::Format m_format; - uint32_t m_last_format_mgr_revision; - lldb::SummaryFormatSP m_last_summary_format; - lldb::SummaryFormatSP m_forced_summary_format; - lldb::ValueFormatSP m_last_value_format; - lldb::SyntheticFilterSP m_last_synthetic_filter; - lldb::user_id_t m_user_id_of_forced_summary; + lldb::Format m_format; + uint32_t m_last_format_mgr_revision; + lldb::SummaryFormatSP m_last_summary_format; + lldb::SummaryFormatSP m_forced_summary_format; + lldb::ValueFormatSP m_last_value_format; + lldb::SyntheticChildrenSP m_last_synthetic_filter; + lldb::user_id_t m_user_id_of_forced_summary; + bool m_value_is_valid:1, m_value_did_change:1, m_children_count_valid:1, diff --git a/lldb/include/lldb/Core/ValueObjectSyntheticFilter.h b/lldb/include/lldb/Core/ValueObjectSyntheticFilter.h index 571fba37dc0..ccfbacf9ead 100644 --- a/lldb/include/lldb/Core/ValueObjectSyntheticFilter.h +++ b/lldb/include/lldb/Core/ValueObjectSyntheticFilter.h @@ -12,6 +12,7 @@ // C Includes // C++ Includes +#include <map> #include <ostream> #include <vector> // Other libraries and framework includes @@ -21,14 +22,17 @@ namespace lldb_private { //---------------------------------------------------------------------- -// A ValueObject that represents memory at a given address, viewed as some -// set lldb type. +// A ValueObject that obtains its children from some source other than +// real information +// This is currently used to implement children filtering, where only +// a subset of the real children are shown, but it can be used for any +// source of made-up children information //---------------------------------------------------------------------- -class ValueObjectSyntheticFilter : public ValueObject +class ValueObjectSynthetic : public ValueObject { public: virtual - ~ValueObjectSyntheticFilter(); + ~ValueObjectSynthetic(); virtual size_t GetByteSize(); @@ -105,16 +109,25 @@ protected: lldb::TypeSP m_type_sp; lldb::ValueObjectSP m_owning_valobj_sp; lldb::SyntheticValueType m_use_synthetic; - lldb::SyntheticFilterSP m_synth_filter; + lldb::SyntheticChildrenFrontEndSP m_synth_filter; + + typedef std::map<uint32_t, lldb::ValueObjectSP> ByIndexMap; + typedef std::map<const char*, uint32_t> NameToIndexMap; + + typedef ByIndexMap::iterator ByIndexIterator; + typedef NameToIndexMap::iterator NameToIndexIterator; + + ByIndexMap m_children_byindex; + NameToIndexMap m_name_toindex; private: friend class ValueObject; - ValueObjectSyntheticFilter (ValueObject &parent, lldb::SyntheticFilterSP filter); + ValueObjectSynthetic (ValueObject &parent, lldb::SyntheticChildrenSP filter); //------------------------------------------------------------------ // For ValueObject only //------------------------------------------------------------------ - DISALLOW_COPY_AND_ASSIGN (ValueObjectSyntheticFilter); + DISALLOW_COPY_AND_ASSIGN (ValueObjectSynthetic); }; } // namespace lldb_private diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreter.h b/lldb/include/lldb/Interpreter/ScriptInterpreter.h index fa7bb4cb4e5..b83617e6a45 100644 --- a/lldb/include/lldb/Interpreter/ScriptInterpreter.h +++ b/lldb/include/lldb/Interpreter/ScriptInterpreter.h @@ -10,10 +10,13 @@ #ifndef liblldb_ScriptInterpreter_h_ #define liblldb_ScriptInterpreter_h_ +#include "lldb/API/SBValue.h" + #include "lldb/lldb-private.h" #include "lldb/Core/Broadcaster.h" #include "lldb/Utility/PseudoTerminal.h" + namespace lldb_private { class ScriptInterpreter @@ -30,6 +33,15 @@ public: typedef std::string (*SWIGPythonTypeScriptCallbackFunction) (const char *python_function_name, const char *session_dictionary_name, const lldb::ValueObjectSP& valobj_sp); + + typedef void* (*SWIGPythonCreateSyntheticProvider) (const std::string python_class_name, + const char *session_dictionary_name, + const lldb::ValueObjectSP& valobj_sp); + + typedef uint32_t (*SWIGPythonCalculateNumChildren) (void *implementor); + typedef void* (*SWIGPythonGetChildAtIndex) (void *implementor, uint32_t idx); + typedef int (*SWIGPythonGetIndexOfChildWithName) (void *implementor, const char* child_name); + typedef lldb::SBValue* (*SWIGPythonCastPyObjectToSBValue) (void* data); typedef enum { @@ -89,6 +101,13 @@ public: return false; } + virtual void* + CreateSyntheticScriptedProvider (std::string class_name, + lldb::ValueObjectSP valobj) + { + return NULL; + } + // use this if the function code is just a one-liner script virtual bool GenerateTypeScriptFunction (const char* oneliner, StringList &output) @@ -113,6 +132,30 @@ public: { return; } + + virtual uint32_t + CalculateNumChildren (void *implementor) + { + return 0; + } + + virtual void* + GetChildAtIndex (void *implementor, uint32_t idx) + { + return NULL; + } + + virtual int + GetIndexOfChildWithName (void *implementor, const char* child_name) + { + return UINT32_MAX; + } + + virtual lldb::SBValue* + CastPyObjectToSBValue (void* data) + { + return NULL; + } const char * GetScriptInterpreterPtyName (); @@ -129,7 +172,12 @@ public: static void InitializeInterpreter (SWIGInitCallback python_swig_init_callback, SWIGBreakpointCallbackFunction python_swig_breakpoint_callback, - SWIGPythonTypeScriptCallbackFunction python_swig_typescript_callback); + SWIGPythonTypeScriptCallbackFunction python_swig_typescript_callback, + SWIGPythonCreateSyntheticProvider python_swig_synthetic_script, + SWIGPythonCalculateNumChildren python_swig_calc_children, + SWIGPythonGetChildAtIndex python_swig_get_child_index, + SWIGPythonGetIndexOfChildWithName python_swig_get_index_child, + SWIGPythonCastPyObjectToSBValue python_swig_cast_to_sbvalue); static void TerminateInterpreter (); diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreterPython.h b/lldb/include/lldb/Interpreter/ScriptInterpreterPython.h index 5d33e7953b7..f107fe5c3da 100644 --- a/lldb/include/lldb/Interpreter/ScriptInterpreterPython.h +++ b/lldb/include/lldb/Interpreter/ScriptInterpreterPython.h @@ -56,6 +56,22 @@ public: bool GenerateTypeScriptFunction (const char* oneliner, StringList &output); + void* + CreateSyntheticScriptedProvider (std::string class_name, + lldb::ValueObjectSP valobj); + + virtual uint32_t + CalculateNumChildren (void *implementor); + + virtual void* + GetChildAtIndex (void *implementor, uint32_t idx); + + virtual int + GetIndexOfChildWithName (void *implementor, const char* child_name); + + virtual lldb::SBValue* + CastPyObjectToSBValue (void* data); + bool GenerateFunction(std::string& signature, StringList &input, StringList &output); @@ -103,7 +119,12 @@ public: static void InitializeInterpreter (SWIGInitCallback python_swig_init_callback, SWIGBreakpointCallbackFunction python_swig_breakpoint_callback, - SWIGPythonTypeScriptCallbackFunction python_swig_typescript_callback); + SWIGPythonTypeScriptCallbackFunction python_swig_typescript_callback, + SWIGPythonCreateSyntheticProvider python_swig_synthetic_script, + SWIGPythonCalculateNumChildren python_swig_calc_children, + SWIGPythonGetChildAtIndex python_swig_get_child_index, + SWIGPythonGetIndexOfChildWithName python_swig_get_index_child, + SWIGPythonCastPyObjectToSBValue python_swig_cast_to_sbvalu); protected: diff --git a/lldb/include/lldb/Utility/PythonPointer.h b/lldb/include/lldb/Utility/PythonPointer.h new file mode 100644 index 00000000000..05852117a29 --- /dev/null +++ b/lldb/include/lldb/Utility/PythonPointer.h @@ -0,0 +1,78 @@ +//===---------------------PythonPointer.h ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef utility_PythonPointer_h_ +#define utility_PythonPointer_h_ + +#include <algorithm> +#include <memory> + +#if defined (__APPLE__) +#include <Python/Python.h> +#else +#include <Python.h> +#endif + +namespace lldb_private { + +template<class T> +class PythonPointer +{ +public: + typedef PyObject* element_type; +private: + element_type* ptr_; + bool my_ref; +public: + + PythonPointer(element_type p, bool steal_ref = false) : + ptr_(p), + my_ref(!steal_ref) + { + if (my_ref) + Py_INCREF(ptr_); + } + + PythonPointer(const PythonPointer& r, bool steal_ref = false) : + ptr_(r.ptr_), + my_ref(!steal_ref) + { + if (my_ref) + Py_INCREF(ptr_); + } + + ~PythonPointer() + { + if (my_ref) + Py_XDECREF(ptr_); + } + + PythonPointer + StealReference() + { + return PythonPointer(ptr_,true); + } + + PythonPointer + DuplicateReference() + { + return PythonPointer(ptr_, false); + } + + element_type get() const {return ptr_;} + + bool IsNull() { return ptr_ == NULL; } + bool IsNone() { return ptr_ == Py_None; } + + operator PyObject* () { return ptr_; } +}; + +} // namespace lldb + +#endif // utility_PythonPointer_h_ diff --git a/lldb/include/lldb/lldb-forward-rtti.h b/lldb/include/lldb/lldb-forward-rtti.h index faedfe6a47c..f154af4eaef 100644 --- a/lldb/include/lldb/lldb-forward-rtti.h +++ b/lldb/include/lldb/lldb-forward-rtti.h @@ -67,7 +67,8 @@ namespace lldb { typedef SharedPtr<lldb_private::SummaryFormat>::Type SummaryFormatSP; typedef SharedPtr<lldb_private::SymbolFile>::Type SymbolFileSP; typedef SharedPtr<lldb_private::SymbolContextSpecifier>::Type SymbolContextSpecifierSP; - typedef SharedPtr<lldb_private::SyntheticFilter>::Type SyntheticFilterSP; + typedef SharedPtr<lldb_private::SyntheticChildren>::Type SyntheticChildrenSP; + typedef SharedPtr<lldb_private::SyntheticChildrenFrontEnd>::Type SyntheticChildrenFrontEndSP; typedef SharedPtr<lldb_private::Target>::Type TargetSP; typedef SharedPtr<lldb_private::Thread>::Type ThreadSP; typedef SharedPtr<lldb_private::ThreadPlan>::Type ThreadPlanSP; diff --git a/lldb/include/lldb/lldb-forward.h b/lldb/include/lldb/lldb-forward.h index 412d9de8e10..b4025a62278 100644 --- a/lldb/include/lldb/lldb-forward.h +++ b/lldb/include/lldb/lldb-forward.h @@ -144,7 +144,9 @@ class SymbolContextSpecifier; class SymbolFile; class SymbolVendor; class Symtab; -class SyntheticFilter; +class SyntheticChildren; +class SyntheticChildrenFrontEnd; +class SyntheticScriptProvider; class Target; class TargetList; class Thread; diff --git a/lldb/lldb.xcodeproj/project.pbxproj b/lldb/lldb.xcodeproj/project.pbxproj index 18c9a3fe74c..c37fa65b7cf 100644 --- a/lldb/lldb.xcodeproj/project.pbxproj +++ b/lldb/lldb.xcodeproj/project.pbxproj @@ -1221,6 +1221,7 @@ 94A9112D13D5DF210046D8A6 /* FormatClasses.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FormatClasses.cpp; path = source/Core/FormatClasses.cpp; sourceTree = "<group>"; }; 94B6E76013D8833C005F417F /* ValueObjectSyntheticFilter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ValueObjectSyntheticFilter.h; path = include/lldb/Core/ValueObjectSyntheticFilter.h; sourceTree = "<group>"; }; 94B6E76113D88362005F417F /* ValueObjectSyntheticFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ValueObjectSyntheticFilter.cpp; path = source/Core/ValueObjectSyntheticFilter.cpp; sourceTree = "<group>"; }; + 94EBAC8313D9EE26009BA64E /* PythonPointer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = PythonPointer.h; path = include/lldb/Utility/PythonPointer.h; sourceTree = "<group>"; }; 961FABB81235DE1600F93A47 /* FuncUnwinders.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FuncUnwinders.cpp; path = source/Symbol/FuncUnwinders.cpp; sourceTree = "<group>"; }; 961FABB91235DE1600F93A47 /* UnwindPlan.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = UnwindPlan.cpp; path = source/Symbol/UnwindPlan.cpp; sourceTree = "<group>"; }; 961FABBA1235DE1600F93A47 /* UnwindTable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = UnwindTable.cpp; path = source/Symbol/UnwindTable.cpp; sourceTree = "<group>"; }; @@ -1864,6 +1865,7 @@ 2682F16B115EDA0D00CCFF99 /* PseudoTerminal.h */, 2682F16A115EDA0D00CCFF99 /* PseudoTerminal.cpp */, 94031A9F13CF5B3D00DCFF3C /* PriorityPointerPair.h */, + 94EBAC8313D9EE26009BA64E /* PythonPointer.h */, ); name = Utility; sourceTree = "<group>"; diff --git a/lldb/scripts/lldb.swig b/lldb/scripts/lldb.swig index 56945335504..46d73c1b148 100644 --- a/lldb/scripts/lldb.swig +++ b/lldb/scripts/lldb.swig @@ -281,7 +281,6 @@ LLDBSwigPythonBreakpointCallbackFunction // Find the current session's dictionary in the main module's dictionary. if (PyDict_Check (main_dict)) - { session_dict = NULL; while (PyDict_Next (main_dict, &pos, &key, &value)) @@ -401,7 +400,6 @@ LLDBSwigPythonCallTypeScript // Find the current session's dictionary in the main module's dictionary. if (PyDict_Check (main_dict)) - { session_dict = NULL; while (PyDict_Next (main_dict, &pos, &key, &value)) @@ -493,5 +491,278 @@ LLDBSwigPythonCallTypeScript return retval; } +SWIGEXPORT void* +LLDBSwigPythonCreateSyntheticProvider +( + const std::string python_class_name, + const char *session_dictionary_name, + const lldb::ValueObjectSP& valobj_sp +) +{ + PyObject* retval = NULL; + + if (python_class_name.empty() || !session_dictionary_name) + Py_RETURN_NONE; + + lldb::ValueObjectSP* valobj_sp_ptr = new lldb::ValueObjectSP(valobj_sp); + + PyObject *ValObj_PyObj = SWIG_NewPointerObj((void *) valobj_sp_ptr, SWIGTYPE_p_lldb__SBValue, SWIG_POINTER_OWN); + + if (ValObj_PyObj == NULL) + Py_RETURN_NONE; + + const char* python_function_name = python_class_name.c_str(); + + PyObject *pmodule, *main_dict, *session_dict, *pfunc; + PyObject *pvalue; + + pmodule = PyImport_AddModule ("__main__"); + if (pmodule != NULL) + { + main_dict = PyModule_GetDict (pmodule); + if (main_dict != NULL) + { + PyObject *key, *value; + Py_ssize_t pos = 0; + + // Find the current session's dictionary in the main module's dictionary. + + if (PyDict_Check (main_dict)) + { + session_dict = NULL; + while (PyDict_Next (main_dict, &pos, &key, &value)) + { + // We have stolen references to the key and value objects in the dictionary; we need to increment + // them now so that Python's garbage collector doesn't collect them out from under us. + Py_INCREF (key); + Py_INCREF (value); + if (strcmp (PyString_AsString (key), session_dictionary_name) == 0) + { + session_dict = value; + break; + } + } + } + + if (!session_dict || !PyDict_Check (session_dict)) + return retval; + + // Find the function we need to call in the current session's dictionary. + + pos = 0; + pfunc = NULL; + while (PyDict_Next (session_dict, &pos, &key, &value)) + { + if (PyString_Check (key)) + { + // We have stolen references to the key and value objects in the dictionary; we need to increment + // them now so that Python's garbage collector doesn't collect them out from under us. + Py_INCREF (key); + Py_INCREF (value); + if (strcmp (PyString_AsString (key), python_function_name) == 0) + { + pfunc = value; + break; + } + } + } + + // Set up the arguments and call the function. + + if (pfunc && PyCallable_Check (pfunc)) + { + PyObject *argList = Py_BuildValue("SS", ValObj_PyObj, session_dict); + + if (PyErr_Occurred ()) + { + PyErr_Print(); + PyErr_Clear(); + return retval; + } + + if (argList == NULL) + { + return retval; + } + + Py_INCREF(ValObj_PyObj); + + pvalue = PyObject_CallObject(pfunc, argList); + + Py_DECREF(argList); + + if (pvalue != NULL) + { + if (pvalue != Py_None) + retval = pvalue; + else + { + retval = Py_None; + Py_INCREF(retval); + } + } + else if (PyErr_Occurred ()) + { + PyErr_Print(); + PyErr_Clear(); + } + Py_INCREF (session_dict); + } + else if (PyErr_Occurred()) + { + PyErr_Print(); + PyErr_Clear(); + } + } + else if (PyErr_Occurred()) + { + PyErr_Print(); + PyErr_Clear(); + } + } + else if (PyErr_Occurred ()) + { + PyErr_Print(); + PyErr_Clear (); + } + if (retval) + return retval; + else + Py_RETURN_NONE; +} + +/* +these four calls below are meant to support +Python-based synthetic children providers +they essentially mimic the four pure virtual +method calls provided by the frontend class +*/ + +SWIGEXPORT uint32_t +LLDBSwigPython_CalculateNumChildren +( + PyObject *implementor +) +{ + + static char callee_name[] = "num_children"; + + if (implementor == NULL || implementor == Py_None) + return 0; + PyObject* py_return = PyObject_CallMethod(implementor, callee_name, NULL); + if (PyErr_Occurred()) + { + PyErr_Print(); + PyErr_Clear(); + } + + if (py_return == NULL || py_return == Py_None) + { + Py_XDECREF(py_return); + return UINT32_MAX; + } + long retval = PyInt_AsLong(py_return); + Py_DECREF(py_return); + if (retval >= 0) + return (uint32_t)retval; + if (PyErr_Occurred()) + { + PyErr_Print(); + PyErr_Clear(); + } + return 0; +} + +SWIGEXPORT PyObject* +LLDBSwigPython_GetChildAtIndex +( + PyObject *implementor, + uint32_t idx +) +{ + + static char callee_name[] = "get_child_at_index"; + static char param_format[] = "i"; + + if (implementor == NULL || implementor == Py_None) + return NULL; + PyObject* py_return = PyObject_CallMethod(implementor, callee_name, param_format, idx); + if (PyErr_Occurred()) + { + PyErr_Print(); + PyErr_Clear(); + } + + if (py_return == NULL || py_return == Py_None) + { + Py_XDECREF(py_return); + return NULL; + } + + lldb::SBValue* sbvalue_ptr = NULL; + + if (SWIG_ConvertPtr(py_return, (void**)&sbvalue_ptr, SWIGTYPE_p_lldb__SBValue, 0) == -1) + { + Py_DECREF(py_return); + return NULL; + } + + if (sbvalue_ptr == NULL) + return NULL; + + return py_return; +} + +SWIGEXPORT int +LLDBSwigPython_GetIndexOfChildWithName +( + PyObject *implementor, + const char* child_name +) +{ + static char callee_name[] = "get_child_index"; + static char param_format[] = "s"; + + if (implementor == NULL || implementor == Py_None) + return 0; + PyObject* py_return = PyObject_CallMethod(implementor, callee_name, param_format, child_name); + if (PyErr_Occurred()) + { + PyErr_Print(); + PyErr_Clear(); + } + + if (py_return == NULL || py_return == Py_None) + { + Py_XDECREF(py_return); + return UINT32_MAX; + } + long retval = PyInt_AsLong(py_return); + Py_DECREF(py_return); + if (retval >= 0) + return (uint32_t)retval; + if (PyErr_Occurred()) + { + PyErr_Print(); + PyErr_Clear(); + } + return 0; +} + +SWIGEXPORT lldb::SBValue* +LLDBSWIGPython_CastPyObjectToSBValue +( + PyObject* data +) +{ + lldb::SBValue* sb_ptr = NULL; + + int valid_cast = SWIG_ConvertPtr(data, (void**)&sb_ptr, SWIGTYPE_p_lldb__SBValue, 0); + + if (valid_cast == -1) + return NULL; + + return sb_ptr; +} %} diff --git a/lldb/source/API/SBCommandInterpreter.cpp b/lldb/source/API/SBCommandInterpreter.cpp index 97b93141e7d..1124d56e169 100644 --- a/lldb/source/API/SBCommandInterpreter.cpp +++ b/lldb/source/API/SBCommandInterpreter.cpp @@ -322,6 +322,19 @@ LLDBSwigPythonCallTypeScript const lldb::ValueObjectSP& valobj_sp ); +extern "C" void* +LLDBSwigPythonCreateSyntheticProvider +( + const std::string python_class_name, + const char *session_dictionary_name, + const lldb::ValueObjectSP& valobj_sp +); + + +extern "C" uint32_t LLDBSwigPython_CalculateNumChildren (void *implementor); +extern "C" void* LLDBSwigPython_GetChildAtIndex (void *implementor, uint32_t idx); +extern "C" int LLDBSwigPython_GetIndexOfChildWithName (void *implementor, const char* child_name); +extern "C" lldb::SBValue* LLDBSWIGPython_CastPyObjectToSBValue (void* data); extern "C" void init_lldb(void); @@ -334,6 +347,11 @@ SBCommandInterpreter::InitializeSWIG () g_initialized = true; ScriptInterpreter::InitializeInterpreter (init_lldb, LLDBSwigPythonBreakpointCallbackFunction, - LLDBSwigPythonCallTypeScript); + LLDBSwigPythonCallTypeScript, + LLDBSwigPythonCreateSyntheticProvider, + LLDBSwigPython_CalculateNumChildren, + LLDBSwigPython_GetChildAtIndex, + LLDBSwigPython_GetIndexOfChildWithName, + LLDBSWIGPython_CastPyObjectToSBValue); } } diff --git a/lldb/source/Commands/CommandObjectType.cpp b/lldb/source/Commands/CommandObjectType.cpp index 3a8b16cf352..0e15dbcf74f 100644 --- a/lldb/source/Commands/CommandObjectType.cpp +++ b/lldb/source/Commands/CommandObjectType.cpp @@ -10,6 +10,9 @@ #include "CommandObjectType.h" // C Includes + +#include <ctype.h> + // C++ Includes #include "lldb/Core/ConstString.h" @@ -418,8 +421,8 @@ CommandObjectTypeFormatList_LoopCallback ( // CommandObjectTypeSummaryAdd //------------------------------------------------------------------------- -static const char *g_reader_instructions = "Enter your Python command(s). Type 'DONE' to end.\n" - "def function (valobj,dict):"; +static const char *g_summary_addreader_instructions = "Enter your Python command(s). Type 'DONE' to end.\n" + "def function (valobj,dict):"; class TypeScriptAddInputReader : public InputReaderEZ { @@ -441,7 +444,7 @@ public: bool batch_mode = data.reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); if (!batch_mode) { - out_stream->Printf ("%s\n", g_reader_instructions); + out_stream->Printf ("%s\n", g_summary_addreader_instructions); if (data.reader.GetPrompt()) out_stream->Printf ("%s", data.reader.GetPrompt()); out_stream->Flush(); @@ -1016,7 +1019,7 @@ CommandObject (interpreter, " value = valobj.GetChildMemberWithName('value');\n" " return 'My value is ' + value.GetValue();\n" "DONE\n" - "(lldb)" + "(lldb) <-- type further LLDB commands here\n" ); } @@ -2404,6 +2407,10 @@ private: case 'c': m_expr_paths.push_back(option_arg); break; + case 'l': + m_class_name = std::string(option_arg); + is_class_based = true; + break; case 'p': m_skip_pointers = true; break; @@ -2425,10 +2432,12 @@ private: OptionParsingStarting () { m_cascade = true; - m_expr_paths.clear(); + m_class_name = ""; m_skip_pointers = false; m_skip_references = false; m_category = NULL; + m_expr_paths.clear(); + is_class_based = false; } const OptionDefinition* @@ -2446,9 +2455,13 @@ private: bool m_cascade; bool m_skip_references; bool m_skip_pointers; + std::string m_class_name; + bool m_input_python; option_vector m_expr_paths; const char* m_category; + bool is_class_based; + typedef option_vector::iterator ExpressionPathsIterator; }; @@ -2460,31 +2473,8 @@ private: return &m_options; } -public: - CommandObjectTypeSynthAdd (CommandInterpreter &interpreter) : - CommandObject (interpreter, - "type synth add", - "Add a new synthetic provider for a type.", - NULL), m_options (interpreter) - { - CommandArgumentEntry type_arg; - CommandArgumentData type_style_arg; - - type_style_arg.arg_type = eArgTypeName; - type_style_arg.arg_repetition = eArgRepeatPlus; - - type_arg.push_back (type_style_arg); - - m_arguments.push_back (type_arg); - - } - - ~CommandObjectTypeSynthAdd () - { - } - bool - Execute (Args& command, CommandReturnObject &result) + Execute_ChildrenList (Args& command, CommandReturnObject &result) { const size_t argc = command.GetArgumentCount(); @@ -2502,17 +2492,19 @@ public: return false; } - SyntheticFilterSP entry; + SyntheticChildrenSP entry; - entry.reset(new SyntheticFilter(m_options.m_cascade, - m_options.m_skip_pointers, - m_options.m_skip_references)); + SyntheticFilter* impl = new SyntheticFilter(m_options.m_cascade, + m_options.m_skip_pointers, + m_options.m_skip_references); + + entry.reset(impl); // go through the expression paths CommandOptions::ExpressionPathsIterator begin, end = m_options.m_expr_paths.end(); for (begin = m_options.m_expr_paths.begin(); begin != end; begin++) - entry->AddExpressionPath(*begin); + impl->AddExpressionPath(*begin); // now I have a valid provider, let's add it to every type @@ -2536,16 +2528,100 @@ public: result.SetStatus(eReturnStatusSuccessFinishNoResult); return result.Succeeded(); } + + bool + Execute_PythonClass (Args& command, CommandReturnObject &result) + { + const size_t argc = command.GetArgumentCount(); + + if (argc < 1) + { + result.AppendErrorWithFormat ("%s takes one or more args.\n", m_cmd_name.c_str()); + result.SetStatus(eReturnStatusFailed); + return false; + } + + if (m_options.m_class_name.empty() && !m_options.m_input_python) + { + result.AppendErrorWithFormat ("%s needs either a Python class name or -P to directly input Python code.\n", m_cmd_name.c_str()); + result.SetStatus(eReturnStatusFailed); + return false; + } + + SyntheticChildrenSP entry; + + SyntheticScriptProvider* impl = new SyntheticScriptProvider(m_options.m_cascade, + m_options.m_skip_pointers, + m_options.m_skip_references, + m_options.m_class_name); + + entry.reset(impl); + + // now I have a valid provider, let's add it to every type + + lldb::FormatCategorySP category; + Debugger::Formatting::Categories::Get(ConstString(m_options.m_category), category); + + for (size_t i = 0; i < argc; i++) { + const char* typeA = command.GetArgumentAtIndex(i); + ConstString typeCS(typeA); + if (typeCS) + category->Filter()->Add(typeCS.GetCString(), entry); + else + { + result.AppendError("empty typenames not allowed"); + result.SetStatus(eReturnStatusFailed); + return false; + } + } + + result.SetStatus(eReturnStatusSuccessFinishNoResult); + return result.Succeeded(); + } + +public: + + CommandObjectTypeSynthAdd (CommandInterpreter &interpreter) : + CommandObject (interpreter, + "type synth add", + "Add a new synthetic provider for a type.", + NULL), m_options (interpreter) + { + CommandArgumentEntry type_arg; + CommandArgumentData type_style_arg; + + type_style_arg.arg_type = eArgTypeName; + type_style_arg.arg_repetition = eArgRepeatPlus; + + type_arg.push_back (type_style_arg); + + m_arguments.push_back (type_arg); + + } + + ~CommandObjectTypeSynthAdd () + { + } + + bool + Execute (Args& command, CommandReturnObject &result) + { + if (m_options.is_class_based) + return Execute_PythonClass(command, result); + else + return Execute_ChildrenList(command, result); + } }; OptionDefinition CommandObjectTypeSynthAdd::CommandOptions::g_option_table[] = { { LLDB_OPT_SET_ALL, false, "cascade", 'C', required_argument, NULL, 0, eArgTypeBoolean, "If true, cascade to derived typedefs."}, - { LLDB_OPT_SET_ALL, false, "child", 'c', required_argument, NULL, 0, eArgTypeName, "Include this expression path in the synthetic view."}, { LLDB_OPT_SET_ALL, false, "skip-pointers", 'p', no_argument, NULL, 0, eArgTypeBoolean, "Don't use this format for pointers-to-type objects."}, { LLDB_OPT_SET_ALL, false, "skip-references", 'r', no_argument, NULL, 0, eArgTypeBoolean, "Don't use this format for references-to-type objects."}, { LLDB_OPT_SET_ALL, false, "category", 'w', required_argument, NULL, 0, eArgTypeName, "Add this to the given category instead of the default one."}, + { LLDB_OPT_SET_1, false, "child", 'c', required_argument, NULL, 0, eArgTypeName, "Include this expression path in the synthetic view."}, + { LLDB_OPT_SET_2, false, "python-class", 'l', required_argument, NULL, 0, eArgTypeName, "Use this Python class to produce synthetic children."}, { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } }; diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp index d7538f60d42..dff2eb00fe8 100644 --- a/lldb/source/Core/Debugger.cpp +++ b/lldb/source/Core/Debugger.cpp @@ -1746,6 +1746,12 @@ GetFormatManager() { return g_format_manager; } +void +Debugger::Formatting::ForceUpdate() +{ + GetFormatManager().Changed(); +} + bool Debugger::Formatting::ValueFormats::Get(ValueObject& vobj, ValueFormat::SharedPointer &entry) { @@ -1796,7 +1802,7 @@ Debugger::Formatting::GetSummaryFormat(ValueObject& vobj, } bool Debugger::Formatting::GetSyntheticFilter(ValueObject& vobj, - lldb::SyntheticFilterSP& entry) + lldb::SyntheticChildrenSP& entry) { return GetFormatManager().Get(vobj, entry); } diff --git a/lldb/source/Core/FormatClasses.cpp b/lldb/source/Core/FormatClasses.cpp index f301a7bbc3a..9a232ae7af4 100644 --- a/lldb/source/Core/FormatClasses.cpp +++ b/lldb/source/Core/FormatClasses.cpp @@ -21,8 +21,10 @@ #include "lldb/Core/Debugger.h" #include "lldb/Core/FormatClasses.h" #include "lldb/Core/StreamString.h" +#include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Symbol/ClangASTType.h" #include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.h" using namespace lldb; using namespace lldb_private; @@ -156,3 +158,25 @@ SyntheticFilter::GetDescription() sstr.Printf("}"); return sstr.GetString(); } + +SyntheticScriptProvider::FrontEnd::FrontEnd(std::string pclass, + lldb::ValueObjectSP be) : +SyntheticChildrenFrontEnd(be), +m_python_class(pclass) +{ + m_interpreter = be->GetUpdatePoint().GetTarget()->GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); + m_wrapper = (PyObject*)m_interpreter->CreateSyntheticScriptedProvider(m_python_class, m_backend); +} + +std::string +SyntheticScriptProvider::GetDescription() +{ + StreamString sstr; + sstr.Printf("%s%s%s Python class: %s", + m_cascades ? "" : " (not cascading)", + m_skip_pointers ? " (skip pointers)" : "", + m_skip_references ? " (skip references)" : "", + m_python_class.c_str()); + + return sstr.GetString(); +}
\ No newline at end of file diff --git a/lldb/source/Core/ValueObject.cpp b/lldb/source/Core/ValueObject.cpp index 76deaf9d288..a98d0beae93 100644 --- a/lldb/source/Core/ValueObject.cpp +++ b/lldb/source/Core/ValueObject.cpp @@ -219,6 +219,8 @@ ValueObject::UpdateFormatsIfNeeded() m_last_value_format.reset(/*(ValueFormat*)NULL*/); if (m_last_synthetic_filter.get()) m_last_synthetic_filter.reset(/*(SyntheticFilter*)NULL*/); + + m_synthetic_value = NULL; Debugger::Formatting::ValueFormats::Get(*this, m_last_value_format); Debugger::Formatting::GetSummaryFormat(*this, m_last_summary_format); @@ -1493,7 +1495,8 @@ ValueObject::CalculateSyntheticValue (lldb::SyntheticValueType use_synthetic) if (m_last_synthetic_filter.get() == NULL) return; - m_synthetic_value = new ValueObjectSyntheticFilter(*this, m_last_synthetic_filter); + if (m_synthetic_value == NULL) + m_synthetic_value = new ValueObjectSynthetic(*this, m_last_synthetic_filter); } diff --git a/lldb/source/Core/ValueObjectSyntheticFilter.cpp b/lldb/source/Core/ValueObjectSyntheticFilter.cpp index 4756b1cea29..d7d00b64611 100644 --- a/lldb/source/Core/ValueObjectSyntheticFilter.cpp +++ b/lldb/source/Core/ValueObjectSyntheticFilter.cpp @@ -35,23 +35,25 @@ using namespace lldb_private; -ValueObjectSyntheticFilter::ValueObjectSyntheticFilter (ValueObject &parent, lldb::SyntheticFilterSP filter) : +ValueObjectSynthetic::ValueObjectSynthetic (ValueObject &parent, lldb::SyntheticChildrenSP filter) : ValueObject(parent), m_address (), m_type_sp(), -m_use_synthetic (lldb::eUseSyntheticFilter), - m_synth_filter(filter) + m_use_synthetic (lldb::eUseSyntheticFilter), + m_synth_filter(filter->GetFrontEnd(parent.GetSP())), + m_children_byindex(), + m_name_toindex() { SetName (parent.GetName().AsCString()); } -ValueObjectSyntheticFilter::~ValueObjectSyntheticFilter() +ValueObjectSynthetic::~ValueObjectSynthetic() { m_owning_valobj_sp.reset(); } lldb::clang_type_t -ValueObjectSyntheticFilter::GetClangType () +ValueObjectSynthetic::GetClangType () { if (m_type_sp) return m_value.GetClangType(); @@ -60,7 +62,7 @@ ValueObjectSyntheticFilter::GetClangType () } ConstString -ValueObjectSyntheticFilter::GetTypeName() +ValueObjectSynthetic::GetTypeName() { const bool success = UpdateValueIfNeeded(); if (success && m_type_sp) @@ -70,22 +72,13 @@ ValueObjectSyntheticFilter::GetTypeName() } uint32_t -ValueObjectSyntheticFilter::CalculateNumChildren() +ValueObjectSynthetic::CalculateNumChildren() { - const bool success = UpdateValueIfNeeded(); - if (!success) - return 0; - if (m_synth_filter.get()) - return m_synth_filter->GetCount(); - return 0; - if (success && m_type_sp) - return ClangASTContext::GetNumChildren (GetClangAST (), GetClangType(), true); - else - return m_parent->GetNumChildren(); + return m_synth_filter->CalculateNumChildren(); } clang::ASTContext * -ValueObjectSyntheticFilter::GetClangAST () +ValueObjectSynthetic::GetClangAST () { const bool success = UpdateValueIfNeeded(false); if (success && m_type_sp) @@ -95,7 +88,7 @@ ValueObjectSyntheticFilter::GetClangAST () } size_t -ValueObjectSyntheticFilter::GetByteSize() +ValueObjectSynthetic::GetByteSize() { const bool success = UpdateValueIfNeeded(); if (success && m_type_sp) @@ -105,13 +98,13 @@ ValueObjectSyntheticFilter::GetByteSize() } lldb::ValueType -ValueObjectSyntheticFilter::GetValueType() const +ValueObjectSynthetic::GetValueType() const { return m_parent->GetValueType(); } bool -ValueObjectSyntheticFilter::UpdateValue () +ValueObjectSynthetic::UpdateValue () { SetValueIsValid (false); m_error.Clear(); @@ -124,46 +117,61 @@ ValueObjectSyntheticFilter::UpdateValue () return false; } + m_children_byindex.clear(); + m_name_toindex.clear(); + SetValueIsValid(true); return true; } lldb::ValueObjectSP -ValueObjectSyntheticFilter::GetChildAtIndex (uint32_t idx, bool can_create) +ValueObjectSynthetic::GetChildAtIndex (uint32_t idx, bool can_create) { - if (!m_synth_filter.get()) - return lldb::ValueObjectSP(); - if (idx >= m_synth_filter->GetCount()) - return lldb::ValueObjectSP(); - return m_parent->GetSyntheticExpressionPathChild(m_synth_filter->GetExpressionPathAtIndex(idx).c_str(), can_create); + ByIndexIterator iter = m_children_byindex.find(idx); + + if (iter == m_children_byindex.end()) + { + if (can_create) + { + lldb::ValueObjectSP synth_guy = m_synth_filter->GetChildAtIndex (idx, can_create); + m_children_byindex[idx]= synth_guy; + return synth_guy; + } + else + return lldb::ValueObjectSP(); + } + else + return iter->second; } lldb::ValueObjectSP -ValueObjectSyntheticFilter::GetChildMemberWithName (const ConstString &name, bool can_create) +ValueObjectSynthetic::GetChildMemberWithName (const ConstString &name, bool can_create) { - if (!m_synth_filter.get()) - return lldb::ValueObjectSP(); - uint32_t idx = GetIndexOfChildWithName(name); - if (idx >= m_synth_filter->GetCount()) + + uint32_t index = GetIndexOfChildWithName(name); + + if (index == UINT32_MAX) return lldb::ValueObjectSP(); - return m_parent->GetSyntheticExpressionPathChild(name.GetCString(), can_create); + + return GetChildAtIndex(index, can_create); } uint32_t -ValueObjectSyntheticFilter::GetIndexOfChildWithName (const ConstString &name) +ValueObjectSynthetic::GetIndexOfChildWithName (const ConstString &name) { - const char* name_cstr = name.GetCString(); - for (int i = 0; i < m_synth_filter->GetCount(); i++) + NameToIndexIterator iter = m_name_toindex.find(name.GetCString()); + + if (iter == m_name_toindex.end()) { - const char* expr_cstr = m_synth_filter->GetExpressionPathAtIndex(i).c_str(); - if (::strcmp(name_cstr, expr_cstr)) - return i; + uint32_t index = m_synth_filter->GetIndexOfChildWithName (name); + m_name_toindex[name.GetCString()] = index; + return index; } - return UINT32_MAX; + return iter->second; } bool -ValueObjectSyntheticFilter::IsInScope () +ValueObjectSynthetic::IsInScope () { return m_parent->IsInScope(); } diff --git a/lldb/source/Interpreter/CommandObjectScript.cpp b/lldb/source/Interpreter/CommandObjectScript.cpp index 70a4eadda2f..e5864975f27 100644 --- a/lldb/source/Interpreter/CommandObjectScript.cpp +++ b/lldb/source/Interpreter/CommandObjectScript.cpp @@ -13,6 +13,9 @@ // C++ Includes // Other libraries and framework includes // Project includes + +#include "lldb/Core/Debugger.h" + #include "lldb/Interpreter/Args.h" #include "lldb/Interpreter/CommandReturnObject.h" @@ -54,6 +57,8 @@ CommandObjectScript::ExecuteRawCommandString result.SetStatus (eReturnStatusFailed); } + Debugger::Formatting::ForceUpdate(); // script might change Python code we use for formatting.. make sure we keep up to date with it + if (command == NULL || command[0] == '\0') { script_interpreter->ExecuteInterpreterLoop (); result.SetStatus (eReturnStatusSuccessFinishNoResult); diff --git a/lldb/source/Interpreter/ScriptInterpreter.cpp b/lldb/source/Interpreter/ScriptInterpreter.cpp index 294aeec3517..27c7badca38 100644 --- a/lldb/source/Interpreter/ScriptInterpreter.cpp +++ b/lldb/source/Interpreter/ScriptInterpreter.cpp @@ -93,11 +93,21 @@ ScriptInterpreter::LanguageToString (lldb::ScriptLanguage language) void ScriptInterpreter::InitializeInterpreter (SWIGInitCallback python_swig_init_callback, SWIGBreakpointCallbackFunction python_swig_breakpoint_callback, - SWIGPythonTypeScriptCallbackFunction python_swig_typescript_callback) + SWIGPythonTypeScriptCallbackFunction python_swig_typescript_callback, + SWIGPythonCreateSyntheticProvider python_swig_synthetic_script, + SWIGPythonCalculateNumChildren python_swig_calc_children, + SWIGPythonGetChildAtIndex python_swig_get_child_index, + SWIGPythonGetIndexOfChildWithName python_swig_get_index_child, + SWIGPythonCastPyObjectToSBValue python_swig_cast_to_sbvalue) { ScriptInterpreterPython::InitializeInterpreter (python_swig_init_callback, python_swig_breakpoint_callback, - python_swig_typescript_callback); + python_swig_typescript_callback, + python_swig_synthetic_script, + python_swig_calc_children, + python_swig_get_child_index, + python_swig_get_index_child, + python_swig_cast_to_sbvalue); } void diff --git a/lldb/source/Interpreter/ScriptInterpreterPython.cpp b/lldb/source/Interpreter/ScriptInterpreterPython.cpp index 57a5929fc58..03eadc6f549 100644 --- a/lldb/source/Interpreter/ScriptInterpreterPython.cpp +++ b/lldb/source/Interpreter/ScriptInterpreterPython.cpp @@ -35,7 +35,11 @@ using namespace lldb_private; static ScriptInterpreter::SWIGInitCallback g_swig_init_callback = NULL; static ScriptInterpreter::SWIGBreakpointCallbackFunction g_swig_breakpoint_callback = NULL; static ScriptInterpreter::SWIGPythonTypeScriptCallbackFunction g_swig_typescript_callback = NULL; - +static ScriptInterpreter::SWIGPythonCreateSyntheticProvider g_swig_synthetic_script = NULL; +static ScriptInterpreter::SWIGPythonCalculateNumChildren g_swig_calc_children = NULL; +static ScriptInterpreter::SWIGPythonGetChildAtIndex g_swig_get_child_index = NULL; +static ScriptInterpreter::SWIGPythonGetIndexOfChildWithName g_swig_get_index_child = NULL; +static ScriptInterpreter::SWIGPythonCastPyObjectToSBValue g_swig_cast_to_sbvalue = NULL; static int _check_and_flush (FILE *stream) @@ -1245,6 +1249,55 @@ ScriptInterpreterPython::GenerateTypeScriptFunction (StringList &user_input, Str return true; } +void* +ScriptInterpreterPython::CreateSyntheticScriptedProvider (std::string class_name, + lldb::ValueObjectSP valobj) +{ + if (class_name.empty()) + return NULL; + + if (!valobj.get()) + return NULL; + + Target *target = valobj->GetUpdatePoint().GetTarget(); + + if (!target) + return NULL; + + Debugger &debugger = target->GetDebugger(); + ScriptInterpreter *script_interpreter = debugger.GetCommandInterpreter().GetScriptInterpreter(); + ScriptInterpreterPython *python_interpreter = (ScriptInterpreterPython *) script_interpreter; + + if (!script_interpreter) + return NULL; + + void* ret_val; + + FILE *tmp_fh = (python_interpreter->m_dbg_stdout ? python_interpreter->m_dbg_stdout : stdout); + if (CurrentThreadHasPythonLock()) + { + python_interpreter->EnterSession (); + ret_val = g_swig_synthetic_script (class_name, + python_interpreter->m_dictionary_name.c_str(), + valobj); + python_interpreter->LeaveSession (); + } + else + { + while (!GetPythonLock (1)) + fprintf (tmp_fh, + "Python interpreter locked on another thread; waiting to acquire lock...\n"); + python_interpreter->EnterSession (); + ret_val = g_swig_synthetic_script (class_name, + python_interpreter->m_dictionary_name.c_str(), + valobj); + python_interpreter->LeaveSession (); + ReleasePythonLock (); + } + + return ret_val; +} + bool ScriptInterpreterPython::GenerateTypeScriptFunction (const char* oneliner, StringList &output) { @@ -1565,15 +1618,161 @@ ScriptInterpreterPython::RunEmbeddedPythonInterpreter (lldb::thread_arg_t baton) return NULL; } +uint32_t +ScriptInterpreterPython::CalculateNumChildren (void *implementor) +{ + if (!implementor) + return 0; + + if (!g_swig_calc_children) + return 0; + + ScriptInterpreterPython *python_interpreter = this; + + uint32_t ret_val = 0; + + FILE *tmp_fh = (python_interpreter->m_dbg_stdout ? python_interpreter->m_dbg_stdout : stdout); + if (CurrentThreadHasPythonLock()) + { + python_interpreter->EnterSession (); + ret_val = g_swig_calc_children (implementor); + python_interpreter->LeaveSession (); + } + else + { + while (!GetPythonLock (1)) + fprintf (tmp_fh, + "Python interpreter locked on another thread; waiting to acquire lock...\n"); + python_interpreter->EnterSession (); + ret_val = g_swig_calc_children (implementor); + python_interpreter->LeaveSession (); + ReleasePythonLock (); + } + + return ret_val; +} + +void* +ScriptInterpreterPython::GetChildAtIndex (void *implementor, uint32_t idx) +{ + if (!implementor) + return 0; + + if (!g_swig_get_child_index) + return 0; + + ScriptInterpreterPython *python_interpreter = this; + + void* ret_val = NULL; + + FILE *tmp_fh = (python_interpreter->m_dbg_stdout ? python_interpreter->m_dbg_stdout : stdout); + if (CurrentThreadHasPythonLock()) + { + python_interpreter->EnterSession (); + ret_val = g_swig_get_child_index (implementor,idx); + python_interpreter->LeaveSession (); + } + else + { + while (!GetPythonLock (1)) + fprintf (tmp_fh, + "Python interpreter locked on another thread; waiting to acquire lock...\n"); + python_interpreter->EnterSession (); + ret_val = g_swig_get_child_index (implementor,idx); + python_interpreter->LeaveSession (); + ReleasePythonLock (); + } + + return ret_val; +} + +int +ScriptInterpreterPython::GetIndexOfChildWithName (void *implementor, const char* child_name) +{ + if (!implementor) + return UINT32_MAX; + + if (!g_swig_get_index_child) + return UINT32_MAX; + + ScriptInterpreterPython *python_interpreter = this; + + int ret_val = UINT32_MAX; + + FILE *tmp_fh = (python_interpreter->m_dbg_stdout ? python_interpreter->m_dbg_stdout : stdout); + if (CurrentThreadHasPythonLock()) + { + python_interpreter->EnterSession (); + ret_val = g_swig_get_index_child (implementor, child_name); + python_interpreter->LeaveSession (); + } + else + { + while (!GetPythonLock (1)) + fprintf (tmp_fh, + "Python interpreter locked on another thread; waiting to acquire lock...\n"); + python_interpreter->EnterSession (); + ret_val = g_swig_get_index_child (implementor, child_name); + python_interpreter->LeaveSession (); + ReleasePythonLock (); + } + + return ret_val; +} + +lldb::SBValue* +ScriptInterpreterPython::CastPyObjectToSBValue (void* data) +{ + if (!data) + return NULL; + + if (!g_swig_cast_to_sbvalue) + return NULL; + + ScriptInterpreterPython *python_interpreter = this; + + lldb::SBValue* ret_val = NULL; + + FILE *tmp_fh = (python_interpreter->m_dbg_stdout ? python_interpreter->m_dbg_stdout : stdout); + if (CurrentThreadHasPythonLock()) + { + python_interpreter->EnterSession (); + ret_val = g_swig_cast_to_sbvalue (data); + python_interpreter->LeaveSession (); + } + else + { + while (!GetPythonLock (1)) + fprintf (tmp_fh, + "Python interpreter locked on another thread; waiting to acquire lock...\n"); + python_interpreter->EnterSession (); + ret_val = g_swig_cast_to_sbvalue (data); + python_interpreter->LeaveSession (); + ReleasePythonLock (); + } + + return ret_val; +} + void ScriptInterpreterPython::InitializeInterpreter (SWIGInitCallback python_swig_init_callback, SWIGBreakpointCallbackFunction python_swig_breakpoint_callback, - SWIGPythonTypeScriptCallbackFunction python_swig_typescript_callback) + SWIGPythonTypeScriptCallbackFunction python_swig_typescript_callback, + SWIGPythonCreateSyntheticProvider python_swig_synthetic_script, + SWIGPythonCalculateNumChildren python_swig_calc_children, + SWIGPythonGetChildAtIndex python_swig_get_child_index, + SWIGPythonGetIndexOfChildWithName python_swig_get_index_child, + SWIGPythonCastPyObjectToSBValue python_swig_cast_to_sbvalue) { g_swig_init_callback = python_swig_init_callback; g_swig_breakpoint_callback = python_swig_breakpoint_callback; g_swig_typescript_callback = python_swig_typescript_callback; + g_swig_synthetic_script = python_swig_synthetic_script; + g_swig_calc_children = python_swig_calc_children; + g_swig_get_child_index = python_swig_get_child_index; + g_swig_get_index_child = python_swig_get_index_child; + g_swig_cast_to_sbvalue = python_swig_cast_to_sbvalue; } void diff --git a/lldb/test/functionalities/data-formatter/data-formatter-python-synth/Makefile b/lldb/test/functionalities/data-formatter/data-formatter-python-synth/Makefile new file mode 100644 index 00000000000..314f1cb2f07 --- /dev/null +++ b/lldb/test/functionalities/data-formatter/data-formatter-python-synth/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/lldb/test/functionalities/data-formatter/data-formatter-python-synth/TestDataFormatterPythonSynth.py b/lldb/test/functionalities/data-formatter/data-formatter-python-synth/TestDataFormatterPythonSynth.py new file mode 100644 index 00000000000..59f02086cc5 --- /dev/null +++ b/lldb/test/functionalities/data-formatter/data-formatter-python-synth/TestDataFormatterPythonSynth.py @@ -0,0 +1,95 @@ +""" +Test lldb data formatter subsystem. +""" + +import os, time +import unittest2 +import lldb +from lldbtest import * + +class DataFormatterTestCase(TestBase): + + mydir = os.path.join("functionalities", "data-formatter", "data-formatter-python-synth") + + @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") + def test_with_dsym_and_run_command(self): + """Test data formatter commands.""" + self.buildDsym() + self.data_formatter_commands() + + def test_with_dwarf_and_run_command(self): + """Test data formatter commands.""" + self.buildDwarf() + self.data_formatter_commands() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.cpp', '// Set break point at this line.') + + def data_formatter_commands(self): + """Test that that file and class static variables display correctly.""" + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + self.expect("breakpoint set -f main.cpp -l %d" % self.line, + BREAKPOINT_CREATED, + startstr = "Breakpoint created: 1: file ='main.cpp', line = %d, locations = 1" % + self.line) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + self.runCmd('type synth clear', check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + # print the f00_1 variable without a synth + self.expect("frame variable f00_1", + substrs = ['a = 0', + 'b = 1', + 'r = 33']); + + # now set up the synth + self.runCmd("script from fooSynthProvider import *") + self.runCmd("type synth add -l fooSynthProvider foo") + + # check that we get only the two variables + self.expect("frame variable f00_1", + substrs = ['r = 33', + 'a = 0']); + + # check that we do not get the extra vars and that we cache results + self.expect("frame variable f00_1", matching=False, + substrs = ['looking for', + 'b = 1']); + + # check that the caching does not span beyond the stopoint + self.runCmd("n") + + self.expect("frame variable f00_1", + substrs = ['r = 33', + 'a = 1']); + + # delete the synth and check that we get good output + self.runCmd("type synth delete foo") + self.expect("frame variable f00_1", + substrs = ['a = 1', + 'b = 1', + 'r = 33']); + +if __name__ == '__main__': + import atexit + lldb.SBDebugger.Initialize() + atexit.register(lambda: lldb.SBDebugger.Terminate()) + unittest2.main() diff --git a/lldb/test/functionalities/data-formatter/data-formatter-python-synth/fooSynthProvider.py b/lldb/test/functionalities/data-formatter/data-formatter-python-synth/fooSynthProvider.py new file mode 100644 index 00000000000..add34074fb0 --- /dev/null +++ b/lldb/test/functionalities/data-formatter/data-formatter-python-synth/fooSynthProvider.py @@ -0,0 +1,16 @@ +class fooSynthProvider: + def __init__(self, valobj, dict): + self.valobj = valobj; + def num_children(self): + return 2; + def get_child_at_index(self, index): + if index == 1: + child = self.valobj.GetChildMemberWithName('a'); + else: + child = self.valobj.GetChildMemberWithName('r'); + return child; + def get_child_index(self, name): + if name == 'a': + return 1; + else: + return 0;
\ No newline at end of file diff --git a/lldb/test/functionalities/data-formatter/data-formatter-python-synth/main.cpp b/lldb/test/functionalities/data-formatter/data-formatter-python-synth/main.cpp new file mode 100644 index 00000000000..b541aa0bf9c --- /dev/null +++ b/lldb/test/functionalities/data-formatter/data-formatter-python-synth/main.cpp @@ -0,0 +1,53 @@ +struct foo +{ + int a; + int b; + int c; + int d; + int e; + int f; + int g; + int h; + int i; + int j; + int k; + int l; + int m; + int n; + int o; + int p; + int q; + int r; + + foo(int X) : + a(X), + b(X+1), + c(X+3), + d(X+5), + e(X+7), + f(X+9), + g(X+11), + h(X+13), + i(X+15), + j(X+17), + k(X+19), + l(X+21), + m(X+23), + n(X+25), + o(X+27), + p(X+29), + q(X+31), + r(X+33) {} +}; + +int main() +{ + foo f00_1(0); + foo f00_2(6); + foo *f00_3 = new foo(12); + foo& f00_4 = *(new foo(18)); + + f00_1.a++; // Set break point at this line. + + return 0; +}
\ No newline at end of file |