avogelsgesang updated this revision to Diff 454911.
avogelsgesang marked 6 inline comments as done.
avogelsgesang added a comment.
addressed comments from @aprantl
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D132415/new/
https://reviews.llvm.org/D132415
Files:
clang/docs/tools/clang-formatted-files.txt
lldb/packages/Python/lldbsuite/test/lldbtest.py
lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt
lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
lldb/source/Plugins/Language/CPlusPlus/Coroutines.cpp
lldb/source/Plugins/Language/CPlusPlus/Coroutines.h
lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/coroutine_handle/Makefile
lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/coroutine_handle/TestCoroutineHandle.py
lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/coroutine_handle/main.cpp
Index: lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/coroutine_handle/main.cpp
===================================================================
--- /dev/null
+++ lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/coroutine_handle/main.cpp
@@ -0,0 +1,40 @@
+#include <coroutine>
+
+// `int_generator` is a stripped down, minimal coroutine generator
+// type.
+struct int_generator {
+ struct promise_type {
+ int current_value = -1;
+
+ auto get_return_object() {
+ return std::coroutine_handle<promise_type>::from_promise(*this);
+ }
+ auto initial_suspend() { return std::suspend_always(); }
+ auto final_suspend() noexcept { return std::suspend_always(); }
+ auto return_void() { return std::suspend_always(); }
+ void unhandled_exception() { __builtin_unreachable(); }
+ auto yield_value(int v) {
+ current_value = v;
+ return std::suspend_always();
+ }
+ };
+
+ std::coroutine_handle<promise_type> hdl;
+
+ int_generator(std::coroutine_handle<promise_type> h) : hdl(h) {}
+ ~int_generator() { hdl.destroy(); }
+};
+
+int_generator my_generator_func() { co_yield 42; }
+
+// This is an empty function which we call just so the debugger has
+// a place to reliably set a breakpoint on.
+void empty_function_so_we_can_set_a_breakpoint() {}
+
+int main() {
+ int_generator gen = my_generator_func();
+ std::coroutine_handle<> type_erased_hdl = gen.hdl;
+ gen.hdl.resume(); // Break at initial_suspend
+ gen.hdl.resume(); // Break after co_yield
+ empty_function_so_we_can_set_a_breakpoint(); // Break at final_suspend
+}
Index: lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/coroutine_handle/TestCoroutineHandle.py
===================================================================
--- /dev/null
+++ lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/coroutine_handle/TestCoroutineHandle.py
@@ -0,0 +1,87 @@
+"""
+Test lldb data formatter subsystem.
+"""
+
+
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+USE_LIBSTDCPP = "USE_LIBSTDCPP"
+USE_LIBCPP = "USE_LIBCPP"
+
+class TestCoroutineHandle(TestBase):
+ def continue_to_source_breakpoint(self, bkpt_pattern, source_spec):
+ target = self.target()
+ breakpoint = target.BreakpointCreateBySourceRegex(
+ bkpt_pattern, source_spec, None)
+ self.assertTrue(breakpoint, VALID_BREAKPOINT)
+ threads = lldbutil.continue_to_breakpoint(self.process(), breakpoint)
+ self.assertEqual(len(threads), 1)
+
+
+ def do_test(self, stdlib_type):
+ """Test std::coroutine_handle is displayed correctly."""
+ self.build(dictionary={stdlib_type: "1"})
+
+ test_generator_func_ptr_re = re.compile(
+ r"^\(a.out`my_generator_func\(\) at main.cpp:[0-9]*\)$")
+
+ # Run until the initial suspension point
+ lldbutil.run_to_source_breakpoint(self, '// Break at initial_suspend',
+ lldb.SBFileSpec("main.cpp", False))
+ # Check that we show the correct function pointers and the `promise`.
+ self.expect_expr("gen.hdl",
+ result_summary=re.compile("^coro frame = 0x[0-9a-f]*$"),
+ result_children=[
+ ValueCheck(name="resume", summary = test_generator_func_ptr_re),
+ ValueCheck(name="destroy", summary = test_generator_func_ptr_re),
+ ValueCheck(name="promise", children=[
+ ValueCheck(name="current_value", value = "-1"),
+ ])
+ ])
+ # For type-erased `coroutine_handle<>` we are missing the `promise`
+ # but still show `resume` and `destroy`.
+ self.expect_expr("type_erased_hdl",
+ result_summary=re.compile("^coro frame = 0x[0-9a-f]*$"),
+ result_children=[
+ ValueCheck(name="resume", summary = test_generator_func_ptr_re),
+ ValueCheck(name="destroy", summary = test_generator_func_ptr_re),
+ ])
+
+ # Run until after the `co_yield`
+ self.continue_to_source_breakpoint('// Break after co_yield',
+ lldb.SBFileSpec("main.cpp", False))
+ # We correctly show the updated value inside `prommise.current_value`.
+ self.expect_expr("gen.hdl",
+ result_children=[
+ ValueCheck(name="resume", summary = test_generator_func_ptr_re),
+ ValueCheck(name="destroy", summary = test_generator_func_ptr_re),
+ ValueCheck(name="promise", children=[
+ ValueCheck(name="current_value", value = "42"),
+ ])
+ ])
+
+ # Run until the `final_suspend`
+ self.continue_to_source_breakpoint('// Break at final_suspend',
+ lldb.SBFileSpec("main.cpp", False))
+ # At the final suspension point, `resume` is set to a nullptr.
+ # Check that we still show the remaining data correctly.
+ self.expect_expr("gen.hdl",
+ result_children=[
+ ValueCheck(name="resume", value = "0x0000000000000000"),
+ ValueCheck(name="destroy", summary = test_generator_func_ptr_re),
+ ValueCheck(name="promise", children=[
+ ValueCheck(name="current_value", value = "42"),
+ ])
+ ])
+
+ @add_test_categories(["libstdcxx"])
+ def test_libstdcpp(self):
+ self.do_test(USE_LIBSTDCPP)
+
+ @add_test_categories(["libc++"])
+ def test_libcpp(self):
+ self.do_test(USE_LIBCPP)
Index: lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/coroutine_handle/Makefile
===================================================================
--- /dev/null
+++ lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/coroutine_handle/Makefile
@@ -0,0 +1,4 @@
+CXX_SOURCES := main.cpp
+CFLAGS_EXTRAS := -std=c++20
+
+include Makefile.rules
Index: lldb/source/Plugins/Language/CPlusPlus/Coroutines.h
===================================================================
--- /dev/null
+++ lldb/source/Plugins/Language/CPlusPlus/Coroutines.h
@@ -0,0 +1,57 @@
+//===-- Coroutines.h --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_COROUTINES_H
+#define LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_COROUTINES_H
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/DataFormatters/TypeSummary.h"
+#include "lldb/DataFormatters/TypeSynthetic.h"
+#include "lldb/Utility/Stream.h"
+
+namespace lldb_private {
+namespace formatters {
+
+/// Summary provider for `std::coroutine_handle<T>` from libc++, libstdc++ and
+/// MSVC STL.
+bool StdlibCoroutineHandleSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+/// Synthetic children frontend for `std::coroutine_handle<promise_type>` from
+/// libc++, libstdc++ and MSVC STL. Shows the compiler-generated `resume` and
+/// `destroy` function pointers as well as the `promise`, if the promise type
+/// is `promise_type != void`.
+class StdlibCoroutineHandleSyntheticFrontEnd
+ : public SyntheticChildrenFrontEnd {
+public:
+ StdlibCoroutineHandleSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ ~StdlibCoroutineHandleSyntheticFrontEnd() override;
+
+ size_t CalculateNumChildren() override;
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+ bool Update() override;
+
+ bool MightHaveChildren() override;
+
+ size_t GetIndexOfChildWithName(ConstString name) override;
+
+private:
+ lldb::ValueObjectSP m_frame_ptr_sp;
+};
+
+SyntheticChildrenFrontEnd *
+StdlibCoroutineHandleSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP);
+
+} // namespace formatters
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_COROUTINES_H
Index: lldb/source/Plugins/Language/CPlusPlus/Coroutines.cpp
===================================================================
--- /dev/null
+++ lldb/source/Plugins/Language/CPlusPlus/Coroutines.cpp
@@ -0,0 +1,137 @@
+//===-- Coroutines.cpp ----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Coroutines.h"
+
+#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+static ValueObjectSP GetCoroFramePtrFromHandle(ValueObject &valobj) {
+ ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue());
+ if (!valobj_sp)
+ return nullptr;
+
+ // We expect a single pointer in the `coroutine_handle` class.
+ // We don't care about its name.
+ if (valobj_sp->GetNumChildren() != 1)
+ return nullptr;
+ ValueObjectSP ptr_sp(valobj_sp->GetChildAtIndex(0, true));
+ if (!ptr_sp)
+ return nullptr;
+ if (!ptr_sp->GetCompilerType().IsPointerType())
+ return nullptr;
+
+ return ptr_sp;
+}
+
+static CompilerType GetCoroutineFrameType(TypeSystemClang &ast_ctx,
+ CompilerType promise_type) {
+ CompilerType void_type = ast_ctx.GetBasicType(lldb::eBasicTypeVoid);
+ CompilerType coro_func_type = ast_ctx.CreateFunctionType(
+ /*result_type*/ void_type, /*args*/ &void_type, /*num_args*/ 1,
+ /*is_variadic*/ false, /*qualifiers*/ 0);
+ CompilerType coro_abi_type;
+ if (promise_type.IsVoidType()) {
+ coro_abi_type = ast_ctx.CreateStructForIdentifier(
+ ConstString(), {{"resume", coro_func_type.GetPointerType()},
+ {"destroy", coro_func_type.GetPointerType()}});
+ } else {
+ coro_abi_type = ast_ctx.CreateStructForIdentifier(
+ ConstString(), {{"resume", coro_func_type.GetPointerType()},
+ {"destroy", coro_func_type.GetPointerType()},
+ {"promise", promise_type}});
+ }
+ return coro_abi_type;
+}
+
+bool lldb_private::formatters::StdlibCoroutineHandleSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ ValueObjectSP ptr_sp(GetCoroFramePtrFromHandle(valobj));
+ if (!ptr_sp)
+ return false;
+
+ stream.Printf("coro frame = 0x%" PRIx64, ptr_sp->GetValueAsUnsigned(0));
+ return true;
+}
+
+lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEnd::
+ StdlibCoroutineHandleSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp) {
+ if (valobj_sp)
+ Update();
+}
+
+lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEnd::
+ ~StdlibCoroutineHandleSyntheticFrontEnd() = default;
+
+size_t lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEnd::
+ CalculateNumChildren() {
+ if (!m_frame_ptr_sp)
+ return 0;
+
+ return m_frame_ptr_sp->GetNumChildren();
+}
+
+lldb::ValueObjectSP lldb_private::formatters::
+ StdlibCoroutineHandleSyntheticFrontEnd::GetChildAtIndex(size_t idx) {
+ if (!m_frame_ptr_sp)
+ return lldb::ValueObjectSP();
+
+ return m_frame_ptr_sp->GetChildAtIndex(idx, true);
+}
+
+bool lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEnd::
+ Update() {
+ m_frame_ptr_sp.reset();
+
+ ValueObjectSP valobj_sp = m_backend.GetSP();
+ if (!valobj_sp)
+ return false;
+
+ ValueObjectSP ptr_sp(GetCoroFramePtrFromHandle(m_backend));
+ if (!ptr_sp)
+ return false;
+
+ TypeSystemClang *ast_ctx = llvm::dyn_cast_or_null<TypeSystemClang>(
+ valobj_sp->GetCompilerType().GetTypeSystem());
+ if (!ast_ctx)
+ return false;
+
+ CompilerType promise_type(
+ valobj_sp->GetCompilerType().GetTypeTemplateArgument(0));
+ if (!promise_type)
+ return false;
+ CompilerType coro_frame_type = GetCoroutineFrameType(*ast_ctx, promise_type);
+
+ m_frame_ptr_sp = ptr_sp->Cast(coro_frame_type.GetPointerType());
+
+ return false;
+}
+
+bool lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEnd::
+ MightHaveChildren() {
+ return true;
+}
+
+size_t StdlibCoroutineHandleSyntheticFrontEnd::GetIndexOfChildWithName(
+ ConstString name) {
+ if (!m_frame_ptr_sp)
+ return UINT32_MAX;
+
+ return m_frame_ptr_sp->GetIndexOfChildWithName(name);
+}
+
+SyntheticChildrenFrontEnd *
+lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEndCreator(
+ CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+ return (valobj_sp ? new StdlibCoroutineHandleSyntheticFrontEnd(valobj_sp)
+ : nullptr);
+}
Index: lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
===================================================================
--- lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
+++ lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
@@ -35,6 +35,7 @@
#include "BlockPointer.h"
#include "CPlusPlusNameParser.h"
+#include "Coroutines.h"
#include "CxxStringTypes.h"
#include "Generic.h"
#include "LibCxx.h"
@@ -796,6 +797,14 @@
ConstString("^std::__[[:alnum:]]+::function<.+>$"),
stl_summary_flags, true);
+ ConstString libcxx_std_coroutine_handle_regex(
+ "^std::__[[:alnum:]]+::coroutine_handle<.+>(( )?&)?$");
+ AddCXXSynthetic(
+ cpp_category_sp,
+ lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEndCreator,
+ "coroutine_handle synthetic children", libcxx_std_coroutine_handle_regex,
+ stl_deref_flags, true);
+
stl_summary_flags.SetDontShowChildren(false);
stl_summary_flags.SetSkipPointers(false);
AddCXXSummary(cpp_category_sp,
@@ -898,6 +907,11 @@
"libc++ std::unique_ptr summary provider",
libcxx_std_unique_ptr_regex, stl_summary_flags, true);
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::StdlibCoroutineHandleSummaryProvider,
+ "libc++ std::coroutine_handle summary provider",
+ libcxx_std_coroutine_handle_regex, stl_summary_flags, true);
+
AddCXXSynthetic(
cpp_category_sp,
lldb_private::formatters::LibCxxVectorIteratorSyntheticFrontEndCreator,
@@ -1122,6 +1136,14 @@
"std::tuple synthetic children", ConstString("^std::tuple<.+>(( )?&)?$"),
stl_synth_flags, true);
+ ConstString libstdcpp_std_coroutine_handle_regex(
+ "^std::coroutine_handle<.+>(( )?&)?$");
+ AddCXXSynthetic(
+ cpp_category_sp,
+ lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEndCreator,
+ "std::coroutine_handle synthetic children",
+ libstdcpp_std_coroutine_handle_regex, stl_deref_flags, true);
+
AddCXXSynthetic(
cpp_category_sp,
lldb_private::formatters::LibStdcppBitsetSyntheticFrontEndCreator,
@@ -1149,6 +1171,10 @@
"libstdc++ std::weak_ptr summary provider",
ConstString("^std::weak_ptr<.+>(( )?&)?$"), stl_summary_flags,
true);
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::StdlibCoroutineHandleSummaryProvider,
+ "libstdc++ std::coroutine_handle summary provider",
+ libstdcpp_std_coroutine_handle_regex, stl_summary_flags, true);
AddCXXSummary(
cpp_category_sp, lldb_private::formatters::GenericOptionalSummaryProvider,
"libstd++ std::optional summary provider",
Index: lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt
===================================================================
--- lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt
+++ lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt
@@ -1,5 +1,6 @@
add_lldb_library(lldbPluginCPlusPlusLanguage PLUGIN
BlockPointer.cpp
+ Coroutines.cpp
CPlusPlusLanguage.cpp
CPlusPlusNameParser.cpp
CxxStringTypes.cpp
Index: lldb/packages/Python/lldbsuite/test/lldbtest.py
===================================================================
--- lldb/packages/Python/lldbsuite/test/lldbtest.py
+++ lldb/packages/Python/lldbsuite/test/lldbtest.py
@@ -292,8 +292,12 @@
test_base.assertEqual(self.expect_type, val.GetDisplayTypeName(),
this_error_msg)
if self.expect_summary:
- test_base.assertEqual(self.expect_summary, val.GetSummary(),
- this_error_msg)
+ if isinstance(self.expect_summary, re.Pattern):
+ test_base.assertRegex(val.GetSummary(), self.expect_summary,
+ this_error_msg)
+ else:
+ test_base.assertEqual(self.expect_summary, val.GetSummary(),
+ this_error_msg)
if self.children is not None:
self.check_value_children(test_base, val, error_msg)
Index: clang/docs/tools/clang-formatted-files.txt
===================================================================
--- clang/docs/tools/clang-formatted-files.txt
+++ clang/docs/tools/clang-formatted-files.txt
@@ -4180,6 +4180,8 @@
lldb/source/Plugins/Language/ClangCommon/ClangHighlighter.h
lldb/source/Plugins/Language/CPlusPlus/BlockPointer.cpp
lldb/source/Plugins/Language/CPlusPlus/BlockPointer.h
+lldb/source/Plugins/Language/CPlusPlus/Coroutines.cpp
+lldb/source/Plugins/Language/CPlusPlus/Coroutines.h
lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h
lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.h
lldb/source/Plugins/Language/CPlusPlus/CxxStringTypes.h
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits