diff --git a/lldb/bindings/python/python-swigsafecast.swig b/lldb/bindings/python/python-swigsafecast.swig index 4721dfdc17e6a..3ea24f1a31414 100644 --- a/lldb/bindings/python/python-swigsafecast.swig +++ b/lldb/bindings/python/python-swigsafecast.swig @@ -142,5 +142,9 @@ PythonObject SWIGBridge::ToSWIGWrapper( return ToSWIGHelper(module_spec_sb.release(), SWIGTYPE_p_lldb__SBModuleSpec); } +PythonObject SWIGBridge::ToSWIGWrapper(lldb::DescriptionLevel level) { + return PythonInteger((int64_t) level); +} + } // namespace python } // namespace lldb_private diff --git a/lldb/bindings/python/python-wrapper.swig b/lldb/bindings/python/python-wrapper.swig index 2c30d536a753d..64b7dc8381073 100644 --- a/lldb/bindings/python/python-wrapper.swig +++ b/lldb/bindings/python/python-wrapper.swig @@ -422,6 +422,30 @@ void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBBreakpoint(PyObject * return sb_ptr; } +void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBFrame(PyObject * data) { + lldb::SBFrame *sb_ptr = nullptr; + + int valid_cast = + SWIG_ConvertPtr(data, (void **)&sb_ptr, SWIGTYPE_p_lldb__SBFrame, 0); + + if (valid_cast == -1) + return NULL; + + return sb_ptr; +} + +void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBBreakpointLocation(PyObject * data) { + lldb::SBBreakpointLocation *sb_ptr = nullptr; + + int valid_cast = + SWIG_ConvertPtr(data, (void **)&sb_ptr, SWIGTYPE_p_lldb__SBBreakpointLocation, 0); + + if (valid_cast == -1) + return NULL; + + return sb_ptr; +} + void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBAttachInfo(PyObject * data) { lldb::SBAttachInfo *sb_ptr = nullptr; diff --git a/lldb/bindings/python/static-binding/LLDBWrapPython.cpp b/lldb/bindings/python/static-binding/LLDBWrapPython.cpp index 5501525fedee0..0d8bebd9e8a58 100644 --- a/lldb/bindings/python/static-binding/LLDBWrapPython.cpp +++ b/lldb/bindings/python/static-binding/LLDBWrapPython.cpp @@ -5238,6 +5238,30 @@ void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBBreakpoint(PyObject * return sb_ptr; } +void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBFrame(PyObject * data) { + lldb::SBFrame *sb_ptr = nullptr; + + int valid_cast = + SWIG_ConvertPtr(data, (void **)&sb_ptr, SWIGTYPE_p_lldb__SBFrame, 0); + + if (valid_cast == -1) + return NULL; + + return sb_ptr; +} + +void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBBreakpointLocation(PyObject * data) { + lldb::SBBreakpointLocation *sb_ptr = nullptr; + + int valid_cast = + SWIG_ConvertPtr(data, (void **)&sb_ptr, SWIGTYPE_p_lldb__SBBreakpointLocation, 0); + + if (valid_cast == -1) + return NULL; + + return sb_ptr; +} + void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBAttachInfo(PyObject * data) { lldb::SBAttachInfo *sb_ptr = nullptr; @@ -11924,6 +11948,34 @@ SWIGINTERN PyObject *_wrap_SBBreakpoint_AddLocation(PyObject *self, PyObject *ar } +SWIGINTERN PyObject *_wrap_SBBreakpoint_AddFacadeLocation(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + lldb::SBBreakpoint *arg1 = (lldb::SBBreakpoint *) 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + lldb::SBBreakpointLocation result; + + (void)self; + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_lldb__SBBreakpoint, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SBBreakpoint_AddFacadeLocation" "', argument " "1"" of type '" "lldb::SBBreakpoint *""'"); + } + arg1 = reinterpret_cast< lldb::SBBreakpoint * >(argp1); + { + SWIG_PYTHON_THREAD_BEGIN_ALLOW; + result = (arg1)->AddFacadeLocation(); + SWIG_PYTHON_THREAD_END_ALLOW; + } + resultobj = SWIG_NewPointerObj((new lldb::SBBreakpointLocation(result)), SWIGTYPE_p_lldb__SBBreakpointLocation, SWIG_POINTER_OWN | 0 ); + return resultobj; +fail: + return NULL; +} + + SWIGINTERN PyObject *_wrap_SBBreakpoint_SerializeToStructuredData(PyObject *self, PyObject *args) { PyObject *resultobj = 0; lldb::SBBreakpoint *arg1 = (lldb::SBBreakpoint *) 0 ; @@ -69598,13 +69650,104 @@ SWIGINTERN PyObject *_wrap_SBTarget_BreakpointCreateByName__SWIG_4(PyObject *sel } +SWIGINTERN PyObject *_wrap_SBTarget_BreakpointCreateByName__SWIG_5(PyObject *self, Py_ssize_t nobjs, PyObject **swig_obj) { + PyObject *resultobj = 0; + lldb::SBTarget *arg1 = (lldb::SBTarget *) 0 ; + char *arg2 = (char *) 0 ; + uint32_t arg3 ; + lldb::LanguageType arg4 ; + lldb::addr_t arg5 ; + bool arg6 ; + lldb::SBFileSpecList *arg7 = 0 ; + lldb::SBFileSpecList *arg8 = 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + int res2 ; + char *buf2 = 0 ; + int alloc2 = 0 ; + unsigned int val3 ; + int ecode3 = 0 ; + int val4 ; + int ecode4 = 0 ; + unsigned long long val5 ; + int ecode5 = 0 ; + bool val6 ; + int ecode6 = 0 ; + void *argp7 = 0 ; + int res7 = 0 ; + void *argp8 = 0 ; + int res8 = 0 ; + lldb::SBBreakpoint result; + + (void)self; + if ((nobjs < 8) || (nobjs > 8)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_lldb__SBTarget, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SBTarget_BreakpointCreateByName" "', argument " "1"" of type '" "lldb::SBTarget *""'"); + } + arg1 = reinterpret_cast< lldb::SBTarget * >(argp1); + res2 = SWIG_AsCharPtrAndSize(swig_obj[1], &buf2, NULL, &alloc2); + if (!SWIG_IsOK(res2)) { + SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "SBTarget_BreakpointCreateByName" "', argument " "2"" of type '" "char const *""'"); + } + arg2 = reinterpret_cast< char * >(buf2); + ecode3 = SWIG_AsVal_unsigned_SS_int(swig_obj[2], &val3); + if (!SWIG_IsOK(ecode3)) { + SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "SBTarget_BreakpointCreateByName" "', argument " "3"" of type '" "uint32_t""'"); + } + arg3 = static_cast< uint32_t >(val3); + ecode4 = SWIG_AsVal_int(swig_obj[3], &val4); + if (!SWIG_IsOK(ecode4)) { + SWIG_exception_fail(SWIG_ArgError(ecode4), "in method '" "SBTarget_BreakpointCreateByName" "', argument " "4"" of type '" "lldb::LanguageType""'"); + } + arg4 = static_cast< lldb::LanguageType >(val4); + ecode5 = SWIG_AsVal_unsigned_SS_long_SS_long(swig_obj[4], &val5); + if (!SWIG_IsOK(ecode5)) { + SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "SBTarget_BreakpointCreateByName" "', argument " "5"" of type '" "lldb::addr_t""'"); + } + arg5 = static_cast< lldb::addr_t >(val5); + ecode6 = SWIG_AsVal_bool(swig_obj[5], &val6); + if (!SWIG_IsOK(ecode6)) { + SWIG_exception_fail(SWIG_ArgError(ecode6), "in method '" "SBTarget_BreakpointCreateByName" "', argument " "6"" of type '" "bool""'"); + } + arg6 = static_cast< bool >(val6); + res7 = SWIG_ConvertPtr(swig_obj[6], &argp7, SWIGTYPE_p_lldb__SBFileSpecList, 0 | 0); + if (!SWIG_IsOK(res7)) { + SWIG_exception_fail(SWIG_ArgError(res7), "in method '" "SBTarget_BreakpointCreateByName" "', argument " "7"" of type '" "lldb::SBFileSpecList const &""'"); + } + if (!argp7) { + SWIG_exception_fail(SWIG_NullReferenceError, "invalid null reference " "in method '" "SBTarget_BreakpointCreateByName" "', argument " "7"" of type '" "lldb::SBFileSpecList const &""'"); + } + arg7 = reinterpret_cast< lldb::SBFileSpecList * >(argp7); + res8 = SWIG_ConvertPtr(swig_obj[7], &argp8, SWIGTYPE_p_lldb__SBFileSpecList, 0 | 0); + if (!SWIG_IsOK(res8)) { + SWIG_exception_fail(SWIG_ArgError(res8), "in method '" "SBTarget_BreakpointCreateByName" "', argument " "8"" of type '" "lldb::SBFileSpecList const &""'"); + } + if (!argp8) { + SWIG_exception_fail(SWIG_NullReferenceError, "invalid null reference " "in method '" "SBTarget_BreakpointCreateByName" "', argument " "8"" of type '" "lldb::SBFileSpecList const &""'"); + } + arg8 = reinterpret_cast< lldb::SBFileSpecList * >(argp8); + { + SWIG_PYTHON_THREAD_BEGIN_ALLOW; + result = (arg1)->BreakpointCreateByName((char const *)arg2,arg3,arg4,arg5,arg6,(lldb::SBFileSpecList const &)*arg7,(lldb::SBFileSpecList const &)*arg8); + SWIG_PYTHON_THREAD_END_ALLOW; + } + resultobj = SWIG_NewPointerObj((new lldb::SBBreakpoint(result)), SWIGTYPE_p_lldb__SBBreakpoint, SWIG_POINTER_OWN | 0 ); + if (alloc2 == SWIG_NEWOBJ) delete[] buf2; + return resultobj; +fail: + if (alloc2 == SWIG_NEWOBJ) delete[] buf2; + return NULL; +} + + SWIGINTERN PyObject *_wrap_SBTarget_BreakpointCreateByName(PyObject *self, PyObject *args) { Py_ssize_t argc; - PyObject *argv[7] = { + PyObject *argv[9] = { 0 }; - if (!(argc = SWIG_Python_UnpackTuple(args, "SBTarget_BreakpointCreateByName", 0, 6, argv))) SWIG_fail; + if (!(argc = SWIG_Python_UnpackTuple(args, "SBTarget_BreakpointCreateByName", 0, 8, argv))) SWIG_fail; --argc; if (argc == 2) { int _v = 0; @@ -69717,6 +69860,51 @@ SWIGINTERN PyObject *_wrap_SBTarget_BreakpointCreateByName(PyObject *self, PyObj } } } + if (argc == 8) { + int _v = 0; + void *vptr = 0; + int res = SWIG_ConvertPtr(argv[0], &vptr, SWIGTYPE_p_lldb__SBTarget, 0); + _v = SWIG_CheckState(res); + if (_v) { + int res = SWIG_AsCharPtrAndSize(argv[1], 0, NULL, 0); + _v = SWIG_CheckState(res); + if (_v) { + { + int res = SWIG_AsVal_unsigned_SS_int(argv[2], NULL); + _v = SWIG_CheckState(res); + } + if (_v) { + { + int res = SWIG_AsVal_int(argv[3], NULL); + _v = SWIG_CheckState(res); + } + if (_v) { + { + int res = SWIG_AsVal_unsigned_SS_long_SS_long(argv[4], NULL); + _v = SWIG_CheckState(res); + } + if (_v) { + { + int res = SWIG_AsVal_bool(argv[5], NULL); + _v = SWIG_CheckState(res); + } + if (_v) { + int res = SWIG_ConvertPtr(argv[6], 0, SWIGTYPE_p_lldb__SBFileSpecList, SWIG_POINTER_NO_NULL | 0); + _v = SWIG_CheckState(res); + if (_v) { + int res = SWIG_ConvertPtr(argv[7], 0, SWIGTYPE_p_lldb__SBFileSpecList, SWIG_POINTER_NO_NULL | 0); + _v = SWIG_CheckState(res); + if (_v) { + return _wrap_SBTarget_BreakpointCreateByName__SWIG_5(self, argc, argv); + } + } + } + } + } + } + } + } + } fail: SWIG_Python_RaiseOrModifyTypeError("Wrong number or type of arguments for overloaded function 'SBTarget_BreakpointCreateByName'.\n" @@ -69725,7 +69913,8 @@ SWIGINTERN PyObject *_wrap_SBTarget_BreakpointCreateByName(PyObject *self, PyObj " lldb::SBTarget::BreakpointCreateByName(char const *)\n" " lldb::SBTarget::BreakpointCreateByName(char const *,lldb::SBFileSpecList const &,lldb::SBFileSpecList const &)\n" " lldb::SBTarget::BreakpointCreateByName(char const *,uint32_t,lldb::SBFileSpecList const &,lldb::SBFileSpecList const &)\n" - " lldb::SBTarget::BreakpointCreateByName(char const *,uint32_t,lldb::LanguageType,lldb::SBFileSpecList const &,lldb::SBFileSpecList const &)\n"); + " lldb::SBTarget::BreakpointCreateByName(char const *,uint32_t,lldb::LanguageType,lldb::SBFileSpecList const &,lldb::SBFileSpecList const &)\n" + " lldb::SBTarget::BreakpointCreateByName(char const *,uint32_t,lldb::LanguageType,lldb::addr_t,bool,lldb::SBFileSpecList const &,lldb::SBFileSpecList const &)\n"); return 0; } @@ -97056,7 +97245,16 @@ static PyMethodDef SwigMethods[] = { "breakpoint locations with hardware breakpoints. Returns an error if this\n" "fails, e.g. when there aren't enough hardware resources available.\n" ""}, - { "SBBreakpoint_AddLocation", _wrap_SBBreakpoint_AddLocation, METH_VARARGS, "SBBreakpoint_AddLocation(SBBreakpoint self, SBAddress address) -> SBError"}, + { "SBBreakpoint_AddLocation", _wrap_SBBreakpoint_AddLocation, METH_VARARGS, "\n" + "Adds a location to the breakpoint at the address passed in.\n" + "Can only be called from a ScriptedBreakpointResolver...\n" + ""}, + { "SBBreakpoint_AddFacadeLocation", _wrap_SBBreakpoint_AddFacadeLocation, METH_O, "\n" + "Add a \"Facade location\" to the breakpoint. This returns the Facade\n" + "Location that was added, which you can then use in\n" + "get_location_description and was_hit in your breakpoint resolver.\n" + "Can only be called from a ScriptedBreakpointResolver.\n" + ""}, { "SBBreakpoint_SerializeToStructuredData", _wrap_SBBreakpoint_SerializeToStructuredData, METH_O, "SBBreakpoint_SerializeToStructuredData(SBBreakpoint self) -> SBStructuredData"}, { "SBBreakpoint___repr__", _wrap_SBBreakpoint___repr__, METH_O, "SBBreakpoint___repr__(SBBreakpoint self) -> std::string"}, { "SBBreakpoint_swigregister", SBBreakpoint_swigregister, METH_O, NULL}, @@ -101023,6 +101221,7 @@ static PyMethodDef SwigMethods[] = { "SBTarget_BreakpointCreateByName(SBTarget self, char const * symbol_name, SBFileSpecList module_list, SBFileSpecList comp_unit_list) -> SBBreakpoint\n" "SBTarget_BreakpointCreateByName(SBTarget self, char const * symbol_name, uint32_t name_type_mask, SBFileSpecList module_list, SBFileSpecList comp_unit_list) -> SBBreakpoint\n" "SBTarget_BreakpointCreateByName(SBTarget self, char const * symbol_name, uint32_t name_type_mask, lldb::LanguageType symbol_language, SBFileSpecList module_list, SBFileSpecList comp_unit_list) -> SBBreakpoint\n" + "SBTarget_BreakpointCreateByName(SBTarget self, char const * symbol_name, uint32_t name_type_mask, lldb::LanguageType symbol_language, lldb::addr_t offset, bool offset_is_insn_count, SBFileSpecList module_list, SBFileSpecList comp_unit_list) -> SBBreakpoint\n" ""}, { "SBTarget_BreakpointCreateByNames", _wrap_SBTarget_BreakpointCreateByNames, METH_VARARGS, "\n" "SBTarget_BreakpointCreateByNames(SBTarget self, char const ** symbol_name, uint32_t name_type_mask, SBFileSpecList module_list, SBFileSpecList comp_unit_list) -> SBBreakpoint\n" @@ -105192,6 +105391,7 @@ SWIG_init(void) { SWIG_Python_SetConstant(d, "eLanguageTypeAssembly",SWIG_From_int(static_cast< int >(lldb::eLanguageTypeAssembly))); SWIG_Python_SetConstant(d, "eLanguageTypeC_sharp",SWIG_From_int(static_cast< int >(lldb::eLanguageTypeC_sharp))); SWIG_Python_SetConstant(d, "eLanguageTypeMojo",SWIG_From_int(static_cast< int >(lldb::eLanguageTypeMojo))); + SWIG_Python_SetConstant(d, "eLanguageTypeLastStandardLanguage",SWIG_From_int(static_cast< int >(lldb::eLanguageTypeLastStandardLanguage))); SWIG_Python_SetConstant(d, "eLanguageTypeMipsAssembler",SWIG_From_int(static_cast< int >(lldb::eLanguageTypeMipsAssembler))); SWIG_Python_SetConstant(d, "eNumLanguageTypes",SWIG_From_int(static_cast< int >(lldb::eNumLanguageTypes))); SWIG_Python_SetConstant(d, "eInstrumentationRuntimeTypeAddressSanitizer",SWIG_From_int(static_cast< int >(lldb::eInstrumentationRuntimeTypeAddressSanitizer))); diff --git a/lldb/bindings/python/static-binding/lldb.py b/lldb/bindings/python/static-binding/lldb.py index 5f1b1cb420974..c831d60417e52 100644 --- a/lldb/bindings/python/static-binding/lldb.py +++ b/lldb/bindings/python/static-binding/lldb.py @@ -877,6 +877,8 @@ def lldb_iter(obj, getsize, getelem): eLanguageTypeMojo = _lldb.eLanguageTypeMojo +eLanguageTypeLastStandardLanguage = _lldb.eLanguageTypeLastStandardLanguage + eLanguageTypeMipsAssembler = _lldb.eLanguageTypeMipsAssembler r"""Mips_Assembler.""" eNumLanguageTypes = _lldb.eNumLanguageTypes @@ -3160,9 +3162,21 @@ def SetIsHardware(self, is_hardware): return _lldb.SBBreakpoint_SetIsHardware(self, is_hardware) def AddLocation(self, address): - r"""AddLocation(SBBreakpoint self, SBAddress address) -> SBError""" + r""" + Adds a location to the breakpoint at the address passed in. + Can only be called from a ScriptedBreakpointResolver... + """ return _lldb.SBBreakpoint_AddLocation(self, address) + def AddFacadeLocation(self): + r""" + Add a "Facade location" to the breakpoint. This returns the Facade + Location that was added, which you can then use in + get_location_description and was_hit in your breakpoint resolver. + Can only be called from a ScriptedBreakpointResolver. + """ + return _lldb.SBBreakpoint_AddFacadeLocation(self) + def SerializeToStructuredData(self): r"""SerializeToStructuredData(SBBreakpoint self) -> SBStructuredData""" return _lldb.SBBreakpoint_SerializeToStructuredData(self) @@ -13489,6 +13503,7 @@ def BreakpointCreateByName(self, *args): BreakpointCreateByName(SBTarget self, char const * symbol_name, SBFileSpecList module_list, SBFileSpecList comp_unit_list) -> SBBreakpoint BreakpointCreateByName(SBTarget self, char const * symbol_name, uint32_t name_type_mask, SBFileSpecList module_list, SBFileSpecList comp_unit_list) -> SBBreakpoint BreakpointCreateByName(SBTarget self, char const * symbol_name, uint32_t name_type_mask, lldb::LanguageType symbol_language, SBFileSpecList module_list, SBFileSpecList comp_unit_list) -> SBBreakpoint + BreakpointCreateByName(SBTarget self, char const * symbol_name, uint32_t name_type_mask, lldb::LanguageType symbol_language, lldb::addr_t offset, bool offset_is_insn_count, SBFileSpecList module_list, SBFileSpecList comp_unit_list) -> SBBreakpoint """ return _lldb.SBTarget_BreakpointCreateByName(self, *args) diff --git a/lldb/docs/use/tutorials/creating-custom-breakpoints.md b/lldb/docs/use/tutorials/creating-custom-breakpoints.md index e3081c44e3650..04673c310ccf3 100644 --- a/lldb/docs/use/tutorials/creating-custom-breakpoints.md +++ b/lldb/docs/use/tutorials/creating-custom-breakpoints.md @@ -125,4 +125,48 @@ you can use for this purpose. Your __init__ function gets passed this SBStructuredData object. This API also allows you to directly provide the list of Modules and the list of CompileUnits that will make up the SearchFilter. If you pass in empty lists, the breakpoint will use the default "search -everywhere,accept everything" filter. \ No newline at end of file +everywhere,accept everything" filter. + +### Providing Facade Locations: + +The breakpoint resolver interface also allows you to present a separate set +of locations for the breakpoint than the ones that actually implement the +breakpoint in the target. + +An example use case for this is if you are providing a debugging interface for a +library that implements an interpreter for a language lldb can't debug. But +while debugging that library at the level of the implementation language (e.g. C/C++, etc) +you would like to offer the ability to "stop when a line in a source language +file is executed". + +You can do this if you know where new lines of code are dispatched in the +interpreter. You would set a breakpoint there, and then look at the state +when that breakpoint is hit to see if it is dispatching the source file and +line that were requested, and stop appropriately. + +Facade breakpoint locations are intended to make a more natural presentation +of that sort of feature. The idea is that you would make a custom breakpoint +resolver that sets actual locations in the places of interest in the interpreter. + +Then your resolver would add "facade locations" that represent the places in the +interpreted code that you want the breakpoint to stop at, using SBBreakpoint::AddFacadeLocation. +When lldb describes the breakpoint, it will only show the Facade locations. +Since facade breakpoint location's description is customizable, you can make these +locations more descriptive. And when the "real" location is hit, lldb will call the +"was_hit" method of your resolver. That will return the facade location you +consider to have been hit this time around, or if you return None, the breakpoint +will be considered not to have been hit. + +Note, this feature is also useful if you don't intend to present facade +locations since it essentially provides a scripted breakpoint condition. Every +time one of the locations in your breakpoint is hit, you can run the code in +your "was_hit" to determine whether to consider the breakpoint hit or not, and +return the location you were passed in if you want it to be a hit, and None if not. + +The Facade location adds these optional affordances to the Resolver class: + +| Name | Arguments | Description| +|-------|-----------|------------| +|`was_hit`| `frame`:`lldb.SBFrame` `bp_loc`:`lldb.SBBreakpointLocation` | This will get called when one of the "real" locations set by your resolver is hit. `frame` is the stack frame that hit this location. `bp_loc` is the real location that was hit. Return either the facade location that you want to consider hit on this stop, or None if you don't consider any of your facade locations to have been hit. | +| `get_location_description` | `bp_loc`:`lldb.SBBreakpointLocation` `desc_level`:`lldb.DescriptionLevel` `bp_loc` is the facade location to describe.| Use this to provide a helpful description of each facade location. ``desc_level`` is the level of description requested. The Brief description is printed when the location is hit. Full is printed for `break list` and Verbose for `break list -v`.| + diff --git a/lldb/include/lldb/API/SBBreakpoint.h b/lldb/include/lldb/API/SBBreakpoint.h index 18ed3e7226d3b..fe19ba998ea67 100644 --- a/lldb/include/lldb/API/SBBreakpoint.h +++ b/lldb/include/lldb/API/SBBreakpoint.h @@ -153,9 +153,15 @@ class LLDB_API SBBreakpoint { /// fails, e.g. when there aren't enough hardware resources available. lldb::SBError SetIsHardware(bool is_hardware); - // Can only be called from a ScriptedBreakpointResolver... + /// Adds a location to the breakpoint at the address passed in. + /// Can only be called from a ScriptedBreakpointResolver... SBError AddLocation(SBAddress &address); + /// Add a "Facade location" to the breakpoint. This returns the Facade + /// Location that was added, which you can then use in + /// get_location_description and was_hit in your breakpoint resolver. + /// Can only be called from a ScriptedBreakpointResolver. + SBBreakpointLocation AddFacadeLocation(); SBStructuredData SerializeToStructuredData(); diff --git a/lldb/include/lldb/API/SBBreakpointLocation.h b/lldb/include/lldb/API/SBBreakpointLocation.h index fa823e2b518ac..9b0d4839aca82 100644 --- a/lldb/include/lldb/API/SBBreakpointLocation.h +++ b/lldb/include/lldb/API/SBBreakpointLocation.h @@ -24,6 +24,8 @@ class SWIGBridge; namespace lldb { class LLDB_API SBBreakpointLocation { + friend class lldb_private::ScriptInterpreter; + public: SBBreakpointLocation(); diff --git a/lldb/include/lldb/API/SBFrame.h b/lldb/include/lldb/API/SBFrame.h index 2f8282b079148..3c559086678df 100644 --- a/lldb/include/lldb/API/SBFrame.h +++ b/lldb/include/lldb/API/SBFrame.h @@ -227,6 +227,7 @@ class LLDB_API SBFrame { friend class SBThread; friend class SBValue; + friend class lldb_private::ScriptInterpreter; friend class lldb_private::python::SWIGBridge; friend class lldb_private::lua::SWIGBridge; diff --git a/lldb/include/lldb/Breakpoint/Breakpoint.h b/lldb/include/lldb/Breakpoint/Breakpoint.h index 26a5e901a0d7e..c0bad98c95b29 100644 --- a/lldb/include/lldb/Breakpoint/Breakpoint.h +++ b/lldb/include/lldb/Breakpoint/Breakpoint.h @@ -248,6 +248,23 @@ class Breakpoint : public std::enable_shared_from_this, /// Returns a pointer to the new location. lldb::BreakpointLocationSP AddLocation(const Address &addr, bool *new_location = nullptr); + /// Add a `facade` location to the breakpoint's collection of facade + /// locations. This is only meant to be called by the breakpoint's resolver. + /// Facade locations are placeholders that a scripted breakpoint can use to + /// represent the stop locations provided by the breakpoint. The scripted + /// breakpoint should record the id of the facade location, and provide + /// the description of the location in the GetDescription method + /// To emulate hitting a facade location, the breakpoint's WasHit should + /// return the ID of the facade that was "hit". + /// + /// \param[out] new_location + /// Set to \b true if a new location was created, to \b false if there + /// already was a location at this Address. + /// \return + /// Returns a pointer to the new location. + lldb::BreakpointLocationSP AddFacadeLocation(); + + lldb::BreakpointLocationSP GetFacadeLocationByID(lldb::break_id_t); /// Find a breakpoint location by Address. /// @@ -268,27 +285,38 @@ class Breakpoint : public std::enable_shared_from_this, /// there is no breakpoint location at that address. lldb::break_id_t FindLocationIDByAddress(const Address &addr); - /// Find a breakpoint location for a given breakpoint location ID. + /// Find a breakpoint location for a given breakpoint location ID. If there + /// are Facade Locations in the breakpoint, the facade locations will be + /// searched instead of the "real" ones. /// /// \param[in] bp_loc_id /// The ID specifying the location. + /// + /// \param[in] use_facade + /// If \b true, then prefer facade locations over "real" ones if they exist. + /// /// \return /// Returns a shared pointer to the location with ID \a bp_loc_id. The /// pointer /// in the shared pointer will be nullptr if there is no location with that /// ID. - lldb::BreakpointLocationSP FindLocationByID(lldb::break_id_t bp_loc_id); + lldb::BreakpointLocationSP FindLocationByID(lldb::break_id_t bp_loc_id, + bool use_facade = true); /// Get breakpoint locations by index. /// /// \param[in] index /// The location index. /// + /// \param[in] use_facade + /// If \b true, then prefer facade locations over "real" ones if they exist. + /// /// \return /// Returns a shared pointer to the location with index \a /// index. The shared pointer might contain nullptr if \a index is /// greater than then number of actual locations. - lldb::BreakpointLocationSP GetLocationAtIndex(size_t index); + lldb::BreakpointLocationSP GetLocationAtIndex(size_t index, + bool use_facade = true); /// Removes all invalid breakpoint locations. /// @@ -409,9 +437,12 @@ class Breakpoint : public std::enable_shared_from_this, /// Return the number of breakpoint locations that have resolved to actual /// breakpoint sites. /// + /// \param[in] use_facade + /// If \b true, then prefer facade locations over "real" ones if they exist. + /// /// \return /// The number locations resolved breakpoint sites. - size_t GetNumResolvedLocations() const; + size_t GetNumResolvedLocations(bool use_facade = true) const; /// Return whether this breakpoint has any resolved locations. /// @@ -421,9 +452,12 @@ class Breakpoint : public std::enable_shared_from_this, /// Return the number of breakpoint locations. /// + /// \param[in] use_facade + /// If \b true, then prefer facade locations over "real" ones if they exist. + /// /// \return /// The number breakpoint locations. - size_t GetNumLocations() const; + size_t GetNumLocations(bool use_facade = true) const; /// Put a description of this breakpoint into the stream \a s. /// @@ -529,6 +563,19 @@ class Breakpoint : public std::enable_shared_from_this, m_name_list.erase(name_to_remove); } + /// This controls whether to display information about + /// the facade locations or the real locations. + enum DisplayType { + eDisplayFacade = 1, // Display facade locations + eDisplayReal = 1 << 1, // Display real locations + eDisplayHeader = 1 << 2 // Display compressed list of locations only + }; + + void GetDescriptionForType(Stream *s, lldb::DescriptionLevel level, + uint8_t display_type, bool show_locations); + + bool HasFacadeLocations() { return m_facade_locations.GetSize() != 0; } + public: bool MatchesName(const char *name) { return m_name_list.find(name) != m_name_list.end(); @@ -657,6 +704,8 @@ class Breakpoint : public std::enable_shared_from_this, BreakpointOptions m_options; // Settable breakpoint options BreakpointLocationList m_locations; // The list of locations currently found for this breakpoint. + BreakpointLocationCollection m_facade_locations; + std::string m_kind_description; bool m_resolve_indirect_symbols; diff --git a/lldb/include/lldb/Breakpoint/BreakpointLocation.h b/lldb/include/lldb/Breakpoint/BreakpointLocation.h index ab2e5e170559d..c44ff471b8c4b 100644 --- a/lldb/include/lldb/Breakpoint/BreakpointLocation.h +++ b/lldb/include/lldb/Breakpoint/BreakpointLocation.h @@ -38,6 +38,12 @@ namespace lldb_private { class BreakpointLocation : public std::enable_shared_from_this { + friend class BreakpointSite; + friend class BreakpointLocationList; + friend class Breakpoint; + friend class Process; + friend class StopInfoBreakpoint; + public: ~BreakpointLocation(); @@ -55,16 +61,39 @@ class BreakpointLocation Target &GetTarget(); + /// This is a programmatic version of a breakpoint "condition". When a + /// breakpoint is hit, WasHit will get called before the synchronous + /// ShouldStop callback is run, and if it returns an empty + /// BreakpointLocationSP, lldb will act as if that breakpoint wasn't hit. + /// + /// \param[in] context + /// The context at the stop point + /// + /// \return + /// This will return the breakpoint location that was hit on this stop. + /// If there was no facade location this will be the original location. + /// If the shared pointer is empty, then we'll treat it as if the + /// breakpoint was not hit. + lldb::BreakpointLocationSP WasHit(StoppointCallbackContext *context); + /// Determines whether we should stop due to a hit at this breakpoint /// location. /// /// Side Effects: This may evaluate the breakpoint condition, and run the /// callback. So this command may do a considerable amount of work. /// + /// \param[in] context + /// The context at the stop point + /// + /// \param[out] facade_loc_sp + /// If this stop should be attributed not to the location that was hit, but + /// to a facade location, it will be returned in this facade_loc_sp. + /// /// \return /// \b true if this breakpoint location thinks we should stop, /// \b false otherwise. - bool ShouldStop(StoppointCallbackContext *context); + bool ShouldStop(StoppointCallbackContext *context, + lldb::BreakpointLocationSP &facade_loc_sp); // The next section deals with various breakpoint options. @@ -292,11 +321,6 @@ class BreakpointLocation } protected: - friend class BreakpointSite; - friend class BreakpointLocationList; - friend class Process; - friend class StopInfoBreakpoint; - /// Set the breakpoint site for this location to \a bp_site_sp. /// /// \param[in] bp_site_sp @@ -346,9 +370,11 @@ class BreakpointLocation // Constructors and Destructors // // Only the Breakpoint can make breakpoint locations, and it owns them. - /// Constructor. /// + /// \param[in] loc_id + /// The location id of the new location. + /// /// \param[in] owner /// A back pointer to the breakpoint that owns this location. /// @@ -359,37 +385,65 @@ class BreakpointLocation /// The thread for which this breakpoint location is valid, or /// LLDB_INVALID_THREAD_ID if it is valid for all threads. /// - BreakpointLocation(lldb::break_id_t bid, Breakpoint &owner, + BreakpointLocation(lldb::break_id_t loc_id, Breakpoint &owner, const Address &addr, lldb::tid_t tid, bool check_for_resolver = true); + /// This is the constructor for locations with no address. Currently this is + /// just used for Facade locations. + /// + /// \param[in] loc_id + /// The location id of the new location. + /// + /// \param[in] owner + /// A back pointer to the breakpoint that owns this location. + /// + /// +public: + BreakpointLocation(lldb::break_id_t loc_id, Breakpoint &owner); + bool IsValid() const { return m_is_valid; } + bool IsFacade() const { return m_is_facade; } + +private: // Data members: bool m_should_resolve_indirect_functions; bool m_is_reexported; bool m_is_indirect; - Address m_address; ///< The address defining this location. - Breakpoint &m_owner; ///< The breakpoint that produced this object. - std::unique_ptr m_options_up; ///< Breakpoint options - /// pointer, nullptr if we're - /// using our breakpoint's - /// options. - lldb::BreakpointSiteSP m_bp_site_sp; ///< Our breakpoint site (it may be - ///shared by more than one location.) - lldb::UserExpressionSP m_user_expression_sp; ///< The compiled expression to - ///use in testing our condition. - std::mutex m_condition_mutex; ///< Guards parsing and evaluation of the - ///condition, which could be evaluated by - /// multiple processes. - size_t m_condition_hash; ///< For testing whether the condition source code - ///changed. - lldb::break_id_t m_loc_id; ///< Breakpoint location ID. - StoppointHitCounter m_hit_counter; ///< Number of times this breakpoint - /// location has been hit. + ///< The address defining this location. + Address m_address; + ///< The breakpoint that produced this object. + Breakpoint &m_owner; + ///< Breakpoint options pointer, nullptr if we're using our breakpoint's + /// options. + std::unique_ptr m_options_up; + ///< Our breakpoint site (it may be shared by more than one location.) + lldb::BreakpointSiteSP m_bp_site_sp; + ///< The compiled expression to use in testing our condition. + lldb::UserExpressionSP m_user_expression_sp; + ///< Guards parsing and evaluation of the condition, which could be evaluated + /// by multiple processes. + std::mutex m_condition_mutex; + ///< For testing whether the condition source code changed. + size_t m_condition_hash; + ///< Breakpoint location ID. + lldb::break_id_t m_loc_id; + ///< Number of times this breakpoint location has been hit. + StoppointHitCounter m_hit_counter; /// If this exists, use it to print the stop description rather than the /// LineEntry m_address resolves to directly. Use this for instance when the /// location was given somewhere in the virtual inlined call stack since the /// Address always resolves to the lowest entry in the stack. std::optional m_preferred_line_entry; + /// Because Facade locations don't have sites we can't use the presence of + /// the site to mean this breakpoint is valid, but must manage the state + /// directly. + bool m_is_valid = true; + /// Facade locations aren't directly triggered and don't have a breakpoint + /// site. They are a useful fiction when you want to represent the stop + /// location as something lldb can't naturally stop at. + bool m_is_facade = false; + + void SetInvalid() { m_is_valid = false; } void SetShouldResolveIndirectFunctions(bool do_resolve) { m_should_resolve_indirect_functions = do_resolve; diff --git a/lldb/include/lldb/Breakpoint/BreakpointLocationCollection.h b/lldb/include/lldb/Breakpoint/BreakpointLocationCollection.h index 3aef1d658c0e5..124cb55eaf723 100644 --- a/lldb/include/lldb/Breakpoint/BreakpointLocationCollection.h +++ b/lldb/include/lldb/Breakpoint/BreakpointLocationCollection.h @@ -9,6 +9,7 @@ #ifndef LLDB_BREAKPOINT_BREAKPOINTLOCATIONCOLLECTION_H #define LLDB_BREAKPOINT_BREAKPOINTLOCATIONCOLLECTION_H +#include #include #include @@ -19,7 +20,15 @@ namespace lldb_private { class BreakpointLocationCollection { public: - BreakpointLocationCollection(); + /// Breakpoint locations don't keep their breakpoint owners alive, so neither + /// will a collection of breakpoint locations. However, if you need to + /// use this collection in a context where some of the breakpoints whose + /// locations are in the collection might get deleted during its lifespan, + /// then you need to make sure the breakpoints don't get deleted out from + /// under you. To do that, pass true for preserving, and so long as there is + /// a location for a given breakpoint in the collection, the breakpoint will + /// not get destroyed. + BreakpointLocationCollection(bool preserving = false); ~BreakpointLocationCollection(); @@ -111,7 +120,8 @@ class BreakpointLocationCollection { /// /// \return /// \b true if we should stop, \b false otherwise. - bool ShouldStop(StoppointCallbackContext *context); + bool ShouldStop(StoppointCallbackContext *context, + BreakpointLocationCollection &stopped_bp_locs); /// Print a description of the breakpoint locations in this list /// to the stream \a s. @@ -163,6 +173,10 @@ class BreakpointLocationCollection { collection m_break_loc_collection; mutable std::mutex m_collection_mutex; + /// These are used if we're preserving breakpoints in this list: + const bool m_preserving_bkpts = false; + std::map, lldb::BreakpointSP> + m_preserved_bps; public: typedef llvm::iterator_range @@ -171,7 +185,6 @@ class BreakpointLocationCollection { return BreakpointLocationCollectionIterable(m_break_loc_collection); } }; - } // namespace lldb_private #endif // LLDB_BREAKPOINT_BREAKPOINTLOCATIONCOLLECTION_H diff --git a/lldb/include/lldb/Breakpoint/BreakpointLocationList.h b/lldb/include/lldb/Breakpoint/BreakpointLocationList.h index 17dc0bfe03148..952db559d6cc7 100644 --- a/lldb/include/lldb/Breakpoint/BreakpointLocationList.h +++ b/lldb/include/lldb/Breakpoint/BreakpointLocationList.h @@ -140,7 +140,8 @@ class BreakpointLocationList { /// /// \return /// \b true if we should stop, \b false otherwise. - bool ShouldStop(StoppointCallbackContext *context, lldb::break_id_t breakID); + bool ShouldStop(StoppointCallbackContext *context, lldb::break_id_t breakID, + lldb::BreakpointLocationSP &bp_loc_sp); /// Returns the number of elements in this breakpoint location list. /// diff --git a/lldb/include/lldb/Breakpoint/BreakpointResolverScripted.h b/lldb/include/lldb/Breakpoint/BreakpointResolverScripted.h index 0322fd9f46ede..c3c1c80f49043 100644 --- a/lldb/include/lldb/Breakpoint/BreakpointResolverScripted.h +++ b/lldb/include/lldb/Breakpoint/BreakpointResolverScripted.h @@ -45,6 +45,13 @@ class BreakpointResolverScripted : public BreakpointResolver { void GetDescription(Stream *s) override; + lldb::BreakpointLocationSP WasHit(lldb::StackFrameSP frame_sp, + lldb::BreakpointLocationSP bp_loc_sp); + + std::optional + GetLocationDescription(lldb::BreakpointLocationSP bp_loc_sp, + lldb::DescriptionLevel level); + void Dump(Stream *s) const override; /// Methods for support type inquiry through isa, cast, and dyn_cast: diff --git a/lldb/include/lldb/Breakpoint/BreakpointSite.h b/lldb/include/lldb/Breakpoint/BreakpointSite.h index 7b3f7be23639f..a935b2441c02a 100644 --- a/lldb/include/lldb/Breakpoint/BreakpointSite.h +++ b/lldb/include/lldb/Breakpoint/BreakpointSite.h @@ -99,7 +99,8 @@ class BreakpointSite : public std::enable_shared_from_this, /// /// \return /// \b true if we should stop, \b false otherwise. - bool ShouldStop(StoppointCallbackContext *context) override; + bool ShouldStop(StoppointCallbackContext *context, + BreakpointLocationCollection &stopping_bp_loc) override; /// Standard Dump method void Dump(Stream *s) const override; diff --git a/lldb/include/lldb/Breakpoint/StopPointSiteList.h b/lldb/include/lldb/Breakpoint/StopPointSiteList.h index 7ed53e952dc8d..101eccda4616b 100644 --- a/lldb/include/lldb/Breakpoint/StopPointSiteList.h +++ b/lldb/include/lldb/Breakpoint/StopPointSiteList.h @@ -213,30 +213,6 @@ template class StopPointSiteList { typedef void (*StopPointSiteSPMapFunc)(StopPointSite &site, void *baton); - /// Enquires of the site on in this list with ID \a site_id - /// whether we should stop for the constituent or not. - /// - /// \param[in] context - /// This contains the information about this stop. - /// - /// \param[in] site_id - /// This site ID that we hit. - /// - /// \return - /// \b true if we should stop, \b false otherwise. - bool ShouldStop(StoppointCallbackContext *context, - typename StopPointSite::SiteID site_id) { - if (StopPointSiteSP site_sp = FindByID(site_id)) { - // Let the site decide if it should stop here (could not have - // reached it's target hit count yet, or it could have a callback that - // decided it shouldn't stop (shared library loads/unloads). - return site_sp->ShouldStop(context); - } - // We should stop here since this site isn't valid anymore or it - // doesn't exist. - return true; - } - /// Returns the number of elements in the list. /// /// \result diff --git a/lldb/include/lldb/Breakpoint/StoppointSite.h b/lldb/include/lldb/Breakpoint/StoppointSite.h index bef19f37908c6..2ceac403940ef 100644 --- a/lldb/include/lldb/Breakpoint/StoppointSite.h +++ b/lldb/include/lldb/Breakpoint/StoppointSite.h @@ -38,7 +38,12 @@ class StoppointSite { virtual bool IsHardware() const = 0; - virtual bool ShouldStop(StoppointCallbackContext* context) = 0; + virtual bool ShouldStop(StoppointCallbackContext *context) { return false; }; + + virtual bool ShouldStop(StoppointCallbackContext *context, + BreakpointLocationCollection &stopping_bp_locs) { + return false; + }; virtual void Dump(Stream* stream) const = 0; diff --git a/lldb/include/lldb/Interpreter/Interfaces/ScriptedBreakpointInterface.h b/lldb/include/lldb/Interpreter/Interfaces/ScriptedBreakpointInterface.h index 28d6ed992b141..d29fd8126e0cc 100644 --- a/lldb/include/lldb/Interpreter/Interfaces/ScriptedBreakpointInterface.h +++ b/lldb/include/lldb/Interpreter/Interfaces/ScriptedBreakpointInterface.h @@ -26,6 +26,16 @@ class ScriptedBreakpointInterface : public ScriptedInterface { virtual bool ResolverCallback(SymbolContext sym_ctx) { return true; } virtual lldb::SearchDepth GetDepth() { return lldb::eSearchDepthModule; } virtual std::optional GetShortHelp() { return nullptr; } + /// WasHit returns the breakpoint location SP for the location that was "hit". + virtual lldb::BreakpointLocationSP + WasHit(lldb::StackFrameSP frame_sp, lldb::BreakpointLocationSP bp_loc_sp) { + return LLDB_INVALID_BREAK_ID; + } + virtual std::optional + GetLocationDescription(lldb::BreakpointLocationSP bp_loc_sp, + lldb::DescriptionLevel level) { + return {}; + } }; } // namespace lldb_private diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreter.h b/lldb/include/lldb/Interpreter/ScriptInterpreter.h index dffb9b82abf3d..c5a67d1ac86a6 100644 --- a/lldb/include/lldb/Interpreter/ScriptInterpreter.h +++ b/lldb/include/lldb/Interpreter/ScriptInterpreter.h @@ -11,6 +11,7 @@ #include "lldb/API/SBAttachInfo.h" #include "lldb/API/SBBreakpoint.h" +#include "lldb/API/SBBreakpointLocation.h" #include "lldb/API/SBData.h" #include "lldb/API/SBError.h" #include "lldb/API/SBEvent.h" @@ -567,12 +568,17 @@ class ScriptInterpreter : public PluginInterface { lldb::StreamSP GetOpaqueTypeFromSBStream(const lldb::SBStream &stream) const; + lldb::StackFrameSP GetOpaqueTypeFromSBFrame(const lldb::SBFrame &frame) const; + SymbolContext GetOpaqueTypeFromSBSymbolContext(const lldb::SBSymbolContext &sym_ctx) const; lldb::BreakpointSP GetOpaqueTypeFromSBBreakpoint(const lldb::SBBreakpoint &breakpoint) const; + lldb::BreakpointLocationSP GetOpaqueTypeFromSBBreakpointLocation( + const lldb::SBBreakpointLocation &break_loc) const; + lldb::ProcessAttachInfoSP GetOpaqueTypeFromSBAttachInfo(const lldb::SBAttachInfo &attach_info) const; diff --git a/lldb/include/lldb/Target/StopInfo.h b/lldb/include/lldb/Target/StopInfo.h index f64a18a554914..43af03f221e9c 100644 --- a/lldb/include/lldb/Target/StopInfo.h +++ b/lldb/include/lldb/Target/StopInfo.h @@ -97,6 +97,12 @@ class StopInfo : public std::enable_shared_from_this { /// and silently continue again one more time. virtual bool WasContinueInterrupted(Thread &thread) { return false; } + virtual uint32_t GetStopReasonDataCount() const { return 0; } + virtual uint64_t GetStopReasonDataAtIndex(uint32_t idx) { + // Handle all the common cases that have no data. + return 0; + } + // Sometimes the thread plan logic will know that it wants a given stop to // stop or not, regardless of what the ordinary logic for that StopInfo would // dictate. The main example of this is the ThreadPlanCallFunction, which diff --git a/lldb/source/API/SBBreakpoint.cpp b/lldb/source/API/SBBreakpoint.cpp index 07c0a2ea907ba..646ad77b73977 100644 --- a/lldb/source/API/SBBreakpoint.cpp +++ b/lldb/source/API/SBBreakpoint.cpp @@ -567,6 +567,15 @@ SBError SBBreakpoint::AddLocation(SBAddress &address) { return error; } +SBBreakpointLocation SBBreakpoint::AddFacadeLocation() { + BreakpointSP bkpt_sp = GetSP(); + if (!bkpt_sp) + return {}; + + BreakpointLocationSP loc_sp = bkpt_sp->AddFacadeLocation(); + return SBBreakpointLocation(loc_sp); +} + SBStructuredData SBBreakpoint::SerializeToStructuredData() { LLDB_INSTRUMENT_VA(this); diff --git a/lldb/source/API/SBThread.cpp b/lldb/source/API/SBThread.cpp index 86519d18e9f70..be7fd4d8de62f 100644 --- a/lldb/source/API/SBThread.cpp +++ b/lldb/source/API/SBThread.cpp @@ -157,52 +157,8 @@ size_t SBThread::GetStopReasonDataCount() { if (exe_ctx) { if (exe_ctx->HasThreadScope()) { StopInfoSP stop_info_sp = exe_ctx->GetThreadPtr()->GetStopInfo(); - if (stop_info_sp) { - StopReason reason = stop_info_sp->GetStopReason(); - switch (reason) { - case eStopReasonInvalid: - case eStopReasonNone: - case eStopReasonTrace: - case eStopReasonExec: - case eStopReasonPlanComplete: - case eStopReasonThreadExiting: - case eStopReasonInstrumentation: - case eStopReasonProcessorTrace: - case eStopReasonVForkDone: - case eStopReasonHistoryBoundary: - // There is no data for these stop reasons. - return 0; - - case eStopReasonBreakpoint: { - break_id_t site_id = stop_info_sp->GetValue(); - lldb::BreakpointSiteSP bp_site_sp( - exe_ctx->GetProcessPtr()->GetBreakpointSiteList().FindByID( - site_id)); - if (bp_site_sp) - return bp_site_sp->GetNumberOfConstituents() * 2; - else - return 0; // Breakpoint must have cleared itself... - } break; - - case eStopReasonWatchpoint: - return 1; - - case eStopReasonSignal: - return 1; - - case eStopReasonInterrupt: - return 1; - - case eStopReasonException: - return 1; - - case eStopReasonFork: - return 1; - - case eStopReasonVFork: - return 1; - } - } + if (stop_info_sp) + return stop_info_sp->GetStopReasonDataCount(); } } else { LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); @@ -220,63 +176,8 @@ uint64_t SBThread::GetStopReasonDataAtIndex(uint32_t idx) { if (exe_ctx->HasThreadScope()) { Thread *thread = exe_ctx->GetThreadPtr(); StopInfoSP stop_info_sp = thread->GetStopInfo(); - if (stop_info_sp) { - StopReason reason = stop_info_sp->GetStopReason(); - switch (reason) { - case eStopReasonInvalid: - case eStopReasonNone: - case eStopReasonTrace: - case eStopReasonExec: - case eStopReasonPlanComplete: - case eStopReasonThreadExiting: - case eStopReasonInstrumentation: - case eStopReasonProcessorTrace: - case eStopReasonVForkDone: - case eStopReasonHistoryBoundary: - // There is no data for these stop reasons. - return 0; - - case eStopReasonBreakpoint: { - break_id_t site_id = stop_info_sp->GetValue(); - lldb::BreakpointSiteSP bp_site_sp( - exe_ctx->GetProcessPtr()->GetBreakpointSiteList().FindByID( - site_id)); - if (bp_site_sp) { - uint32_t bp_index = idx / 2; - BreakpointLocationSP bp_loc_sp( - bp_site_sp->GetConstituentAtIndex(bp_index)); - if (bp_loc_sp) { - if (idx & 1) { - // Odd idx, return the breakpoint location ID - return bp_loc_sp->GetID(); - } else { - // Even idx, return the breakpoint ID - return bp_loc_sp->GetBreakpoint().GetID(); - } - } - } - return LLDB_INVALID_BREAK_ID; - } break; - - case eStopReasonWatchpoint: - return stop_info_sp->GetValue(); - - case eStopReasonSignal: - return stop_info_sp->GetValue(); - - case eStopReasonInterrupt: - return stop_info_sp->GetValue(); - - case eStopReasonException: - return stop_info_sp->GetValue(); - - case eStopReasonFork: - return stop_info_sp->GetValue(); - - case eStopReasonVFork: - return stop_info_sp->GetValue(); - } - } + if (stop_info_sp) + return stop_info_sp->GetStopReasonDataAtIndex(idx); } } else { LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); diff --git a/lldb/source/Breakpoint/Breakpoint.cpp b/lldb/source/Breakpoint/Breakpoint.cpp index d757bc41cdc32..df9296417ced0 100644 --- a/lldb/source/Breakpoint/Breakpoint.cpp +++ b/lldb/source/Breakpoint/Breakpoint.cpp @@ -58,7 +58,13 @@ Breakpoint::Breakpoint(Target &new_target, const Breakpoint &source_bp) m_hit_counter() {} // Destructor -Breakpoint::~Breakpoint() = default; +Breakpoint::~Breakpoint() { + for (BreakpointLocationSP location_sp : m_locations.BreakpointLocations()) + location_sp->SetInvalid(); + for (BreakpointLocationSP location_sp : + m_facade_locations.BreakpointLocations()) + location_sp->SetInvalid(); +} BreakpointSP Breakpoint::CopyFromBreakpoint(TargetSP new_target, const Breakpoint &bp_to_copy_from) { @@ -303,6 +309,20 @@ BreakpointLocationSP Breakpoint::AddLocation(const Address &addr, new_location); } +BreakpointLocationSP Breakpoint::AddFacadeLocation() { + size_t next_id = m_facade_locations.GetSize() + 1; + BreakpointLocationSP break_loc_sp = + std::make_shared(next_id, *this); + break_loc_sp->m_is_facade = true; + m_facade_locations.Add(break_loc_sp); + return break_loc_sp; +} + +BreakpointLocationSP +Breakpoint::GetFacadeLocationByID(lldb::break_id_t loc_id) { + return m_facade_locations.GetByIndex(loc_id - 1); +} + BreakpointLocationSP Breakpoint::FindLocationByAddress(const Address &addr) { return m_locations.FindByAddress(addr); } @@ -311,15 +331,23 @@ break_id_t Breakpoint::FindLocationIDByAddress(const Address &addr) { return m_locations.FindIDByAddress(addr); } -BreakpointLocationSP Breakpoint::FindLocationByID(break_id_t bp_loc_id) { +BreakpointLocationSP Breakpoint::FindLocationByID(break_id_t bp_loc_id, + bool use_facade) { + if (use_facade && m_facade_locations.GetSize()) + return GetFacadeLocationByID(bp_loc_id); return m_locations.FindByID(bp_loc_id); } -BreakpointLocationSP Breakpoint::GetLocationAtIndex(size_t index) { +BreakpointLocationSP Breakpoint::GetLocationAtIndex(size_t index, + bool use_facade) { + if (use_facade && m_facade_locations.GetSize() > 0) + return m_facade_locations.GetByIndex(index); return m_locations.GetByIndex(index); } void Breakpoint::RemoveInvalidLocations(const ArchSpec &arch) { + // FIXME: Should we ask the scripted resolver whether any of its facade + // locations are invalid? m_locations.RemoveInvalidLocations(arch); } @@ -865,9 +893,15 @@ void Breakpoint::ModuleReplaced(ModuleSP old_module_sp, void Breakpoint::Dump(Stream *) {} -size_t Breakpoint::GetNumResolvedLocations() const { +size_t Breakpoint::GetNumResolvedLocations(bool use_facade) const { // Return the number of breakpoints that are actually resolved and set down // in the inferior process. + // All facade locations are considered to be resolved: + if (use_facade) { + size_t num_facade_locs = m_facade_locations.GetSize(); + if (num_facade_locs) + return num_facade_locs; + } return m_locations.GetNumResolvedLocations(); } @@ -875,7 +909,14 @@ bool Breakpoint::HasResolvedLocations() const { return GetNumResolvedLocations() > 0; } -size_t Breakpoint::GetNumLocations() const { return m_locations.GetSize(); } +size_t Breakpoint::GetNumLocations(bool use_facade) const { + if (use_facade) { + size_t num_facade_locs = m_facade_locations.GetSize(); + if (num_facade_locs > 0) + return num_facade_locs; + } + return m_locations.GetSize(); +} void Breakpoint::AddName(llvm::StringRef new_name) { m_name_list.insert(new_name.str()); @@ -900,8 +941,31 @@ void Breakpoint::GetDescription(Stream *s, lldb::DescriptionLevel level, s->Printf("Kind: %s\n", GetBreakpointKind()); } - const size_t num_locations = GetNumLocations(); - const size_t num_resolved_locations = GetNumResolvedLocations(); + bool show_both_types = level == eDescriptionLevelVerbose && + HasFacadeLocations() && show_locations; + uint8_t display_mask = eDisplayFacade; + if (show_both_types) + display_mask |= eDisplayHeader; + + GetDescriptionForType(s, level, display_mask, show_locations); + + if (show_both_types) { + display_mask = eDisplayReal | eDisplayHeader; + GetDescriptionForType(s, level, display_mask, show_locations); + } + // Reset the colors back to normal if they were previously greyed out. + if (dim_breakpoint_description) + s->Printf("%s", ansi::FormatAnsiTerminalCodes( + GetTarget().GetDebugger().GetDisabledAnsiSuffix()) + .c_str()); +} + +void Breakpoint::GetDescriptionForType(Stream *s, lldb::DescriptionLevel level, + uint8_t display_type, + bool show_locations) { + bool use_facade = (display_type & eDisplayFacade) != 0; + const size_t num_locations = GetNumLocations(use_facade); + const size_t num_resolved_locations = GetNumResolvedLocations(use_facade); // They just made the breakpoint, they don't need to be told HOW they made // it... Also, we'll print the breakpoint number differently depending on @@ -958,7 +1022,7 @@ void Breakpoint::GetDescription(Stream *s, lldb::DescriptionLevel level, } else if (num_locations == 1 && !show_locations) { // There is only one location, so we'll just print that location // information. - GetLocationAtIndex(0)->GetDescription(s, level); + GetLocationAtIndex(0, use_facade)->GetDescription(s, level); } else { s->Printf("%" PRIu64 " locations.", static_cast(num_locations)); } @@ -980,20 +1044,20 @@ void Breakpoint::GetDescription(Stream *s, lldb::DescriptionLevel level, // The brief description is just the location name (1.2 or whatever). That's // pointless to show in the breakpoint's description, so suppress it. if (show_locations && level != lldb::eDescriptionLevelBrief) { + if ((display_type & eDisplayHeader) != 0) { + if ((display_type & eDisplayFacade) != 0) + s->Printf("Facade locations:\n"); + else + s->Printf("Implementation Locations\n"); + } s->IndentMore(); for (size_t i = 0; i < num_locations; ++i) { - BreakpointLocation *loc = GetLocationAtIndex(i).get(); + BreakpointLocation *loc = GetLocationAtIndex(i, use_facade).get(); loc->GetDescription(s, level); s->EOL(); } s->IndentLess(); } - - // Reset the colors back to normal if they were previously greyed out. - if (dim_breakpoint_description) - s->Printf("%s", ansi::FormatAnsiTerminalCodes( - GetTarget().GetDebugger().GetDisabledAnsiSuffix()) - .c_str()); } void Breakpoint::GetResolverDescription(Stream *s) { diff --git a/lldb/source/Breakpoint/BreakpointLocation.cpp b/lldb/source/Breakpoint/BreakpointLocation.cpp index 443d4f50833d3..22c98acda8c59 100644 --- a/lldb/source/Breakpoint/BreakpointLocation.cpp +++ b/lldb/source/Breakpoint/BreakpointLocation.cpp @@ -8,6 +8,8 @@ #include "lldb/Breakpoint/BreakpointLocation.h" #include "lldb/Breakpoint/BreakpointID.h" +#include "lldb/Breakpoint/BreakpointResolver.h" +#include "lldb/Breakpoint/BreakpointResolverScripted.h" #include "lldb/Breakpoint/StoppointCallbackContext.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/Module.h" @@ -45,6 +47,13 @@ BreakpointLocation::BreakpointLocation(break_id_t loc_id, Breakpoint &owner, SetThreadIDInternal(tid); } +BreakpointLocation::BreakpointLocation(break_id_t loc_id, Breakpoint &owner) + : m_should_resolve_indirect_functions(false), m_is_reexported(false), + m_is_indirect(false), m_address(LLDB_INVALID_ADDRESS), m_owner(owner), + m_condition_hash(0), m_loc_id(loc_id), m_hit_counter() { + SetThreadIDInternal(LLDB_INVALID_THREAD_ID); +} + BreakpointLocation::~BreakpointLocation() { llvm::consumeError(ClearBreakpointSite()); } @@ -372,12 +381,43 @@ bool BreakpointLocation::ValidForThisThread(Thread &thread) { .GetThreadSpecNoCreate()); } +BreakpointLocationSP +BreakpointLocation::WasHit(StoppointCallbackContext *context) { + // Only the BreakpointResolverScripted provides WasHit. + BreakpointResolverSP resolver_sp = GetBreakpoint().GetResolver(); + BreakpointResolverScripted *scripted = + llvm::dyn_cast(resolver_sp.get()); + if (!scripted) + return shared_from_this(); + + StackFrameSP frame_sp = context->exe_ctx_ref.GetFrameSP(); + if (!frame_sp) + return shared_from_this(); + + BreakpointLocationSP return_loc_sp = + scripted->WasHit(frame_sp, shared_from_this()); + // If this is a facade location, then we won't have bumped its hit count + // while processing the original location hit. Do so here. We don't need + // to bump the breakpoint's hit count, however, since hitting the real + // location would have already done that. + // Also we have to check the enabled state here, since we would never have + // gotten here with a real location... + if (return_loc_sp && return_loc_sp->IsFacade()) { + if (return_loc_sp->IsEnabled()) + return_loc_sp->m_hit_counter.Increment(); + else + return {}; + } + return return_loc_sp; +} + // RETURNS - true if we should stop at this breakpoint, false if we // should continue. Note, we don't check the thread spec for the breakpoint // here, since if the breakpoint is not for this thread, then the event won't // even get reported, so the check is redundant. -bool BreakpointLocation::ShouldStop(StoppointCallbackContext *context) { +bool BreakpointLocation::ShouldStop(StoppointCallbackContext *context, + lldb::BreakpointLocationSP &facade_loc_sp) { bool should_stop = true; Log *log = GetLog(LLDBLog::Breakpoints); @@ -386,6 +426,27 @@ bool BreakpointLocation::ShouldStop(StoppointCallbackContext *context) { if (!IsEnabled()) return false; + // Next check WasHit: + BreakpointLocationSP loc_hit_sp = WasHit(context); + + if (!loc_hit_sp) { + // We bump the hit counts in StopInfoBreakpoint::ShouldStopSynchronous, + // before we call into each location's ShouldStop. So we need to undo + // that here. + UndoBumpHitCount(); + return false; + } + + // If the location hit was not us, it was a facade location, in which case + // we should use the facade location's callbacks, etc. Those will all be + // run in the asynchronous phase, so for now we just have to record the fact + // that we should treat this as a facade hit. This is strictly an out + // parameter, so clear it if this isn't a facade hit. + if (loc_hit_sp.get() != this) + facade_loc_sp = loc_hit_sp; + else + facade_loc_sp.reset(); + // We only run synchronous callbacks in ShouldStop: context->is_synchronous = true; should_stop = InvokeCallback(context); @@ -395,6 +456,11 @@ bool BreakpointLocation::ShouldStop(StoppointCallbackContext *context) { GetDescription(&s, lldb::eDescriptionLevelVerbose); LLDB_LOGF(log, "Hit breakpoint location: %s, %s.\n", s.GetData(), should_stop ? "stopping" : "continuing"); + if (facade_loc_sp) { + s.Clear(); + facade_loc_sp->GetDescription(&s, lldb::eDescriptionLevelVerbose); + LLDB_LOGF(log, "Attributing to facade location: %s.\n", s.GetData()); + } } return should_stop; @@ -417,7 +483,10 @@ void BreakpointLocation::UndoBumpHitCount() { } bool BreakpointLocation::IsResolved() const { - return m_bp_site_sp.get() != nullptr; + + bool has_site = m_bp_site_sp.get() != nullptr; + // Facade locations are currently always considered resolved. + return has_site || IsFacade(); } lldb::BreakpointSiteSP BreakpointLocation::GetBreakpointSite() const { @@ -425,7 +494,9 @@ lldb::BreakpointSiteSP BreakpointLocation::GetBreakpointSite() const { } llvm::Error BreakpointLocation::ResolveBreakpointSite() { - if (m_bp_site_sp) + // This might be a facade location, which doesn't have an address. + // In that case, don't attempt to make a site. + if (m_bp_site_sp || IsFacade()) return llvm::Error::success(); Process *process = m_owner.GetTarget().GetProcessSP().get(); @@ -454,8 +525,12 @@ bool BreakpointLocation::SetBreakpointSite(BreakpointSiteSP &bp_site_sp) { } llvm::Error BreakpointLocation::ClearBreakpointSite() { - if (!m_bp_site_sp) + if (!m_bp_site_sp) { + // This might be a Facade Location, which don't have sites or addresses + if (IsFacade()) + return llvm::Error::success(); return llvm::createStringError("no breakpoint site to clear"); + } // If the process exists, get it to remove the owner, it will remove the // physical implementation of the breakpoint as well if there are no more @@ -474,6 +549,17 @@ void BreakpointLocation::GetDescription(Stream *s, lldb::DescriptionLevel level) { SymbolContext sc; + // If this is a scripted breakpoint, give it a chance to describe its + // locations: + std::optional scripted_opt; + BreakpointResolverSP resolver_sp = GetBreakpoint().GetResolver(); + BreakpointResolverScripted *scripted = + llvm::dyn_cast(resolver_sp.get()); + if (scripted) + scripted_opt = scripted->GetLocationDescription(shared_from_this(), level); + + bool is_scripted_desc = scripted_opt.has_value(); + // If the description level is "initial" then the breakpoint is printing out // our initial state, and we should let it decide how it wants to print our // label. @@ -491,7 +577,9 @@ void BreakpointLocation::GetDescription(Stream *s, if (level == lldb::eDescriptionLevelVerbose) s->IndentMore(); - if (m_address.IsSectionOffset()) { + if (is_scripted_desc) { + s->PutCString(scripted_opt->c_str()); + } else if (m_address.IsSectionOffset()) { m_address.CalculateSymbolContext(&sc); if (level == lldb::eDescriptionLevelFull || @@ -566,43 +654,51 @@ void BreakpointLocation::GetDescription(Stream *s, s->Indent(); } - if (m_address.IsSectionOffset() && - (level == eDescriptionLevelFull || level == eDescriptionLevelInitial)) - s->Printf(", "); - s->Printf("address = "); - - ExecutionContextScope *exe_scope = nullptr; - Target *target = &m_owner.GetTarget(); - if (target) - exe_scope = target->GetProcessSP().get(); - if (exe_scope == nullptr) - exe_scope = target; - - if (level == eDescriptionLevelInitial) - m_address.Dump(s, exe_scope, Address::DumpStyleLoadAddress, - Address::DumpStyleFileAddress); - else - m_address.Dump(s, exe_scope, Address::DumpStyleLoadAddress, - Address::DumpStyleModuleWithFileAddress); - - if (IsIndirect() && m_bp_site_sp) { - Address resolved_address; - resolved_address.SetLoadAddress(m_bp_site_sp->GetLoadAddress(), target); - Symbol *resolved_symbol = resolved_address.CalculateSymbolContextSymbol(); - if (resolved_symbol) { - if (level == eDescriptionLevelFull || level == eDescriptionLevelInitial) - s->Printf(", "); - else if (level == lldb::eDescriptionLevelVerbose) { - s->EOL(); - s->Indent(); + if (!is_scripted_desc) { + if (m_address.IsSectionOffset() && + (level == eDescriptionLevelFull || level == eDescriptionLevelInitial)) + s->Printf(", "); + s->Printf("address = "); + + ExecutionContextScope *exe_scope = nullptr; + Target *target = &m_owner.GetTarget(); + if (target) + exe_scope = target->GetProcessSP().get(); + if (exe_scope == nullptr) + exe_scope = target; + + if (level == eDescriptionLevelInitial) + m_address.Dump(s, exe_scope, Address::DumpStyleLoadAddress, + Address::DumpStyleFileAddress); + else + m_address.Dump(s, exe_scope, Address::DumpStyleLoadAddress, + Address::DumpStyleModuleWithFileAddress); + + if (IsIndirect() && m_bp_site_sp) { + Address resolved_address; + resolved_address.SetLoadAddress(m_bp_site_sp->GetLoadAddress(), target); + Symbol *resolved_symbol = resolved_address.CalculateSymbolContextSymbol(); + if (resolved_symbol) { + if (level == eDescriptionLevelFull || level == eDescriptionLevelInitial) + s->Printf(", "); + else if (level == lldb::eDescriptionLevelVerbose) { + s->EOL(); + s->Indent(); + } + s->Printf("indirect target = %s", + resolved_symbol->GetName().GetCString()); } - s->Printf("indirect target = %s", - resolved_symbol->GetName().GetCString()); } } - bool is_resolved = IsResolved(); - bool is_hardware = is_resolved && m_bp_site_sp->IsHardware(); + // FIXME: scripted breakpoint are currently always resolved. Does this seem + // right? If they don't add any scripted locations, we shouldn't consider them + // resolved. + bool is_resolved = is_scripted_desc || IsResolved(); + // A scripted breakpoint might be resolved but not have a site. Be sure to + // check for that. + bool is_hardware = !is_scripted_desc && IsResolved() && m_bp_site_sp && + m_bp_site_sp->IsHardware(); if (level == lldb::eDescriptionLevelVerbose) { s->EOL(); @@ -717,9 +813,9 @@ void BreakpointLocation::SwapLocation(BreakpointLocationSP swap_from) { } void BreakpointLocation::SetThreadIDInternal(lldb::tid_t thread_id) { - if (thread_id != LLDB_INVALID_THREAD_ID) + if (thread_id != LLDB_INVALID_THREAD_ID) { GetLocationOptions().SetThreadID(thread_id); - else { + } else { // If we're resetting this to an invalid thread id, then don't make an // options pointer just to do that. if (m_options_up != nullptr) diff --git a/lldb/source/Breakpoint/BreakpointLocationCollection.cpp b/lldb/source/Breakpoint/BreakpointLocationCollection.cpp index 81bec0bd7583d..97715836ec104 100644 --- a/lldb/source/Breakpoint/BreakpointLocationCollection.cpp +++ b/lldb/source/Breakpoint/BreakpointLocationCollection.cpp @@ -17,7 +17,8 @@ using namespace lldb; using namespace lldb_private; // BreakpointLocationCollection constructor -BreakpointLocationCollection::BreakpointLocationCollection() = default; +BreakpointLocationCollection::BreakpointLocationCollection(bool preserving) + : m_preserving_bkpts(preserving) {} // Destructor BreakpointLocationCollection::~BreakpointLocationCollection() = default; @@ -26,8 +27,19 @@ void BreakpointLocationCollection::Add(const BreakpointLocationSP &bp_loc) { std::lock_guard guard(m_collection_mutex); BreakpointLocationSP old_bp_loc = FindByIDPair(bp_loc->GetBreakpoint().GetID(), bp_loc->GetID()); - if (!old_bp_loc.get()) + if (!old_bp_loc.get()) { m_break_loc_collection.push_back(bp_loc); + if (m_preserving_bkpts) { + lldb::break_id_t bp_loc_id = bp_loc->GetID(); + Breakpoint &bkpt = bp_loc->GetBreakpoint(); + lldb::break_id_t bp_id = bkpt.GetID(); + std::pair key = + std::make_pair(bp_id, bp_loc_id); + auto entry = m_preserved_bps.find(key); + if (entry == m_preserved_bps.end()) + m_preserved_bps.emplace(key, bkpt.shared_from_this()); + } + } } bool BreakpointLocationCollection::Remove(lldb::break_id_t bp_id, @@ -35,6 +47,15 @@ bool BreakpointLocationCollection::Remove(lldb::break_id_t bp_id, std::lock_guard guard(m_collection_mutex); collection::iterator pos = GetIDPairIterator(bp_id, bp_loc_id); // Predicate if (pos != m_break_loc_collection.end()) { + if (m_preserving_bkpts) { + std::pair key = + std::make_pair(bp_id, bp_loc_id); + auto entry = m_preserved_bps.find(key); + if (entry == m_preserved_bps.end()) + assert(0 && "Breakpoint added to collection but not preserving map."); + else + m_preserved_bps.erase(entry); + } m_break_loc_collection.erase(pos); return true; } @@ -115,7 +136,8 @@ BreakpointLocationCollection::GetByIndex(size_t i) const { } bool BreakpointLocationCollection::ShouldStop( - StoppointCallbackContext *context) { + StoppointCallbackContext *context, + BreakpointLocationCollection &stopped_bp_locs) { bool shouldStop = false; size_t i = 0; size_t prev_size = GetSize(); @@ -123,9 +145,20 @@ bool BreakpointLocationCollection::ShouldStop( // ShouldStop can remove the breakpoint from the list, or even delete // it, so we should BreakpointLocationSP cur_loc_sp = GetByIndex(i); + BreakpointLocationSP reported_loc_sp; BreakpointSP keep_bkpt_alive_sp = cur_loc_sp->GetBreakpoint().shared_from_this(); - if (cur_loc_sp->ShouldStop(context)) + // We're building up the list or which locations claim responsibility for + // this stop. If the location's ShouldStop defers to a facade location by + // returning a non-null reported location, we want to use that. Otherwise + // use the original location. + if (cur_loc_sp->ShouldStop(context, reported_loc_sp)) { + if (reported_loc_sp) + stopped_bp_locs.Add(reported_loc_sp); + else + stopped_bp_locs.Add(cur_loc_sp); + shouldStop = true; + } if (prev_size == GetSize()) i++; diff --git a/lldb/source/Breakpoint/BreakpointLocationList.cpp b/lldb/source/Breakpoint/BreakpointLocationList.cpp index 44d1eb5bf7140..ea431aa3bb953 100644 --- a/lldb/source/Breakpoint/BreakpointLocationList.cpp +++ b/lldb/source/Breakpoint/BreakpointLocationList.cpp @@ -41,13 +41,14 @@ BreakpointLocationList::Create(const Address &addr, } bool BreakpointLocationList::ShouldStop(StoppointCallbackContext *context, - lldb::break_id_t break_id) { + lldb::break_id_t break_id, + lldb::BreakpointLocationSP &bp_loc_sp) { BreakpointLocationSP bp = FindByID(break_id); if (bp) { // Let the BreakpointLocation decide if it should stop here (could not have // reached it's target hit count yet, or it could have a callback that // decided it shouldn't stop (shared library loads/unloads). - return bp->ShouldStop(context); + return bp->ShouldStop(context, bp_loc_sp); } // We should stop here since this BreakpointLocation isn't valid anymore or // it doesn't exist. diff --git a/lldb/source/Breakpoint/BreakpointResolverScripted.cpp b/lldb/source/Breakpoint/BreakpointResolverScripted.cpp index 701cabae3ee97..373bd74d24b67 100644 --- a/lldb/source/Breakpoint/BreakpointResolverScripted.cpp +++ b/lldb/source/Breakpoint/BreakpointResolverScripted.cpp @@ -50,7 +50,9 @@ void BreakpointResolverScripted::CreateImplementationIfNeeded( if (!script_interp) return; - m_interface_sp = script_interp->CreateScriptedBreakpointInterface(); + if (!m_interface_sp) + m_interface_sp = script_interp->CreateScriptedBreakpointInterface(); + if (!m_interface_sp) { m_error = Status::FromErrorStringWithFormat( "BreakpointResolverScripted::%s () - ERROR: %s", __FUNCTION__, @@ -61,6 +63,7 @@ void BreakpointResolverScripted::CreateImplementationIfNeeded( auto obj_or_err = m_interface_sp->CreatePluginObject(m_class_name, breakpoint_sp, m_args); if (!obj_or_err) { + m_interface_sp.reset(); m_error = Status::FromError(obj_or_err.takeError()); return; } @@ -146,6 +149,8 @@ void BreakpointResolverScripted::GetDescription(Stream *s) { StructuredData::GenericSP generic_sp; std::optional short_help; + CreateImplementationIfNeeded(GetBreakpoint()); + if (m_interface_sp) { short_help = m_interface_sp->GetShortHelp(); } @@ -155,6 +160,22 @@ void BreakpointResolverScripted::GetDescription(Stream *s) { s->Printf("python class = %s", m_class_name.c_str()); } +std::optional BreakpointResolverScripted::GetLocationDescription( + lldb::BreakpointLocationSP bp_loc_sp, lldb::DescriptionLevel level) { + CreateImplementationIfNeeded(GetBreakpoint()); + if (m_interface_sp) + return m_interface_sp->GetLocationDescription(bp_loc_sp, level); + return {}; +} + +lldb::BreakpointLocationSP +BreakpointResolverScripted::WasHit(lldb::StackFrameSP frame_sp, + lldb::BreakpointLocationSP bp_loc_sp) { + if (m_interface_sp) + return m_interface_sp->WasHit(frame_sp, bp_loc_sp); + return {}; +} + void BreakpointResolverScripted::Dump(Stream *s) const {} lldb::BreakpointResolverSP diff --git a/lldb/source/Breakpoint/BreakpointSite.cpp b/lldb/source/Breakpoint/BreakpointSite.cpp index d430e3de788f0..fd7666be6b1bf 100644 --- a/lldb/source/Breakpoint/BreakpointSite.cpp +++ b/lldb/source/Breakpoint/BreakpointSite.cpp @@ -45,7 +45,9 @@ break_id_t BreakpointSite::GetNextID() { // RETURNS - true if we should stop at this breakpoint, false if we // should continue. -bool BreakpointSite::ShouldStop(StoppointCallbackContext *context) { +bool BreakpointSite::ShouldStop( + StoppointCallbackContext *context, + BreakpointLocationCollection &stopping_bp_locs) { m_hit_counter.Increment(); // ShouldStop can do a lot of work, and might even come back and hit // this breakpoint site again. So don't hold the m_constituents_mutex the @@ -56,7 +58,7 @@ bool BreakpointSite::ShouldStop(StoppointCallbackContext *context) { std::lock_guard guard(m_constituents_mutex); constituents_copy = m_constituents; } - return constituents_copy.ShouldStop(context); + return constituents_copy.ShouldStop(context, stopping_bp_locs); } bool BreakpointSite::IsBreakpointAtThisSite(lldb::break_id_t bp_id) { diff --git a/lldb/source/Interpreter/ScriptInterpreter.cpp b/lldb/source/Interpreter/ScriptInterpreter.cpp index 5ee19ffda0a0b..5c0e71389ca6c 100644 --- a/lldb/source/Interpreter/ScriptInterpreter.cpp +++ b/lldb/source/Interpreter/ScriptInterpreter.cpp @@ -81,6 +81,12 @@ lldb::BreakpointSP ScriptInterpreter::GetOpaqueTypeFromSBBreakpoint( return breakpoint.m_opaque_wp.lock(); } +lldb::BreakpointLocationSP +ScriptInterpreter::GetOpaqueTypeFromSBBreakpointLocation( + const lldb::SBBreakpointLocation &break_loc) const { + return break_loc.m_opaque_wp.lock(); +} + lldb::ProcessAttachInfoSP ScriptInterpreter::GetOpaqueTypeFromSBAttachInfo( const lldb::SBAttachInfo &attach_info) const { return attach_info.m_opaque_sp; @@ -100,6 +106,13 @@ ScriptInterpreter::GetStatusFromSBError(const lldb::SBError &error) const { return Status(); } +lldb::StackFrameSP +ScriptInterpreter::GetOpaqueTypeFromSBFrame(const lldb::SBFrame &frame) const { + if (frame.m_opaque_sp) + return frame.m_opaque_sp->GetFrameSP(); + return nullptr; +} + Event * ScriptInterpreter::GetOpaqueTypeFromSBEvent(const lldb::SBEvent &event) const { return event.m_opaque_ptr; diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedBreakpointPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedBreakpointPythonInterface.cpp index 660edaa0191f0..c9bb38d213c99 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedBreakpointPythonInterface.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedBreakpointPythonInterface.cpp @@ -81,6 +81,32 @@ std::optional ScriptedBreakpointPythonInterface::GetShortHelp() { return obj->GetAsString()->GetValue().str(); } +lldb::BreakpointLocationSP ScriptedBreakpointPythonInterface::WasHit( + lldb::StackFrameSP frame_sp, lldb::BreakpointLocationSP bp_loc_sp) { + Status py_error; + lldb::BreakpointLocationSP loc_sp = Dispatch( + "was_hit", py_error, frame_sp, bp_loc_sp); + + if (py_error.Fail()) + return bp_loc_sp; + + return loc_sp; +} + +std::optional +ScriptedBreakpointPythonInterface::GetLocationDescription( + lldb::BreakpointLocationSP bp_loc_sp, lldb::DescriptionLevel level) { + Status error; + StructuredData::ObjectSP obj = + Dispatch("get_location_description", error, bp_loc_sp, level); + + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, + error)) + return {}; + + return obj->GetAsString()->GetValue().str(); +} + void ScriptedBreakpointPythonInterface::Initialize() { const std::vector ci_usages = { "breakpoint set -P classname [-k key -v value ...]"}; diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedBreakpointPythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedBreakpointPythonInterface.h index 27bdd8718ac0c..72da0a195ee01 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedBreakpointPythonInterface.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedBreakpointPythonInterface.h @@ -36,6 +36,12 @@ class ScriptedBreakpointPythonInterface : public ScriptedBreakpointInterface, bool ResolverCallback(SymbolContext sym_ctx) override; lldb::SearchDepth GetDepth() override; std::optional GetShortHelp() override; + lldb::BreakpointLocationSP + WasHit(lldb::StackFrameSP frame_sp, + lldb::BreakpointLocationSP bp_loc_sp) override; + virtual std::optional + GetLocationDescription(lldb::BreakpointLocationSP bp_loc_sp, + lldb::DescriptionLevel level) override; static void Initialize(); diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp index b49d1d82fe535..2625244f7d60b 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp @@ -80,6 +80,19 @@ ScriptedPythonInterface::ExtractValueFromPythonObject( return nullptr; } +template <> +lldb::StackFrameSP +ScriptedPythonInterface::ExtractValueFromPythonObject( + python::PythonObject &p, Status &error) { + if (lldb::SBFrame *sb_frame = reinterpret_cast( + python::LLDBSWIGPython_CastPyObjectToSBFrame(p.get()))) + return m_interpreter.GetOpaqueTypeFromSBFrame(*sb_frame); + error = Status::FromErrorString( + "Couldn't cast lldb::SBFrame to lldb_private::StackFrame."); + + return nullptr; +} + template <> SymbolContext ScriptedPythonInterface::ExtractValueFromPythonObject( @@ -126,6 +139,24 @@ ScriptedPythonInterface::ExtractValueFromPythonObject( return m_interpreter.GetOpaqueTypeFromSBBreakpoint(*sb_breakpoint); } +template <> +lldb::BreakpointLocationSP +ScriptedPythonInterface::ExtractValueFromPythonObject< + lldb::BreakpointLocationSP>(python::PythonObject &p, Status &error) { + lldb::SBBreakpointLocation *sb_break_loc = + reinterpret_cast( + python::LLDBSWIGPython_CastPyObjectToSBBreakpointLocation(p.get())); + + if (!sb_break_loc) { + error = Status::FromErrorStringWithFormat( + "Couldn't cast lldb::SBBreakpointLocation to " + "lldb::BreakpointLocationSP."); + return nullptr; + } + + return m_interpreter.GetOpaqueTypeFromSBBreakpointLocation(*sb_break_loc); +} + template <> lldb::ProcessAttachInfoSP ScriptedPythonInterface::ExtractValueFromPythonObject< lldb::ProcessAttachInfoSP>(python::PythonObject &p, Status &error) { @@ -193,4 +224,22 @@ ScriptedPythonInterface::ExtractValueFromPythonObject< return m_interpreter.GetOpaqueTypeFromSBExecutionContext(*sb_exe_ctx); } +template <> +lldb::DescriptionLevel +ScriptedPythonInterface::ExtractValueFromPythonObject( + python::PythonObject &p, Status &error) { + lldb::DescriptionLevel ret_val = lldb::eDescriptionLevelBrief; + llvm::Expected unsigned_or_err = p.AsUnsignedLongLong(); + if (!unsigned_or_err) { + error = (Status::FromError(unsigned_or_err.takeError())); + return ret_val; + } + unsigned long long unsigned_val = *unsigned_or_err; + if (unsigned_val >= lldb::DescriptionLevel::kNumDescriptionLevels) { + error = Status("value too large for lldb::DescriptionLevel."); + return ret_val; + } + return static_cast(unsigned_val); +} + #endif diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h index f769d3d29add7..2335b2ef0f171 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h @@ -436,6 +436,10 @@ class ScriptedPythonInterface : virtual public ScriptedInterface { return python::SWIGBridge::ToSWIGWrapper(arg); } + python::PythonObject Transform(lldb::BreakpointLocationSP arg) { + return python::SWIGBridge::ToSWIGWrapper(arg); + } + python::PythonObject Transform(lldb::ProcessSP arg) { return python::SWIGBridge::ToSWIGWrapper(arg); } @@ -464,10 +468,18 @@ class ScriptedPythonInterface : virtual public ScriptedInterface { return python::SWIGBridge::ToSWIGWrapper(arg.get()); } + python::PythonObject Transform(lldb::StackFrameSP arg) { + return python::SWIGBridge::ToSWIGWrapper(arg); + } + python::PythonObject Transform(lldb::DataExtractorSP arg) { return python::SWIGBridge::ToSWIGWrapper(arg); } + python::PythonObject Transform(lldb::DescriptionLevel arg) { + return python::SWIGBridge::ToSWIGWrapper(arg); + } + template void ReverseTransform(T &original_arg, U transformed_arg, Status &error) { // If U is not a PythonObject, don't touch it! @@ -573,11 +585,21 @@ lldb::StreamSP ScriptedPythonInterface::ExtractValueFromPythonObject( python::PythonObject &p, Status &error); +template <> +lldb::StackFrameSP +ScriptedPythonInterface::ExtractValueFromPythonObject( + python::PythonObject &p, Status &error); + template <> lldb::BreakpointSP ScriptedPythonInterface::ExtractValueFromPythonObject( python::PythonObject &p, Status &error); +template <> +lldb::BreakpointLocationSP +ScriptedPythonInterface::ExtractValueFromPythonObject< + lldb::BreakpointLocationSP>(python::PythonObject &p, Status &error); + template <> lldb::ProcessAttachInfoSP ScriptedPythonInterface::ExtractValueFromPythonObject< lldb::ProcessAttachInfoSP>(python::PythonObject &p, Status &error); @@ -601,6 +623,11 @@ lldb::ExecutionContextRefSP ScriptedPythonInterface::ExtractValueFromPythonObject< lldb::ExecutionContextRefSP>(python::PythonObject &p, Status &error); +template <> +lldb::DescriptionLevel +ScriptedPythonInterface::ExtractValueFromPythonObject( + python::PythonObject &p, Status &error); + } // namespace lldb_private #endif // LLDB_ENABLE_PYTHON diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h index 413778639c297..7b39d29ba2b20 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h @@ -107,6 +107,7 @@ class SWIGBridge { static PythonObject ToSWIGWrapper(lldb::ProcessAttachInfoSP attach_info_sp); static PythonObject ToSWIGWrapper(lldb::ProcessLaunchInfoSP launch_info_sp); static PythonObject ToSWIGWrapper(lldb::DataExtractorSP data_extractor_sp); + static PythonObject ToSWIGWrapper(lldb::DescriptionLevel level); static PythonObject ToSWIGWrapper(std::unique_ptr data_sb); @@ -256,11 +257,13 @@ class SWIGBridge { void *LLDBSWIGPython_CastPyObjectToSBData(PyObject *data); void *LLDBSWIGPython_CastPyObjectToSBBreakpoint(PyObject *data); +void *LLDBSWIGPython_CastPyObjectToSBBreakpointLocation(PyObject *data); void *LLDBSWIGPython_CastPyObjectToSBAttachInfo(PyObject *data); void *LLDBSWIGPython_CastPyObjectToSBLaunchInfo(PyObject *data); void *LLDBSWIGPython_CastPyObjectToSBError(PyObject *data); void *LLDBSWIGPython_CastPyObjectToSBEvent(PyObject *data); void *LLDBSWIGPython_CastPyObjectToSBStream(PyObject *data); +void *LLDBSWIGPython_CastPyObjectToSBFrame(PyObject *data); void *LLDBSWIGPython_CastPyObjectToSBSymbolContext(PyObject *data); void *LLDBSWIGPython_CastPyObjectToSBValue(PyObject *data); void *LLDBSWIGPython_CastPyObjectToSBMemoryRegionInfo(PyObject *data); diff --git a/lldb/source/Target/StopInfo.cpp b/lldb/source/Target/StopInfo.cpp index 384c050fadb00..3b6d6fb8c79c5 100644 --- a/lldb/source/Target/StopInfo.cpp +++ b/lldb/source/Target/StopInfo.cpp @@ -87,11 +87,15 @@ bool StopInfo::HasTargetRunSinceMe() { namespace lldb_private { class StopInfoBreakpoint : public StopInfo { public: + // We use a "breakpoint preserving BreakpointLocationCollection because we + // may need to hand out the "breakpoint hit" list as any point, potentially + // after the breakpoint has been deleted. But we still need to refer to them. StopInfoBreakpoint(Thread &thread, break_id_t break_id) : StopInfo(thread, break_id), m_should_stop(false), m_should_stop_is_valid(false), m_should_perform_action(true), m_address(LLDB_INVALID_ADDRESS), m_break_id(LLDB_INVALID_BREAK_ID), - m_was_all_internal(false), m_was_one_shot(false) { + m_was_all_internal(false), m_was_one_shot(false), + m_async_stopped_locs(true) { StoreBPInfo(); } @@ -99,7 +103,8 @@ class StopInfoBreakpoint : public StopInfo { : StopInfo(thread, break_id), m_should_stop(should_stop), m_should_stop_is_valid(true), m_should_perform_action(true), m_address(LLDB_INVALID_ADDRESS), m_break_id(LLDB_INVALID_BREAK_ID), - m_was_all_internal(false), m_was_one_shot(false) { + m_was_all_internal(false), m_was_one_shot(false), + m_async_stopped_locs(true) { StoreBPInfo(); } @@ -108,8 +113,7 @@ class StopInfoBreakpoint : public StopInfo { void StoreBPInfo() { ThreadSP thread_sp(m_thread_wp.lock()); if (thread_sp) { - BreakpointSiteSP bp_site_sp( - thread_sp->GetProcess()->GetBreakpointSiteList().FindByID(m_value)); + BreakpointSiteSP bp_site_sp = GetBreakpointSiteSP(); if (bp_site_sp) { uint32_t num_constituents = bp_site_sp->GetNumberOfConstituents(); if (num_constituents == 1) { @@ -139,8 +143,7 @@ class StopInfoBreakpoint : public StopInfo { bool IsValidForOperatingSystemThread(Thread &thread) override { ProcessSP process_sp(thread.GetProcess()); if (process_sp) { - BreakpointSiteSP bp_site_sp( - process_sp->GetBreakpointSiteList().FindByID(m_value)); + BreakpointSiteSP bp_site_sp = GetBreakpointSiteSP(); if (bp_site_sp) return bp_site_sp->ValidForThisThread(thread); } @@ -154,13 +157,13 @@ class StopInfoBreakpoint : public StopInfo { if (thread_sp) { if (!m_should_stop_is_valid) { // Only check once if we should stop at a breakpoint - BreakpointSiteSP bp_site_sp( - thread_sp->GetProcess()->GetBreakpointSiteList().FindByID(m_value)); + BreakpointSiteSP bp_site_sp = GetBreakpointSiteSP(); if (bp_site_sp) { ExecutionContext exe_ctx(thread_sp->GetStackFrameAtIndex(0)); StoppointCallbackContext context(event_ptr, exe_ctx, true); bp_site_sp->BumpHitCounts(); - m_should_stop = bp_site_sp->ShouldStop(&context); + m_should_stop = + bp_site_sp->ShouldStop(&context, m_async_stopped_locs); } else { Log *log = GetLog(LLDBLog::Process); @@ -183,11 +186,11 @@ class StopInfoBreakpoint : public StopInfo { } const char *GetDescription() override { + // FIXME: only print m_async_stopped_locs. if (m_description.empty()) { ThreadSP thread_sp(m_thread_wp.lock()); if (thread_sp) { - BreakpointSiteSP bp_site_sp( - thread_sp->GetProcess()->GetBreakpointSiteList().FindByID(m_value)); + BreakpointSiteSP bp_site_sp = GetBreakpointSiteSP(); if (bp_site_sp) { StreamString strm; // If we have just hit an internal breakpoint, and it has a kind @@ -206,7 +209,7 @@ class StopInfoBreakpoint : public StopInfo { } strm.Printf("breakpoint "); - bp_site_sp->GetDescription(&strm, eDescriptionLevelBrief); + m_async_stopped_locs.GetDescription(&strm, eDescriptionLevelBrief); m_description = std::string(strm.GetString()); } else { StreamString strm; @@ -247,6 +250,44 @@ class StopInfoBreakpoint : public StopInfo { return m_description.c_str(); } + uint32_t GetStopReasonDataCount() const override { + size_t num_async_locs = m_async_stopped_locs.GetSize(); + // If we have async locations, they are the ones we should report: + if (num_async_locs > 0) + return num_async_locs * 2; + + // Otherwise report the number of locations at this breakpoint's site. + lldb::BreakpointSiteSP bp_site_sp = GetBreakpointSiteSP(); + if (bp_site_sp) + return bp_site_sp->GetNumberOfConstituents() * 2; + return 0; // Breakpoint must have cleared itself... + } + + uint64_t GetStopReasonDataAtIndex(uint32_t idx) override { + uint32_t bp_index = idx / 2; + BreakpointLocationSP loc_to_report_sp; + + size_t num_async_locs = m_async_stopped_locs.GetSize(); + if (num_async_locs > 0) { + // GetByIndex returns an empty SP if we ask past its contents: + loc_to_report_sp = m_async_stopped_locs.GetByIndex(bp_index); + } else { + lldb::BreakpointSiteSP bp_site_sp = GetBreakpointSiteSP(); + if (bp_site_sp) + loc_to_report_sp = bp_site_sp->GetConstituentAtIndex(bp_index); + } + if (loc_to_report_sp) { + if (idx & 1) { + // Odd idx, return the breakpoint location ID + return loc_to_report_sp->GetID(); + } else { + // Even idx, return the breakpoint ID + return loc_to_report_sp->GetBreakpoint().GetID(); + } + } + return LLDB_INVALID_BREAK_ID; + } + std::optional GetSuggestedStackFrameIndex(bool inlined_stack) override { if (!inlined_stack) @@ -255,8 +296,7 @@ class StopInfoBreakpoint : public StopInfo { ThreadSP thread_sp(m_thread_wp.lock()); if (!thread_sp) return {}; - BreakpointSiteSP bp_site_sp( - thread_sp->GetProcess()->GetBreakpointSiteList().FindByID(m_value)); + BreakpointSiteSP bp_site_sp = GetBreakpointSiteSP(); if (!bp_site_sp) return {}; @@ -297,8 +337,7 @@ class StopInfoBreakpoint : public StopInfo { return; } - BreakpointSiteSP bp_site_sp( - thread_sp->GetProcess()->GetBreakpointSiteList().FindByID(m_value)); + BreakpointSiteSP bp_site_sp = GetBreakpointSiteSP(); std::unordered_set precondition_breakpoints; // Breakpoints that fail their condition check are not considered to // have been hit. If the only locations at this site have failed their @@ -312,8 +351,7 @@ class StopInfoBreakpoint : public StopInfo { // local list. That way if one of the breakpoint actions changes the // site, then we won't be operating on a bad list. BreakpointLocationCollection site_locations; - size_t num_constituents = - bp_site_sp->CopyConstituentsList(site_locations); + size_t num_constituents = m_async_stopped_locs.GetSize(); if (num_constituents == 0) { m_should_stop = true; @@ -413,16 +451,26 @@ class StopInfoBreakpoint : public StopInfo { // I'm just sticking the BreakpointSP's in a vector since I'm only // using it to locally increment their retain counts. + // We are holding onto the breakpoint locations that were hit + // by this stop info between the "synchonous" ShouldStop and now. + // But an intervening action might have deleted one of the breakpoints + // we hit before we get here. So at the same time let's build a list + // of the still valid locations: std::vector location_constituents; + BreakpointLocationCollection valid_locs; for (size_t j = 0; j < num_constituents; j++) { - BreakpointLocationSP loc(site_locations.GetByIndex(j)); - location_constituents.push_back( - loc->GetBreakpoint().shared_from_this()); + BreakpointLocationSP loc_sp(m_async_stopped_locs.GetByIndex(j)); + if (loc_sp->IsValid()) { + location_constituents.push_back( + loc_sp->GetBreakpoint().shared_from_this()); + valid_locs.Add(loc_sp); + } } - for (size_t j = 0; j < num_constituents; j++) { - lldb::BreakpointLocationSP bp_loc_sp = site_locations.GetByIndex(j); + size_t num_valid_locs = valid_locs.GetSize(); + for (size_t j = 0; j < num_valid_locs; j++) { + lldb::BreakpointLocationSP bp_loc_sp = valid_locs.GetByIndex(j); StreamString loc_desc; if (log) { bp_loc_sp->GetDescription(&loc_desc, eDescriptionLevelBrief); @@ -629,6 +677,20 @@ class StopInfoBreakpoint : public StopInfo { } private: + BreakpointSiteSP GetBreakpointSiteSP() const { + if (m_value == LLDB_INVALID_BREAK_ID) + return {}; + + ThreadSP thread_sp = GetThread(); + if (!thread_sp) + return {}; + ProcessSP process_sp = thread_sp->GetProcess(); + if (!process_sp) + return {}; + + return process_sp->GetBreakpointSiteList().FindByID(m_value); + } + bool m_should_stop; bool m_should_stop_is_valid; bool m_should_perform_action; // Since we are trying to preserve the "state" @@ -642,6 +704,11 @@ class StopInfoBreakpoint : public StopInfo { lldb::break_id_t m_break_id; bool m_was_all_internal; bool m_was_one_shot; + /// The StopInfoBreakpoint lives after the stop, and could get queried + /// at any time so we need to make sure that it keeps the breakpoints for + /// each of the locations it records alive while it is around. That's what + /// The BreakpointPreservingLocationCollection does. + BreakpointLocationCollection m_async_stopped_locs; }; // StopInfoWatchpoint @@ -699,6 +766,13 @@ class StopInfoWatchpoint : public StopInfo { StopReason GetStopReason() const override { return eStopReasonWatchpoint; } + uint32_t GetStopReasonDataCount() const override { return 1; } + uint64_t GetStopReasonDataAtIndex(uint32_t idx) override { + if (idx == 0) + return GetValue(); + return 0; + } + const char *GetDescription() override { if (m_description.empty()) { StreamString strm; @@ -1138,6 +1212,13 @@ class StopInfoUnixSignal : public StopInfo { bool ShouldSelect() const override { return IsShouldStopSignal(); } + uint32_t GetStopReasonDataCount() const override { return 1; } + uint64_t GetStopReasonDataAtIndex(uint32_t idx) override { + if (idx == 0) + return GetValue(); + return 0; + } + private: // In siginfo_t terms, if m_value is si_signo, m_code is si_code. std::optional m_code; @@ -1170,6 +1251,14 @@ class StopInfoInterrupt : public StopInfo { } return m_description.c_str(); } + + uint32_t GetStopReasonDataCount() const override { return 1; } + uint64_t GetStopReasonDataAtIndex(uint32_t idx) override { + if (idx == 0) + return GetValue(); + else + return 0; + } }; // StopInfoTrace @@ -1248,6 +1337,13 @@ class StopInfoException : public StopInfo { else return m_description.c_str(); } + uint32_t GetStopReasonDataCount() const override { return 1; } + uint64_t GetStopReasonDataAtIndex(uint32_t idx) override { + if (idx == 0) + return GetValue(); + else + return 0; + } }; // StopInfoProcessorTrace @@ -1395,6 +1491,14 @@ class StopInfoFork : public StopInfo { const char *GetDescription() override { return "fork"; } + uint32_t GetStopReasonDataCount() const override { return 1; } + uint64_t GetStopReasonDataAtIndex(uint32_t idx) override { + if (idx == 0) + return GetValue(); + else + return 0; + } + protected: void PerformAction(Event *event_ptr) override { // Only perform the action once @@ -1429,6 +1533,13 @@ class StopInfoVFork : public StopInfo { const char *GetDescription() override { return "vfork"; } + uint32_t GetStopReasonDataCount() const override { return 1; } + uint64_t GetStopReasonDataAtIndex(uint32_t idx) override { + if (idx == 0) + return GetValue(); + return 0; + } + protected: void PerformAction(Event *event_ptr) override { // Only perform the action once diff --git a/lldb/test/API/functionalities/breakpoint/callback_deletes_breakpoints/Makefile b/lldb/test/API/functionalities/breakpoint/callback_deletes_breakpoints/Makefile new file mode 100644 index 0000000000000..695335e068c0c --- /dev/null +++ b/lldb/test/API/functionalities/breakpoint/callback_deletes_breakpoints/Makefile @@ -0,0 +1,4 @@ +C_SOURCES := main.c +CFLAGS_EXTRAS := -std=c99 + +include Makefile.rules diff --git a/lldb/test/API/functionalities/breakpoint/callback_deletes_breakpoints/TestCallbackDeletesBreakpoints.py b/lldb/test/API/functionalities/breakpoint/callback_deletes_breakpoints/TestCallbackDeletesBreakpoints.py new file mode 100644 index 0000000000000..2b8fc662ad42e --- /dev/null +++ b/lldb/test/API/functionalities/breakpoint/callback_deletes_breakpoints/TestCallbackDeletesBreakpoints.py @@ -0,0 +1,67 @@ +""" +Make sure that deleting breakpoints in another breakpoint +callback doesn't cause problems. +""" + + +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + + +class TestBreakpointDeletionInCallback(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + def test_breakpoint_deletion_in_callback(self): + self.build() + self.main_source_file = lldb.SBFileSpec("main.c") + self.delete_others_test() + + def delete_others_test(self): + """You might use the test implementation in several ways, say so here.""" + + # This function starts a process, "a.out" by default, sets a source + # breakpoint, runs to it, and returns the thread, process & target. + # It optionally takes an SBLaunchOption argument if you want to pass + # arguments or environment variables. + (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( + self, "Set a breakpoint here", self.main_source_file + ) + + # Now set a breakpoint on "I did something" several times + # + bkpt_numbers = [] + for idx in range(0, 5): + bkpt_numbers.append( + lldbutil.run_break_set_by_source_regexp(self, "// Deletable location") + ) + + # And add commands to the third one to delete two others: + deleter = target.FindBreakpointByID(bkpt_numbers[2]) + self.assertTrue(deleter.IsValid(), "Deleter is a good breakpoint") + commands = lldb.SBStringList() + deleted_ids = [bkpt_numbers[0], bkpt_numbers[3]] + for idx in deleted_ids: + commands.AppendString(f"break delete {idx}") + + deleter.SetCommandLineCommands(commands) + + thread_list = lldbutil.continue_to_breakpoint(process, deleter) + self.assertEqual(len(thread_list), 1) + stop_data = thread.stop_reason_data + # There are 5 breakpoints so 10 break_id, break_loc_id. + self.assertEqual(len(stop_data), 10) + # We should have been able to get break ID's and locations for all the + # breakpoints that we originally hit, but some won't be around anymore: + for idx in range(0, 5): + bkpt_id = stop_data[idx * 2] + print(f"{idx}: {bkpt_id}") + self.assertIn(bkpt_id, bkpt_numbers, "Found breakpoints are right") + loc_id = stop_data[idx * 2 + 1] + self.assertEqual(loc_id, 1, "All breakpoints have one location") + bkpt = target.FindBreakpointByID(bkpt_id) + if bkpt_id in deleted_ids: + # Looking these up should be an error: + self.assertFalse(bkpt.IsValid(), "Deleted breakpoints are deleted") + else: + self.assertTrue(bkpt.IsValid(), "The rest are still valid") diff --git a/lldb/test/API/functionalities/breakpoint/callback_deletes_breakpoints/main.c b/lldb/test/API/functionalities/breakpoint/callback_deletes_breakpoints/main.c new file mode 100644 index 0000000000000..2ffb897b2f92d --- /dev/null +++ b/lldb/test/API/functionalities/breakpoint/callback_deletes_breakpoints/main.c @@ -0,0 +1,12 @@ +#include + +int do_something(int input) { + return input % 5; // Deletable location +} + +int main() { + printf("Set a breakpoint here.\n"); + do_something(100); + do_something(200); + return 0; +} diff --git a/lldb/test/API/functionalities/breakpoint/scripted_bkpt/resolver.py b/lldb/test/API/functionalities/breakpoint/scripted_bkpt/resolver.py index 85c7340127615..95868486b8cba 100644 --- a/lldb/test/API/functionalities/breakpoint/scripted_bkpt/resolver.py +++ b/lldb/test/API/functionalities/breakpoint/scripted_bkpt/resolver.py @@ -51,7 +51,6 @@ def __callback__(self, sym_ctx): def get_short_help(self): return "I am a python breakpoint resolver" - class ResolverModuleDepth(Resolver): def __get_depth__(self): return lldb.eSearchDepthModule diff --git a/lldb/test/API/functionalities/breakpoint/scripted_bkpt/was_hit/Makefile b/lldb/test/API/functionalities/breakpoint/scripted_bkpt/was_hit/Makefile new file mode 100644 index 0000000000000..695335e068c0c --- /dev/null +++ b/lldb/test/API/functionalities/breakpoint/scripted_bkpt/was_hit/Makefile @@ -0,0 +1,4 @@ +C_SOURCES := main.c +CFLAGS_EXTRAS := -std=c99 + +include Makefile.rules diff --git a/lldb/test/API/functionalities/breakpoint/scripted_bkpt/was_hit/TestWasHit.py b/lldb/test/API/functionalities/breakpoint/scripted_bkpt/was_hit/TestWasHit.py new file mode 100644 index 0000000000000..2e176239facf0 --- /dev/null +++ b/lldb/test/API/functionalities/breakpoint/scripted_bkpt/was_hit/TestWasHit.py @@ -0,0 +1,102 @@ +""" +Test the WasHit feature of scripted breakpoints +""" + +import os +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * + + +class TestWasHit(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24528") + def test_was_hit_resolver(self): + """Use facade breakpoints to emulate hitting some locations""" + self.build() + self.do_test() + + def make_target_and_import(self): + target = lldbutil.run_to_breakpoint_make_target(self) + self.import_resolver_script() + return target + + def import_resolver_script(self): + interp = self.dbg.GetCommandInterpreter() + error = lldb.SBError() + + script_name = os.path.join(self.getSourceDir(), "bkpt_resolver.py") + + command = "command script import " + script_name + self.runCmd(command) + + def make_extra_args(self, sym_name, num_locs, loc_to_miss): + return f" -k symbol -v {sym_name} -k num_locs -v {num_locs} -k loc_to_miss -v {loc_to_miss} " + + def do_test(self): + """This reads in a python file and sets a breakpoint using it.""" + + target = self.make_target_and_import() + extra_args = self.make_extra_args("stop_symbol", 4, 2) + + bkpt_no = lldbutil.run_break_set_by_script( + self, "bkpt_resolver.FacadeExample", extra_args, 4 + ) + + # Make sure the help text shows up in the "break list" output: + self.expect( + "break list", + substrs=["I am a facade resolver - sym: stop_symbol - num_locs: 4"], + msg="Help is listed in break list", + ) + + bkpt = target.FindBreakpointByID(bkpt_no) + self.assertTrue(bkpt.IsValid(), "Found the right breakpoint") + + # Now continue. We should hit locations 1, 3 and 4: + (target, process, thread, bkpt) = lldbutil.run_to_breakpoint_do_run( + self, target, bkpt + ) + # This location should be bkpt_no.1: + self.assertEqual( + thread.stop_reason_data[0], bkpt_no, "Hit the right breakpoint" + ) + self.assertEqual(thread.stop_reason_data[1], 1, "First location hit is 1") + + for loc in [3, 4]: + process.Continue() + self.assertEqual( + thread.stop_reason, lldb.eStopReasonBreakpoint, "Hit breakpoint" + ) + self.assertEqual( + thread.stop_reason_data[0], bkpt_no, "Hit the right breakpoint" + ) + self.assertEqual( + thread.stop_reason_data[1], loc, f"Hit the right location: {loc}" + ) + + # At this point we should have hit three of the four locations, and not location 1.2. + # Check that that is true, and that the descriptions for the location are the ones + # the resolver provided. + self.assertEqual(bkpt.hit_count, 3, "Hit three locations") + for loc_id in range(1, 4): + bkpt_loc = bkpt.FindLocationByID(loc_id) + self.assertTrue(bkpt_loc.IsValid(), f"{loc_id} was invalid.") + if loc_id != 2: + self.assertEqual( + bkpt_loc.hit_count, 1, f"Loc {loc_id} hit count was wrong" + ) + else: + self.assertEqual(bkpt_loc.hit_count, 0, "We didn't skip loc 2") + stream = lldb.SBStream() + self.assertTrue( + bkpt_loc.GetDescription(stream, lldb.eDescriptionLevelFull), + f"Didn't get description for {loc_id}", + ) + self.assertIn( + f"Location index: {loc_id}", + stream.GetData(), + f"Wrong desciption for {loc_id}", + ) diff --git a/lldb/test/API/functionalities/breakpoint/scripted_bkpt/was_hit/bkpt_resolver.py b/lldb/test/API/functionalities/breakpoint/scripted_bkpt/was_hit/bkpt_resolver.py new file mode 100644 index 0000000000000..acc8513e3e3e0 --- /dev/null +++ b/lldb/test/API/functionalities/breakpoint/scripted_bkpt/was_hit/bkpt_resolver.py @@ -0,0 +1,49 @@ +import lldb + + +class FacadeExample: + def __init__(self, bkpt, extra_args, dict): + self.bkpt = bkpt + self.extra_args = extra_args + self.base_sym = None + self.facade_locs = [] + self.facade_locs_desc = [] + self.cur_facade_loc = 1 + + self.sym_name = extra_args.GetValueForKey("symbol").GetStringValue(100) + self.num_locs = extra_args.GetValueForKey("num_locs").GetIntegerValue(5) + self.loc_to_miss = extra_args.GetValueForKey("loc_to_miss").GetIntegerValue( + 10000 + ) + + def __callback__(self, sym_ctx): + self.base_sym = sym_ctx.module.FindSymbol(self.sym_name, lldb.eSymbolTypeCode) + if self.base_sym.IsValid(): + self.bkpt.AddLocation(self.base_sym.GetStartAddress()) + # Locations are 1 based, so to keep things simple, I'm making + # the array holding locations 1 based as well: + self.facade_locs_desc.append( + "This is the zero index, you shouldn't see this" + ) + self.facade_locs.append(None) + for i in range(1, self.num_locs + 1): + self.facade_locs_desc.append(f"Location index: {i}") + self.facade_locs.append(self.bkpt.AddFacadeLocation()) + + def get_short_help(self): + return f"I am a facade resolver - sym: {self.sym_name} - num_locs: {self.num_locs} - locs_to_miss: {self.loc_to_miss}" + + def was_hit(self, frame, bp_loc): + tmp_loc = self.cur_facade_loc + + self.cur_facade_loc = self.cur_facade_loc + 1 + if self.cur_facade_loc == self.num_locs + 1: + self.cur_facade_loc = 1 + + if tmp_loc == self.loc_to_miss: + return None + + return self.facade_locs[tmp_loc] + + def get_location_description(self, bp_loc, desc_level): + return self.facade_locs_desc[bp_loc.id] diff --git a/lldb/test/API/functionalities/breakpoint/scripted_bkpt/was_hit/main.c b/lldb/test/API/functionalities/breakpoint/scripted_bkpt/was_hit/main.c new file mode 100644 index 0000000000000..b8f977e493513 --- /dev/null +++ b/lldb/test/API/functionalities/breakpoint/scripted_bkpt/was_hit/main.c @@ -0,0 +1,14 @@ +#include + +int stop_symbol() { + static int s_cnt = 0; + printf("I am in the stop symbol: %d\n", s_cnt++); + return s_cnt; +} + +int main() { + for (int i = 0; i < 100; i++) { + stop_symbol(); + } + return 0; +} diff --git a/lldb/test/API/functionalities/postmortem/elf-core/gcore/TestGCore.py b/lldb/test/API/functionalities/postmortem/elf-core/gcore/TestGCore.py index 020a226924ea6..497b8e8a19a86 100644 --- a/lldb/test/API/functionalities/postmortem/elf-core/gcore/TestGCore.py +++ b/lldb/test/API/functionalities/postmortem/elf-core/gcore/TestGCore.py @@ -37,7 +37,7 @@ def do_test(self, filename, pid): for thread in process: reason = thread.GetStopReason() self.assertStopReason(reason, lldb.eStopReasonSignal) - signal = thread.GetStopReasonDataAtIndex(1) + signal = thread.GetStopReasonDataAtIndex(0) # Check we got signal 19 (SIGSTOP) self.assertEqual(signal, 19) diff --git a/lldb/test/API/functionalities/postmortem/elf-core/thread_crash/TestLinuxCoreThreads.py b/lldb/test/API/functionalities/postmortem/elf-core/thread_crash/TestLinuxCoreThreads.py index 4a848d1c2eb9e..6d9aef286a796 100644 --- a/lldb/test/API/functionalities/postmortem/elf-core/thread_crash/TestLinuxCoreThreads.py +++ b/lldb/test/API/functionalities/postmortem/elf-core/thread_crash/TestLinuxCoreThreads.py @@ -91,7 +91,7 @@ def do_test(self, filename, pid, tid): reason = thread.GetStopReason() if thread.GetThreadID() == tid: self.assertStopReason(reason, lldb.eStopReasonSignal) - signal = thread.GetStopReasonDataAtIndex(1) + signal = thread.GetStopReasonDataAtIndex(0) # Check we got signal 4 (SIGILL) self.assertEqual(signal, 4) else: diff --git a/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp b/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp index fbb005b3f0fd3..f4b5a810f9e30 100644 --- a/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp +++ b/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp @@ -105,6 +105,11 @@ void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBBreakpoint( return nullptr; } +void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBBreakpointLocation( + PyObject *data) { + return nullptr; +} + void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBAttachInfo( PyObject *data) { return nullptr; @@ -130,6 +135,11 @@ lldb_private::python::LLDBSWIGPython_CastPyObjectToSBStream(PyObject *data) { return nullptr; } +void * +lldb_private::python::LLDBSWIGPython_CastPyObjectToSBFrame(PyObject *data) { + return nullptr; +} + void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBSymbolContext( PyObject *data) { return nullptr;