From df9551dc5e8f0c610f1b38cf83ed8b7055e621d9 Mon Sep 17 00:00:00 2001 From: Med Ismail Bennani Date: Tue, 2 Jun 2020 22:22:22 +0200 Subject: [PATCH] [lldb/formatter] Add Swift.UnsafeBufferPointer data formatter This patch adds support for Swift's UnsafeBufferPointer and UnsafeMutableBufferPointer data formatting. It introduces a summary provider and a synthetic frontend creator for these types. Signed-off-by: Med Ismail Bennani --- .../Plugins/Language/Swift/CMakeLists.txt | 1 + .../Plugins/Language/Swift/SwiftFormatters.h | 1 + .../Plugins/Language/Swift/SwiftLanguage.cpp | 14 + .../Language/Swift/SwiftUnsafeTypes.cpp | 258 ++++++++++++++++++ .../Plugins/Language/Swift/SwiftUnsafeTypes.h | 34 +++ .../data-formatter/swift-unsafe/Makefile | 3 + .../TestSwiftUnsafeTypeFormatters.py | 7 + .../data-formatter/swift-unsafe/main.swift | 123 +++++++++ 8 files changed, 441 insertions(+) create mode 100644 lldb/source/Plugins/Language/Swift/SwiftUnsafeTypes.cpp create mode 100644 lldb/source/Plugins/Language/Swift/SwiftUnsafeTypes.h create mode 100644 lldb/test/API/functionalities/data-formatter/swift-unsafe/Makefile create mode 100644 lldb/test/API/functionalities/data-formatter/swift-unsafe/TestSwiftUnsafeTypeFormatters.py create mode 100644 lldb/test/API/functionalities/data-formatter/swift-unsafe/main.swift diff --git a/lldb/source/Plugins/Language/Swift/CMakeLists.txt b/lldb/source/Plugins/Language/Swift/CMakeLists.txt index 1f0642417eccd..194250e8dc2b0 100644 --- a/lldb/source/Plugins/Language/Swift/CMakeLists.txt +++ b/lldb/source/Plugins/Language/Swift/CMakeLists.txt @@ -14,6 +14,7 @@ add_lldb_library(lldbPluginSwiftLanguage PLUGIN SwiftOptional.cpp SwiftRuntimeFailureRecognizer.cpp SwiftSet.cpp + SwiftUnsafeTypes.cpp LINK_LIBS lldbCore diff --git a/lldb/source/Plugins/Language/Swift/SwiftFormatters.h b/lldb/source/Plugins/Language/Swift/SwiftFormatters.h index 9607f0c79f987..acebeacab8571 100644 --- a/lldb/source/Plugins/Language/Swift/SwiftFormatters.h +++ b/lldb/source/Plugins/Language/Swift/SwiftFormatters.h @@ -29,6 +29,7 @@ #include "SwiftOptionSet.h" #include "SwiftOptional.h" #include "SwiftSet.h" +#include "SwiftUnsafeTypes.h" namespace lldb_private { namespace formatters { diff --git a/lldb/source/Plugins/Language/Swift/SwiftLanguage.cpp b/lldb/source/Plugins/Language/Swift/SwiftLanguage.cpp index 0ff206533d275..54a72d185db70 100644 --- a/lldb/source/Plugins/Language/Swift/SwiftLanguage.cpp +++ b/lldb/source/Plugins/Language/Swift/SwiftLanguage.cpp @@ -326,6 +326,13 @@ static void LoadSwiftFormatters(lldb::TypeCategoryImplSP swift_category_sp) { "Swift.Array summary provider", ConstString("_TtCs22__SwiftDeferredNSArray"), summary_flags, false); + AddCXXSummary( + swift_category_sp, + lldb_private::formatters::swift::UnsafeBufferPointerSummaryProvider, + "Swift.Unsafe(Mutable)BufferPointer", + ConstString("^Swift.Unsafe(Mutable)?BufferPointer<.+>$"), summary_flags, + true); + DictionaryConfig::Get() .RegisterSummaryProviders(swift_category_sp, summary_flags); SetConfig::Get() @@ -383,6 +390,13 @@ static void LoadSwiftFormatters(lldb::TypeCategoryImplSP swift_category_sp) { synth_flags, false); + AddCXXSynthetic(swift_category_sp, + lldb_private::formatters::swift:: + UnsafeBufferPointerSyntheticFrontEndCreator, + "Swift.Unsafe(Mutable)BufferPointer", + ConstString("^Swift.Unsafe(Mutable)?BufferPointer<.+>$"), + synth_flags, true); + DictionaryConfig::Get() .RegisterSyntheticChildrenCreators(swift_category_sp, synth_flags); SetConfig::Get() diff --git a/lldb/source/Plugins/Language/Swift/SwiftUnsafeTypes.cpp b/lldb/source/Plugins/Language/Swift/SwiftUnsafeTypes.cpp new file mode 100644 index 0000000000000..d0bfe281ae9e8 --- /dev/null +++ b/lldb/source/Plugins/Language/Swift/SwiftUnsafeTypes.cpp @@ -0,0 +1,258 @@ +#include "SwiftUnsafeTypes.h" + +#include "lldb/DataFormatters/TypeSynthetic.h" +#include "lldb/Symbol/SwiftASTContext.h" +#include "lldb/Target/SwiftLanguageRuntime.h" + +#include + +using namespace lldb; +using namespace lldb_private; + +class SwiftUnsafeBufferPointer { +public: + SwiftUnsafeBufferPointer(ValueObject &valobj); + size_t GetCount() const { return m_count; } + addr_t GetStartAddress() const { return m_start_addr; } + CompilerType GetElementType() const { return m_elem_type; } + bool Update(); + +private: + ValueObject &m_valobj; + size_t m_count; + addr_t m_start_addr; + CompilerType m_elem_type; +}; + +SwiftUnsafeBufferPointer::SwiftUnsafeBufferPointer(ValueObject &valobj) + : m_valobj(*valobj.GetNonSyntheticValue().get()) {} + +bool SwiftUnsafeBufferPointer::Update() { + if (!m_valobj.GetNumChildren()) + return false; + + // Here is the layout of Swift's Unsafe[Mutable]BufferPointer. + // + // ▿ UnsafeBufferPointer + // ▿ _position : Optional> + // ▿ some : UnsafePointer + // - pointerValue : Int + // - count : Int + // + // The structure has 2 children: + // 1. The buffer `count` child stored as a Swift `Int` type. This entry is a + // "value-providing synthetic children", so lldb need to access to its + // children in order to get the actual value. + // 2. An Optional UnsafePointer to the buffer start address. To access the + // pointer address, lldb unfolds every value object child until reaching + // `pointerValue`. + + static ConstString g_count("count"); + ValueObjectSP count_value_sp(m_valobj.GetChildMemberWithName(g_count, true)); + if (!count_value_sp) + return false; + + ValueObjectSP value_provided_child_sp = nullptr; + + // Implement Swift's 'value-providing synthetic children' workaround. + // Depending on whether the value object type is a primitive or a structure, + // lldb should prioritize the synthetic value children. + // If it has no synthetic children then fallback to non synthetic children. + ValueObjectSP synthetic = count_value_sp->GetSyntheticValue(); + if (synthetic) + value_provided_child_sp = synthetic->GetChildAtIndex(0, true); + if (!value_provided_child_sp) + value_provided_child_sp = count_value_sp->GetChildAtIndex(0, true); + // If neither child exists, fail. + if (!value_provided_child_sp) + return false; + + size_t count = value_provided_child_sp->GetValueAsUnsigned(UINT64_MAX); + + if (count == UINT64_MAX) + return false; + + m_count = count; + + static ConstString g_position("_position"); + ValueObjectSP position_value_sp( + m_valobj.GetChildMemberWithName(g_position, true)); + if (!position_value_sp || !position_value_sp->GetNumChildren()) + return false; + + ValueObjectSP some_value_sp = position_value_sp->GetChildAtIndex(0, true); + if (!some_value_sp || !some_value_sp->GetNumChildren()) + return false; + + CompilerType argument_type; + + if (CompilerType type = some_value_sp->GetCompilerType()) + argument_type = SwiftASTContext::GetGenericArgumentType(type, 0); + + if (!argument_type.IsValid()) + return nullptr; + + m_elem_type = argument_type; + + ValueObjectSP pointer_value_sp = some_value_sp->GetChildAtIndex(0, true); + if (!pointer_value_sp) + return false; + + addr_t addr = pointer_value_sp->GetValueAsUnsigned(LLDB_INVALID_ADDRESS); + + if (!addr || addr == LLDB_INVALID_ADDRESS) + return false; + + m_start_addr = addr; + + return true; +} + +bool lldb_private::formatters::swift::UnsafeBufferPointerSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + + SwiftUnsafeBufferPointer swift_ubp(valobj); + + if (!swift_ubp.Update()) + return false; + + size_t count = swift_ubp.GetCount(); + addr_t addr = swift_ubp.GetStartAddress(); + + stream.Printf("%zu %s (0x%" PRIx64 ")", count, + (count == 1) ? "value" : "values", addr); + + return true; +} + +namespace lldb_private { +namespace formatters { +namespace swift { +class UnsafeBufferPointerSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + UnsafeBufferPointerSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + virtual size_t CalculateNumChildren(); + + virtual lldb::ValueObjectSP GetChildAtIndex(size_t idx); + + virtual bool Update(); + + virtual bool MightHaveChildren(); + + virtual size_t GetIndexOfChildWithName(ConstString name); + + virtual ~UnsafeBufferPointerSyntheticFrontEnd() = default; + +private: + ExecutionContextRef m_exe_ctx_ref; + uint8_t m_ptr_size; + lldb::ByteOrder m_order; + + SwiftUnsafeBufferPointer m_unsafe_ptr; + size_t m_element_stride; + DataBufferSP m_buffer_sp; + std::vector m_children; +}; +} // namespace swift +} // namespace formatters +} // namespace lldb_private + +lldb_private::formatters::swift::UnsafeBufferPointerSyntheticFrontEnd:: + UnsafeBufferPointerSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp.get()), + m_unsafe_ptr(*valobj_sp.get()) { + + ProcessSP process_sp = valobj_sp->GetProcessSP(); + if (!process_sp) + return; + + m_ptr_size = process_sp->GetAddressByteSize(); + m_order = process_sp->GetByteOrder(); + + if (valobj_sp) + Update(); +} + +size_t lldb_private::formatters::swift::UnsafeBufferPointerSyntheticFrontEnd:: + CalculateNumChildren() { + return m_unsafe_ptr.GetCount(); +} + +lldb::ValueObjectSP lldb_private::formatters::swift:: + UnsafeBufferPointerSyntheticFrontEnd::GetChildAtIndex(size_t idx) { + const size_t num_children = CalculateNumChildren(); + + if (idx >= num_children || idx >= m_children.size()) + return lldb::ValueObjectSP(); + + return m_children[idx]; +} + +bool lldb_private::formatters::swift::UnsafeBufferPointerSyntheticFrontEnd:: + Update() { + m_children.clear(); + ValueObjectSP valobj_sp = m_backend.GetSP(); + if (!valobj_sp) + return false; + m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); + + lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); + if (!process_sp) + return false; + if (!m_unsafe_ptr.Update()) + return false; + + const addr_t start_addr = m_unsafe_ptr.GetStartAddress(); + const size_t num_children = CalculateNumChildren(); + const CompilerType element_type = m_unsafe_ptr.GetElementType(); + + auto stride = element_type.GetByteStride(process_sp.get()); + if (!stride) + return false; + + m_element_stride = *stride; + + if (m_children.empty()) { + size_t buffer_size = num_children * m_element_stride; + m_buffer_sp.reset(new DataBufferHeap(buffer_size, 0)); + + Status error; + size_t read_bytes = process_sp->ReadMemory( + start_addr, m_buffer_sp->GetBytes(), buffer_size, error); + + if (!read_bytes || error.Fail()) + return false; + + DataExtractor buffer_data(m_buffer_sp->GetBytes(), + m_buffer_sp->GetByteSize(), m_order, m_ptr_size); + + for (size_t i = 0; i < num_children; i++) { + StreamString idx_name; + idx_name.Printf("[%" PRIu64 "]", i); + DataExtractor data(buffer_data, i * m_element_stride, m_element_stride); + m_children.push_back(CreateValueObjectFromData( + idx_name.GetString(), data, m_exe_ctx_ref, element_type)); + } + } + + return m_children.size() == num_children; +} + +bool lldb_private::formatters::swift::UnsafeBufferPointerSyntheticFrontEnd:: + MightHaveChildren() { + return m_unsafe_ptr.GetCount(); +} + +size_t lldb_private::formatters::swift::UnsafeBufferPointerSyntheticFrontEnd:: + GetIndexOfChildWithName(ConstString name) { + return UINT32_MAX; +} + +SyntheticChildrenFrontEnd * +lldb_private::formatters::swift::UnsafeBufferPointerSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + if (!valobj_sp) + return nullptr; + return (new UnsafeBufferPointerSyntheticFrontEnd(valobj_sp)); +} diff --git a/lldb/source/Plugins/Language/Swift/SwiftUnsafeTypes.h b/lldb/source/Plugins/Language/Swift/SwiftUnsafeTypes.h new file mode 100644 index 0000000000000..8f790d15e4291 --- /dev/null +++ b/lldb/source/Plugins/Language/Swift/SwiftUnsafeTypes.h @@ -0,0 +1,34 @@ +//===-- SwiftUnsafeTypes.h --------------------------------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_SwiftUnsafeTypes_h_ +#define liblldb_SwiftUnsafeTypes_h_ + +#include "lldb/Core/ValueObject.h" +#include "lldb/Utility/Stream.h" + +namespace lldb_private { +namespace formatters { +namespace swift { + +bool UnsafeBufferPointerSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &); + +SyntheticChildrenFrontEnd * +UnsafeBufferPointerSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +}; // namespace swift +}; // namespace formatters +}; // namespace lldb_private + +#endif diff --git a/lldb/test/API/functionalities/data-formatter/swift-unsafe/Makefile b/lldb/test/API/functionalities/data-formatter/swift-unsafe/Makefile new file mode 100644 index 0000000000000..2a69023633b34 --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/swift-unsafe/Makefile @@ -0,0 +1,3 @@ +SWIFT_SOURCES := main.swift + +include Makefile.rules diff --git a/lldb/test/API/functionalities/data-formatter/swift-unsafe/TestSwiftUnsafeTypeFormatters.py b/lldb/test/API/functionalities/data-formatter/swift-unsafe/TestSwiftUnsafeTypeFormatters.py new file mode 100644 index 0000000000000..d99b361976261 --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/swift-unsafe/TestSwiftUnsafeTypeFormatters.py @@ -0,0 +1,7 @@ +""" +Test that Swift unsafe types get formatted properly +""" +import lldbsuite.test.lldbinline as lldbinline +from lldbsuite.test.decorators import * + +lldbinline.MakeInlineTest(__file__, globals(), decorators=[swiftTest]) diff --git a/lldb/test/API/functionalities/data-formatter/swift-unsafe/main.swift b/lldb/test/API/functionalities/data-formatter/swift-unsafe/main.swift new file mode 100644 index 0000000000000..5f2bbab373ec7 --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/swift-unsafe/main.swift @@ -0,0 +1,123 @@ +struct IntPair { + var original: Int + var opposite: Int + + init(_ value: Int) { + self.original = value + self.opposite = -value + } +} + +enum Toggle { case On; case Off } + +enum ColorCode { + case RGB(UInt8, UInt8, UInt8) + case Hex(Int) +} + +protocol Flyable { + var fly : String { get } +} + +struct Bird: Flyable { + var fly: String = "🦅" +} + +struct Plane: Flyable { + var fly: String = "🛩" +} + +class Number { + + var number_value : T + + init (number value : T) { + number_value = value + } +} + +func main() { + // UnsafeBufferPointer + let structArray = [ IntPair(1), IntPair(-2), IntPair(3) ] + structArray.withUnsafeBufferPointer { + let buf = $0 + print("break here ...") + //% self.expect("frame variable -d run-target buf", + //% patterns=[ + //% '\(UnsafeBufferPointer<(.*)\.IntPair>\) buf = 3 values \(0[xX][0-9a-fA-F]+\) {', + //% '\[0\] = \(original = 1, opposite = -1\)', + //% '\[1\] = \(original = -2, opposite = 2\)', + //% '\[2\] = \(original = 3, opposite = -3\)', + //% ]) + } + + // UnsafeMutableBufferPointer + var enumArray = [ Toggle.Off ] + enumArray.withUnsafeMutableBufferPointer { + let mutbuf = $0 + print("... here ...") + //% self.expect("frame variable -d run-target mutbuf", + //% patterns=[ + //% '\(UnsafeMutableBufferPointer<(.*)\.Toggle>\) mutbuf = 1 value \(0[xX][0-9a-fA-F]+\) {', + //% '\[0\] = Off', + //% ]) + mutbuf[0] = Toggle.On + print("... and here!") + //% self.expect("frame variable -d run-target mutbuf", + //% patterns=[ + //% '\(UnsafeMutableBufferPointer<(.*)\.Toggle>\) mutbuf = 1 value \(0[xX][0-9a-fA-F]+\) {', + //% '\[0\] = On', + //% ]) + } + + let colors = [ColorCode.RGB(155,219,255), ColorCode.Hex(0x4545ff)] + + colors.withUnsafeBufferPointer { + let buf = $0 + //% self.expect("frame variable -d run-target buf", + //% patterns=[ + //% '\(UnsafeBufferPointer<(.*)\.ColorCode>\) buf = 2 values \(0[xX][0-9a-fA-F]+\) {', + //% '\[0\] = RGB {', + //% 'RGB = \(0 = 155, 1 = 219, 2 = 255\)', + //% '\[1\] = Hex \(Hex = 4539903\)', + //% ]) + } + + var flyingObjects : [Flyable] = [ Bird(), Plane() ] + + flyingObjects.withUnsafeMutableBufferPointer { + let mutbuf = $0 + //% self.expect("frame variable -d run-target mutbuf", + //% patterns=[ + //% '\(UnsafeMutableBufferPointer<(.*)\.Flyable>\) mutbuf = 2 values \(0[xX][0-9a-fA-F]+\) {', + //% '\[0\] = \(fly = "🦅"\)', + //% '\[1\] = \(fly = "🛩"\)', + //% ]) + struct UFO: Flyable { + var fly: String = "🛸" + } + + mutbuf[1] = UFO() + //% self.expect("frame variable -d run-target mutbuf", + //% patterns=[ + //% '\(UnsafeMutableBufferPointer<(.*)\.Flyable>\) mutbuf = 2 values \(0[xX][0-9a-fA-F]+\) {', + //% '\[0\] = \(fly = "🦅"\)', + //% '\[1\] = \(fly = "🛸"\)', + //% ]) + } + + let numbers = [ Number(number: 42), Number(number: 3.14)] + + numbers.withUnsafeBufferPointer { + let buf = $0 + //% self.expect("frame variable -d run-target buf", + //% patterns=[ + //% '\(UnsafeBufferPointer<(.*)\.Number>\) buf = 2 values \(0[xX][0-9a-fA-F]+\) {', + //% '\[0\] = 0[xX][0-9a-fA-F]+ \(number_value = 42\)', + //% '\[1\] = 0[xX][0-9a-fA-F]+ \(number_value = 3.14[0-9]*\)', + //% ]) + } + +} + +main()