[Lldb-commits] [lldb] Add `SBModule.SetLocateDwoCallback` (PR #69517)
via lldb-commits
lldb-commits at lists.llvm.org
Wed Oct 18 13:51:44 PDT 2023
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-lldb
Author: Tom Yang (zhyty)
<details>
<summary>Changes</summary>
Add a way to set a *static* callback to locate DWO files in `SBModule`.
```
SBError SBLocateDwoCallback(void *baton, const SBFileSpec &objfile_spec, const char *dwo_name, const char *comp_dir, const int64_t dwo_id, SBFileSpec &located_dwo_file_spec);
```
where `located_dwo_file_spec` is the output of the function.
This is more powerful than using a list of search directories via `target.debug-file-search-paths`; for example, we could write a callback that traverses up the object file path and searches for some expected folder name. The callback supplements the existing search paths for dwo files. If there is no callback or the callback fails, we fall back to other ways of finding dwos.
On `Python`, the callback arguments are just `objfile_spec`, `dwo_name`, `comp_dir`, `dwo_id`, and `located_dwo_file_spec`, no `baton`. See `TestLocateDwoCallback.py` for examples.
To implement this, I pretty religiously followed the pattern set by Kazuki in his OSS RFC
https://discourse.llvm.org/t/rfc-python-callback-for-target-get-module/71580.
# Testing
Tested manually, and with
```
ninja check-lldb-shell-symbolfile-dwarf
```
and
```
lldb-dotest -p TestLocateDwoCallback
```
---
Full diff: https://github.com/llvm/llvm-project/pull/69517.diff
15 Files Affected:
- (modified) lldb/bindings/python/python-typemaps.swig (+52)
- (modified) lldb/bindings/python/python-wrapper.swig (+54)
- (modified) lldb/include/lldb/API/SBDefines.h (+7)
- (modified) lldb/include/lldb/API/SBError.h (+1)
- (modified) lldb/include/lldb/API/SBModule.h (+14)
- (modified) lldb/include/lldb/Core/ModuleList.h (+1)
- (modified) lldb/include/lldb/Symbol/SymbolFile.h (+11)
- (modified) lldb/source/API/SBModule.cpp (+27)
- (modified) lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp (+26-9)
- (modified) lldb/source/Symbol/SymbolFile.cpp (+9)
- (added) lldb/test/API/python_api/sbmodule/locate_dwo_callback/Makefile (+4)
- (added) lldb/test/API/python_api/sbmodule/locate_dwo_callback/TestLocateDwoCallback.py (+119)
- (added) lldb/test/API/python_api/sbmodule/locate_dwo_callback/foo.cpp (+3)
- (added) lldb/test/API/python_api/sbmodule/locate_dwo_callback/foo.h (+6)
- (added) lldb/test/API/python_api/sbmodule/locate_dwo_callback/main.cpp (+3)
``````````diff
diff --git a/lldb/bindings/python/python-typemaps.swig b/lldb/bindings/python/python-typemaps.swig
index 7660e0282c8fcf4..9749452c6e52359 100644
--- a/lldb/bindings/python/python-typemaps.swig
+++ b/lldb/bindings/python/python-typemaps.swig
@@ -696,3 +696,55 @@ template <> bool SetNumberFromPyObject<double>(double &number, PyObject *obj) {
$1 = $input == Py_None;
$1 = $1 || PyCallable_Check(reinterpret_cast<PyObject *>($input));
}
+
+// For lldb::SBModuleLocateDwoCallback
+// The `baton` is the actual Python function passed, and we invoke the `baton` via a SWIG function.
+%typemap(in) (lldb::SBModuleLocateDwoCallback callback,
+ void *baton) {
+ if (!($input == Py_None ||
+ PyCallable_Check(reinterpret_cast<PyObject *>($input)))) {
+ PyErr_SetString(PyExc_TypeError, "Need a callable object or None!");
+ SWIG_fail;
+ }
+
+ if ($input == Py_None) {
+ $1 = nullptr;
+ $2 = nullptr;
+ } else {
+ PythonCallable callable = Retain<PythonCallable>($input);
+ if (!callable.IsValid()) {
+ PyErr_SetString(PyExc_TypeError, "Need a valid callable object");
+ SWIG_fail;
+ }
+
+ llvm::Expected<PythonCallable::ArgInfo> arg_info = callable.GetArgInfo();
+ if (!arg_info) {
+ PyErr_SetString(PyExc_TypeError,
+ ("Could not get arguments: " +
+ llvm::toString(arg_info.takeError())).c_str());
+ SWIG_fail;
+ }
+
+ if (arg_info.get().max_positional_args != 5) {
+ PyErr_SetString(PyExc_TypeError, "Expected 5 argument callable object");
+ SWIG_fail;
+ }
+
+ // NOTE: When this is called multiple times, this will leak the Python
+ // callable object as other callbacks, because this does not call Py_DECREF
+ // the object. But it should be almost zero impact since this method is
+ // expected to be called only once.
+
+ // Don't lose the callback reference
+ Py_INCREF($input);
+
+ $1 = LLDBSwigPythonCallLocateDwoCallback;
+ $2 = $input;
+ }
+}
+
+%typemap(typecheck) (lldb::SBModuleLocateDwoCallback callback,
+ void *baton) {
+ $1 = $input == Py_None;
+ $1 = $1 || PyCallable_Check(reinterpret_cast<PyObject *>($input));
+}
diff --git a/lldb/bindings/python/python-wrapper.swig b/lldb/bindings/python/python-wrapper.swig
index cb54901e66d03c6..1b8e545beba7b43 100644
--- a/lldb/bindings/python/python-wrapper.swig
+++ b/lldb/bindings/python/python-wrapper.swig
@@ -1168,4 +1168,58 @@ static SBError LLDBSwigPythonCallLocateModuleCallback(
return *sb_error_ptr;
}
+
+// `comp_dir` is allowed to be NULL. All other arguments must be valid values.
+static SBError LLDBSwigPythonCallLocateDwoCallback(
+ void *baton, const SBFileSpec &objfile_spec_sb,
+ const char *dwo_name, const char *comp_dir, const int64_t dwo_id, SBFileSpec &located_dwo_file_spec_sb) {
+ SWIG_Python_Thread_Block swig_thread_block;
+
+ PyErr_Cleaner py_err_cleaner(true);
+ if (dwo_name == NULL) {
+ return SBError("`dwo_name` is NULL. Expected a valid string.");
+ }
+ PythonString dwo_name_arg(dwo_name);
+ PythonObject comp_dir_arg(PyRefType::Borrowed, Py_None);
+ if (comp_dir != NULL) {
+ comp_dir_arg = PythonString(comp_dir);
+ }
+ PythonObject dwo_id_arg = PythonInteger(dwo_id);
+ PythonObject objfile_spec_arg = SWIGBridge::ToSWIGWrapper(
+ std::make_unique<SBFileSpec>(objfile_spec_sb));
+ PythonObject located_dwo_file_spec_arg = SWIGBridge::ToSWIGWrapper(
+ std::make_unique<SBFileSpec>(located_dwo_file_spec_sb));
+
+ PythonCallable callable =
+ Retain<PythonCallable>(reinterpret_cast<PyObject *>(baton));
+ if (!callable.IsValid()) {
+ return SBError("The callback callable is not valid.");
+ }
+
+ PythonObject result = callable(objfile_spec_arg,
+ dwo_name_arg,
+ comp_dir_arg,
+ dwo_id_arg,
+ located_dwo_file_spec_arg);
+
+ if (!result.IsAllocated())
+ return SBError("No result.");
+ lldb::SBError *sb_error_ptr = nullptr;
+ if (SWIG_ConvertPtr(result.get(), (void **)&sb_error_ptr,
+ SWIGTYPE_p_lldb__SBError, 0) == -1) {
+ return SBError("Result is not SBError.");
+ }
+
+ if (sb_error_ptr->Success()) {
+ lldb::SBFileSpec *located_dwo_file_spec_ptr = nullptr;
+ if (SWIG_ConvertPtr(located_dwo_file_spec_arg.get(),
+ (void **)&located_dwo_file_spec_ptr,
+ SWIGTYPE_p_lldb__SBFileSpec, 0) == -1)
+ return SBError("located_dwo_file_spec is not SBFileSpec.");
+
+ located_dwo_file_spec_sb = *located_dwo_file_spec_ptr;
+ }
+
+ return *sb_error_ptr;
+}
%}
diff --git a/lldb/include/lldb/API/SBDefines.h b/lldb/include/lldb/API/SBDefines.h
index c6f01cc03f263c8..c4691cff47d45c9 100644
--- a/lldb/include/lldb/API/SBDefines.h
+++ b/lldb/include/lldb/API/SBDefines.h
@@ -139,6 +139,13 @@ typedef void (*SBDebuggerDestroyCallback)(lldb::user_id_t debugger_id,
typedef SBError (*SBPlatformLocateModuleCallback)(
void *baton, const SBModuleSpec &module_spec, SBFileSpec &module_file_spec,
SBFileSpec &symbol_file_spec);
+
+typedef SBError (*SBModuleLocateDwoCallback)(void *baton,
+ const SBFileSpec &objfile_spec,
+ const char *dwo_name,
+ const char *comp_dir,
+ const int64_t dwo_id,
+ SBFileSpec &located_dwo_file_spec);
}
#endif // LLDB_API_SBDEFINES_H
diff --git a/lldb/include/lldb/API/SBError.h b/lldb/include/lldb/API/SBError.h
index b241052ed9cc2a2..0e62eed8878586b 100644
--- a/lldb/include/lldb/API/SBError.h
+++ b/lldb/include/lldb/API/SBError.h
@@ -91,6 +91,7 @@ class LLDB_API SBError {
friend class SBValue;
friend class SBValueList;
friend class SBWatchpoint;
+ friend class SBModule;
friend class lldb_private::ScriptInterpreter;
friend class lldb_private::python::SWIGBridge;
diff --git a/lldb/include/lldb/API/SBModule.h b/lldb/include/lldb/API/SBModule.h
index 7200a1ef53fd827..193662f8c3a1821 100644
--- a/lldb/include/lldb/API/SBModule.h
+++ b/lldb/include/lldb/API/SBModule.h
@@ -296,6 +296,20 @@ class LLDB_API SBModule {
/// Remove any global modules which are no longer needed.
static void GarbageCollectAllocatedModules();
+ /// Set a callback which is called to find separate DWARF DWO debug info
+ /// files.
+ ///
+ /// This is useful when DWO files have been moved, or when the symbol file
+ /// contains relative paths to the DWO files.
+ ///
+ /// If the callback fails to
+ /// find the DWO file (`located_dwo_file_spec` is unset), then LLDB will fall
+ /// back to the default search locations. The callback will be cleared if
+ /// `nullptr` is set.
+ /// `comp_dir` may be `nullptr`.
+ static void SetLocateDwoCallback(lldb::SBModuleLocateDwoCallback callback,
+ void *baton);
+
private:
friend class SBAddress;
friend class SBFrame;
diff --git a/lldb/include/lldb/Core/ModuleList.h b/lldb/include/lldb/Core/ModuleList.h
index 9826dd09e91d714..a3b1d4807f0514b 100644
--- a/lldb/include/lldb/Core/ModuleList.h
+++ b/lldb/include/lldb/Core/ModuleList.h
@@ -68,6 +68,7 @@ class ModuleListProperties : public Properties {
uint64_t GetLLDBIndexCacheExpirationDays();
FileSpec GetLLDBIndexCachePath() const;
bool SetLLDBIndexCachePath(const FileSpec &path);
+ FileSpec GetLocateDwoScriptPath() const;
bool GetLoadSymbolOnDemand();
diff --git a/lldb/include/lldb/Symbol/SymbolFile.h b/lldb/include/lldb/Symbol/SymbolFile.h
index 512dd9acb86db61..9dde6ea040fc6cb 100644
--- a/lldb/include/lldb/Symbol/SymbolFile.h
+++ b/lldb/include/lldb/Symbol/SymbolFile.h
@@ -77,6 +77,15 @@ class SymbolFile : public PluginInterface {
static SymbolFile *FindPlugin(lldb::ObjectFileSP objfile_sp);
+ typedef std::function<Status(
+ const FileSpec &objfile_spec, const char *dwo_name, const char *comp_dir,
+ const uint64_t dwo_id, FileSpec &located_dwo_file_spec)>
+ LocateDwoCallback;
+
+ static void SetLocateDwoCallback(LocateDwoCallback callback);
+
+ static LocateDwoCallback GetLocateDwoCallback();
+
// Constructors and Destructors
SymbolFile() = default;
@@ -475,6 +484,8 @@ class SymbolFile : public PluginInterface {
private:
SymbolFile(const SymbolFile &) = delete;
const SymbolFile &operator=(const SymbolFile &) = delete;
+
+ static LocateDwoCallback LOCATE_DWO_CALLBACK;
};
/// Containing protected virtual methods for child classes to override.
diff --git a/lldb/source/API/SBModule.cpp b/lldb/source/API/SBModule.cpp
index b865502228e0a15..c0d5db531fa3cc6 100644
--- a/lldb/source/API/SBModule.cpp
+++ b/lldb/source/API/SBModule.cpp
@@ -677,3 +677,30 @@ void SBModule::GarbageCollectAllocatedModules() {
const bool mandatory = false;
ModuleList::RemoveOrphanSharedModules(mandatory);
}
+
+void SBModule::SetLocateDwoCallback(lldb::SBModuleLocateDwoCallback callback,
+ void *baton) {
+ LLDB_INSTRUMENT();
+
+ if (!callback) {
+ SymbolFile::SetLocateDwoCallback(nullptr);
+ return;
+ }
+
+ // Wraps around the `callback` call to convert the SB* classes to their
+ // private counterparts.
+ SymbolFile::SetLocateDwoCallback(
+ [callback, baton](const FileSpec &objfile_spec, const char *dwo_name,
+ const char *comp_dir, const uint64_t dwo_id,
+ FileSpec &located_dwo_file_spec) {
+ SBFileSpec located_dwo_file_spec_sb;
+ SBError error = callback(baton, SBFileSpec(objfile_spec), dwo_name,
+ comp_dir, dwo_id, located_dwo_file_spec_sb);
+
+ if (error.Success()) {
+ located_dwo_file_spec = located_dwo_file_spec_sb.ref();
+ }
+
+ return error.ref();
+ });
+}
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
index f52a095bf167523..dcc5a43f3752883 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
@@ -1742,20 +1742,37 @@ SymbolFileDWARF::GetDwoSymbolFileForCompileUnit(
if (std::shared_ptr<SymbolFileDWARFDwo> dwp_sp = GetDwpSymbolFile())
return dwp_sp;
- FileSpec dwo_file(dwo_name);
- FileSystem::Instance().Resolve(dwo_file);
- bool found = false;
+ const char *comp_dir =
+ cu_die.GetAttributeValueAsString(dwarf_cu, DW_AT_comp_dir, nullptr);
+
+ // Try locating the dwo via the callback first.
+ FileSpec dwo_file;
+ SymbolFile::LocateDwoCallback locate_dwo_callback =
+ SymbolFile::GetLocateDwoCallback();
+ if (locate_dwo_callback) {
+ const FileSpec &objfile_spec = m_objfile_sp->GetFileSpec();
+ Status error = locate_dwo_callback(objfile_spec, dwo_name, comp_dir,
+ dwarf_cu->GetDWOId().value(), dwo_file);
+ if (error.Fail()) {
+ GetObjectFile()->GetModule()->ReportWarning(
+ "locate DWO callback failed with error: {0}",
+ error.AsCString("unknown error"));
+ }
+ }
+ bool found = dwo_file && FileSystem::Instance().Exists(dwo_file);
+
+ if (!found) {
+ dwo_file = FileSpec(dwo_name);
+ FileSystem::Instance().Resolve(dwo_file);
+ // It's relative, e.g. "foo.dwo", but we just to happen to be right next to
+ // it. Or it's absolute.
+ found = FileSystem::Instance().Exists(dwo_file);
+ }
const FileSpecList &debug_file_search_paths =
Target::GetDefaultDebugFileSearchPaths();
size_t num_search_paths = debug_file_search_paths.GetSize();
- // It's relative, e.g. "foo.dwo", but we just to happen to be right next to
- // it. Or it's absolute.
- found = FileSystem::Instance().Exists(dwo_file);
-
- const char *comp_dir =
- cu_die.GetAttributeValueAsString(dwarf_cu, DW_AT_comp_dir, nullptr);
if (!found) {
// It could be a relative path that also uses DW_AT_COMP_DIR.
if (comp_dir) {
diff --git a/lldb/source/Symbol/SymbolFile.cpp b/lldb/source/Symbol/SymbolFile.cpp
index 7dcee8ced0ea11b..d35c31d9b6a5479 100644
--- a/lldb/source/Symbol/SymbolFile.cpp
+++ b/lldb/source/Symbol/SymbolFile.cpp
@@ -28,6 +28,7 @@ using namespace lldb;
char SymbolFile::ID;
char SymbolFileCommon::ID;
+SymbolFile::LocateDwoCallback SymbolFile::LOCATE_DWO_CALLBACK;
void SymbolFile::PreloadSymbols() {
// No-op for most implementations.
@@ -105,6 +106,14 @@ SymbolFile *SymbolFile::FindPlugin(ObjectFileSP objfile_sp) {
return best_symfile_up.release();
}
+void SymbolFile::SetLocateDwoCallback(LocateDwoCallback callback) {
+ LOCATE_DWO_CALLBACK = callback;
+}
+
+SymbolFile::LocateDwoCallback SymbolFile::GetLocateDwoCallback() {
+ return LOCATE_DWO_CALLBACK;
+}
+
uint32_t
SymbolFile::ResolveSymbolContext(const SourceLocationSpec &src_location_spec,
lldb::SymbolContextItem resolve_scope,
diff --git a/lldb/test/API/python_api/sbmodule/locate_dwo_callback/Makefile b/lldb/test/API/python_api/sbmodule/locate_dwo_callback/Makefile
new file mode 100644
index 000000000000000..53fb8b78eba4503
--- /dev/null
+++ b/lldb/test/API/python_api/sbmodule/locate_dwo_callback/Makefile
@@ -0,0 +1,4 @@
+CXX_SOURCES := main.cpp foo.cpp
+CFLAGS_EXTRAS := -g -gsplit-dwarf
+
+include Makefile.rules
diff --git a/lldb/test/API/python_api/sbmodule/locate_dwo_callback/TestLocateDwoCallback.py b/lldb/test/API/python_api/sbmodule/locate_dwo_callback/TestLocateDwoCallback.py
new file mode 100644
index 000000000000000..cc134913620f806
--- /dev/null
+++ b/lldb/test/API/python_api/sbmodule/locate_dwo_callback/TestLocateDwoCallback.py
@@ -0,0 +1,119 @@
+"""
+Test module locate dwo callback functionality
+"""
+
+import ctypes
+import shutil
+from lldbsuite.test.decorators import *
+import lldb
+from lldbsuite.test import lldbtest, lldbutil
+
+
+class LocateDwoCallbackTestCase(lldbtest.TestBase):
+ NO_DEBUG_INFO_TESTCASE = True
+
+ def setUp(self):
+ lldbtest.TestBase.setUp(self)
+
+ self.build()
+ self.exe = self.getBuildArtifact("a.out")
+ self.dwos = [
+ self.getBuildArtifact("main.dwo"),
+ self.getBuildArtifact("foo.dwo")
+ ]
+
+ def run_program(self):
+ # Set a breakpoint at main
+ target = self.dbg.CreateTarget(self.exe)
+ self.assertTrue(target, lldbtest.VALID_TARGET)
+ lldbutil.run_break_set_by_symbol(self, "main")
+
+ # Now launch the process, and do not stop at entry point.
+ self.process = target.LaunchSimple(None, None, self.get_process_working_directory())
+ self.assertTrue(self.process, lldbtest.PROCESS_IS_VALID)
+
+ def check_symbolicated(self):
+ thread = self.process.GetSelectedThread()
+ frame = thread.GetSelectedFrame()
+ self.assertEquals(len(frame.get_arguments()), 2)
+
+ def check_not_symbolicated(self):
+ thread = self.process.GetSelectedThread()
+ frame = thread.GetSelectedFrame()
+ self.assertNotEquals(len(frame.get_arguments()), 2)
+
+ def moveDwos(self):
+ """Move the dwos to a subdir in the build dir"""
+ dwo_folder = os.path.join(self.getBuildDir(), "dwos")
+ lldbutil.mkdir_p(dwo_folder)
+ for dwo in self.dwos:
+ shutil.move(dwo, dwo_folder)
+
+ @skipIfWindows
+ @skipIfDarwin
+ def test_set_non_callable(self):
+ with self.assertRaises(TypeError):
+ lldb.SBModule.SetLocateDwoCallback("a")
+
+ @skipIfWindows
+ @skipIfDarwin
+ def test_set_wrong_args(self):
+ def test_args_less_than_5(a, b, c, d):
+ pass
+ with self.assertRaises(TypeError):
+ lldb.SBModule.SetLocateDwoCallback(test_args_less_than_5)
+
+ @skipIfWindows
+ @skipIfDarwin
+ def test_default_succeeds(self):
+ lldb.SBModule.SetLocateDwoCallback(None)
+
+ self.moveDwos()
+ self.run_program()
+ self.check_not_symbolicated()
+
+ @skipIfWindows
+ @skipIfDarwin
+ def test_default_fails_when_dwos_moved(self):
+ lldb.SBModule.SetLocateDwoCallback(None)
+
+ self.moveDwos()
+ self.run_program()
+ self.check_not_symbolicated()
+
+ @skipIfWindows
+ @skipIfDarwin
+ def test_callback_finds_dwos(self):
+ lldb.SBModule.SetLocateDwoCallback(locate_dwo_callback)
+
+ self.moveDwos()
+ self.run_program()
+ self.check_symbolicated()
+
+ # We don't want to interfere with other tests, so we unset it here.
+ lldb.SBModule.SetLocateDwoCallback(None)
+
+ @skipIfWindows
+ @skipIfDarwin
+ def test_falls_back_when_dwo_fails(self):
+ # Note that we *don't* move the dwo files here, so the callback
+ # *shouldn't* actually find the files.
+ lldb.SBModule.SetLocateDwoCallback(locate_dwo_callback)
+
+ self.run_program()
+
+ # We should properly fall back to the default.
+ self.check_symbolicated()
+
+ # We don't want to interfere with other tests, so we unset it here.
+ lldb.SBModule.SetLocateDwoCallback(None)
+
+def locate_dwo_callback(objfile, dwo_name, comp_dir, dwo_id, result_file_spec):
+ import os
+ dwo_dir_after_move = os.path.join(comp_dir, "dwos")
+
+ if os.path.exists(os.path.join(dwo_dir_after_move, dwo_name)):
+ result_file_spec.SetDirectory(dwo_dir_after_move)
+ result_file_spec.SetFilename(dwo_name)
+
+ return lldb.SBError()
diff --git a/lldb/test/API/python_api/sbmodule/locate_dwo_callback/foo.cpp b/lldb/test/API/python_api/sbmodule/locate_dwo_callback/foo.cpp
new file mode 100644
index 000000000000000..28e2b6e768df4e7
--- /dev/null
+++ b/lldb/test/API/python_api/sbmodule/locate_dwo_callback/foo.cpp
@@ -0,0 +1,3 @@
+#include "foo.h"
+
+int foo() { return 1; }
diff --git a/lldb/test/API/python_api/sbmodule/locate_dwo_callback/foo.h b/lldb/test/API/python_api/sbmodule/locate_dwo_callback/foo.h
new file mode 100644
index 000000000000000..4ec598ad513eb91
--- /dev/null
+++ b/lldb/test/API/python_api/sbmodule/locate_dwo_callback/foo.h
@@ -0,0 +1,6 @@
+#ifndef FOO_H
+#define FOO_H
+
+int foo();
+
+#endif
diff --git a/lldb/test/API/python_api/sbmodule/locate_dwo_callback/main.cpp b/lldb/test/API/python_api/sbmodule/locate_dwo_callback/main.cpp
new file mode 100644
index 000000000000000..f0259ba63c8f370
--- /dev/null
+++ b/lldb/test/API/python_api/sbmodule/locate_dwo_callback/main.cpp
@@ -0,0 +1,3 @@
+#include "foo.h"
+
+int main(int argc, char **argv) { return foo(); }
``````````
</details>
https://github.com/llvm/llvm-project/pull/69517
More information about the lldb-commits
mailing list