summaryrefslogtreecommitdiff
path: root/lldb/source/DataFormatters
diff options
context:
space:
mode:
Diffstat (limited to 'lldb/source/DataFormatters')
-rw-r--r--lldb/source/DataFormatters/CMakeLists.txt2
-rw-r--r--lldb/source/DataFormatters/FormatterBytecode.cpp575
-rw-r--r--lldb/source/DataFormatters/FormatterBytecode.def101
-rw-r--r--lldb/source/DataFormatters/FormatterBytecode.h64
-rw-r--r--lldb/source/DataFormatters/FormatterSection.cpp170
-rw-r--r--lldb/source/DataFormatters/TypeSummary.cpp77
6 files changed, 986 insertions, 3 deletions
diff --git a/lldb/source/DataFormatters/CMakeLists.txt b/lldb/source/DataFormatters/CMakeLists.txt
index 13faf65227d2..91b10ba9e0ac 100644
--- a/lldb/source/DataFormatters/CMakeLists.txt
+++ b/lldb/source/DataFormatters/CMakeLists.txt
@@ -5,7 +5,9 @@ add_lldb_library(lldbDataFormatters NO_PLUGIN_DEPENDENCIES
FormatCache.cpp
FormatClasses.cpp
FormatManager.cpp
+ FormatterBytecode.cpp
FormattersHelpers.cpp
+ FormatterSection.cpp
LanguageCategory.cpp
StringPrinter.cpp
TypeCategory.cpp
diff --git a/lldb/source/DataFormatters/FormatterBytecode.cpp b/lldb/source/DataFormatters/FormatterBytecode.cpp
new file mode 100644
index 000000000000..e49c75067818
--- /dev/null
+++ b/lldb/source/DataFormatters/FormatterBytecode.cpp
@@ -0,0 +1,575 @@
+//===-- FormatterBytecode.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 "FormatterBytecode.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/ValueObject/ValueObject.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/DataExtractor.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/FormatProviders.h"
+#include "llvm/Support/FormatVariadicDetails.h"
+
+using namespace lldb;
+namespace lldb_private {
+
+std::string toString(FormatterBytecode::OpCodes op) {
+ switch (op) {
+#define DEFINE_OPCODE(OP, MNEMONIC, NAME) \
+ case OP: { \
+ const char *s = MNEMONIC; \
+ return s ? s : #NAME; \
+ }
+#include "FormatterBytecode.def"
+#undef DEFINE_SIGNATURE
+ }
+ return llvm::utostr(op);
+}
+
+std::string toString(FormatterBytecode::Selectors sel) {
+ switch (sel) {
+#define DEFINE_SELECTOR(ID, NAME) \
+ case ID: \
+ return "@" #NAME;
+#include "FormatterBytecode.def"
+#undef DEFINE_SIGNATURE
+ }
+ return "@" + llvm::utostr(sel);
+}
+
+std::string toString(FormatterBytecode::Signatures sig) {
+ switch (sig) {
+#define DEFINE_SIGNATURE(ID, NAME) \
+ case ID: \
+ return "@" #NAME;
+#include "FormatterBytecode.def"
+#undef DEFINE_SIGNATURE
+ }
+ return llvm::utostr(sig);
+}
+
+std::string toString(const FormatterBytecode::DataStack &data) {
+ std::string s;
+ llvm::raw_string_ostream os(s);
+ os << "[ ";
+ for (auto &d : data) {
+ if (auto s = std::get_if<std::string>(&d))
+ os << '"' << *s << '"';
+ else if (auto u = std::get_if<uint64_t>(&d))
+ os << *u << 'u';
+ else if (auto i = std::get_if<int64_t>(&d))
+ os << *i;
+ else if (auto valobj = std::get_if<ValueObjectSP>(&d)) {
+ if (!valobj->get())
+ os << "null";
+ else
+ os << "object(" << valobj->get()->GetValueAsCString() << ')';
+ } else if (auto type = std::get_if<CompilerType>(&d)) {
+ os << '(' << type->GetTypeName(true) << ')';
+ } else if (auto sel = std::get_if<FormatterBytecode::Selectors>(&d)) {
+ os << toString(*sel);
+ }
+ os << ' ';
+ }
+ os << ']';
+ return s;
+}
+
+namespace FormatterBytecode {
+
+/// Implement the @format function.
+static llvm::Error FormatImpl(DataStack &data) {
+ auto fmt = data.Pop<std::string>();
+ auto replacements =
+ llvm::formatv_object_base::parseFormatString(fmt, 0, false);
+ std::string s;
+ llvm::raw_string_ostream os(s);
+ unsigned num_args = 0;
+ for (const auto &r : replacements)
+ if (r.Type == llvm::ReplacementType::Format)
+ num_args = std::max(num_args, r.Index + 1);
+
+ if (data.size() < num_args)
+ return llvm::createStringError("not enough arguments");
+
+ for (const auto &r : replacements) {
+ if (r.Type == llvm::ReplacementType::Literal) {
+ os << r.Spec;
+ continue;
+ }
+ using namespace llvm::support::detail;
+ auto arg = data[data.size() - num_args + r.Index];
+ auto format = [&](format_adapter &&adapter) {
+ llvm::FmtAlign Align(adapter, r.Where, r.Width, r.Pad);
+ Align.format(os, r.Options);
+ };
+
+ if (auto s = std::get_if<std::string>(&arg))
+ format(build_format_adapter(s->c_str()));
+ else if (auto u = std::get_if<uint64_t>(&arg))
+ format(build_format_adapter(u));
+ else if (auto i = std::get_if<int64_t>(&arg))
+ format(build_format_adapter(i));
+ else if (auto valobj = std::get_if<ValueObjectSP>(&arg)) {
+ if (!valobj->get())
+ format(build_format_adapter("null object"));
+ else
+ format(build_format_adapter(valobj->get()->GetValueAsCString()));
+ } else if (auto type = std::get_if<CompilerType>(&arg))
+ format(build_format_adapter(type->GetDisplayTypeName()));
+ else if (auto sel = std::get_if<FormatterBytecode::Selectors>(&arg))
+ format(build_format_adapter(toString(*sel)));
+ }
+ data.Push(s);
+ return llvm::Error::success();
+}
+
+static llvm::Error TypeCheck(llvm::ArrayRef<DataStackElement> data,
+ DataType type) {
+ if (data.size() < 1)
+ return llvm::createStringError("not enough elements on data stack");
+
+ auto &elem = data.back();
+ switch (type) {
+ case Any:
+ break;
+ case String:
+ if (!std::holds_alternative<std::string>(elem))
+ return llvm::createStringError("expected String");
+ break;
+ case UInt:
+ if (!std::holds_alternative<uint64_t>(elem))
+ return llvm::createStringError("expected UInt");
+ break;
+ case Int:
+ if (!std::holds_alternative<int64_t>(elem))
+ return llvm::createStringError("expected Int");
+ break;
+ case Object:
+ if (!std::holds_alternative<ValueObjectSP>(elem))
+ return llvm::createStringError("expected Object");
+ break;
+ case Type:
+ if (!std::holds_alternative<CompilerType>(elem))
+ return llvm::createStringError("expected Type");
+ break;
+ case Selector:
+ if (!std::holds_alternative<Selectors>(elem))
+ return llvm::createStringError("expected Selector");
+ break;
+ }
+ return llvm::Error::success();
+}
+
+static llvm::Error TypeCheck(llvm::ArrayRef<DataStackElement> data,
+ DataType type1, DataType type2) {
+ if (auto error = TypeCheck(data, type2))
+ return error;
+ return TypeCheck(data.drop_back(), type1);
+}
+
+static llvm::Error TypeCheck(llvm::ArrayRef<DataStackElement> data,
+ DataType type1, DataType type2, DataType type3) {
+ if (auto error = TypeCheck(data, type3))
+ return error;
+ return TypeCheck(data.drop_back(1), type2, type1);
+}
+
+llvm::Error Interpret(std::vector<ControlStackElement> &control,
+ DataStack &data, Selectors sel) {
+ if (control.empty())
+ return llvm::Error::success();
+ // Since the only data types are single endian and ULEBs, the
+ // endianness should not matter.
+ llvm::DataExtractor cur_block(control.back(), true, 64);
+ llvm::DataExtractor::Cursor pc(0);
+
+ while (!control.empty()) {
+ /// Activate the top most block from the control stack.
+ auto activate_block = [&]() {
+ // Save the return address.
+ if (control.size() > 1)
+ control[control.size() - 2] = cur_block.getData().drop_front(pc.tell());
+ cur_block = llvm::DataExtractor(control.back(), true, 64);
+ if (pc)
+ pc = llvm::DataExtractor::Cursor(0);
+ };
+
+ /// Fetch the next byte in the instruction stream.
+ auto next_byte = [&]() -> uint8_t {
+ // At the end of the current block?
+ while (pc.tell() >= cur_block.size() && !control.empty()) {
+ if (control.size() == 1) {
+ control.pop_back();
+ return 0;
+ }
+ control.pop_back();
+ activate_block();
+ }
+
+ // Fetch the next instruction.
+ return cur_block.getU8(pc);
+ };
+
+ // Fetch the next opcode.
+ OpCodes opcode = (OpCodes)next_byte();
+ if (control.empty() || !pc)
+ return pc.takeError();
+
+ LLDB_LOGV(GetLog(LLDBLog::DataFormatters),
+ "[eval {0}] opcode={1}, control={2}, data={3}", toString(sel),
+ toString(opcode), control.size(), toString(data));
+
+ // Various shorthands to improve the readability of error handling.
+#define TYPE_CHECK(...) \
+ if (auto error = TypeCheck(data, __VA_ARGS__)) \
+ return error;
+
+ auto error = [&](llvm::Twine msg) {
+ return llvm::createStringError(msg + "(opcode=" + toString(opcode) + ")");
+ };
+
+ switch (opcode) {
+ // Data stack manipulation.
+ case op_dup:
+ TYPE_CHECK(Any);
+ data.Push(data.back());
+ continue;
+ case op_drop:
+ TYPE_CHECK(Any);
+ data.pop_back();
+ continue;
+ case op_pick: {
+ TYPE_CHECK(UInt);
+ uint64_t idx = data.Pop<uint64_t>();
+ if (idx >= data.size())
+ return error("index out of bounds");
+ data.Push(data[idx]);
+ continue;
+ }
+ case op_over:
+ TYPE_CHECK(Any, Any);
+ data.Push(data[data.size() - 2]);
+ continue;
+ case op_swap: {
+ TYPE_CHECK(Any, Any);
+ auto x = data.PopAny();
+ auto y = data.PopAny();
+ data.Push(x);
+ data.Push(y);
+ continue;
+ }
+ case op_rot: {
+ TYPE_CHECK(Any, Any, Any);
+ auto z = data.PopAny();
+ auto y = data.PopAny();
+ auto x = data.PopAny();
+ data.Push(z);
+ data.Push(x);
+ data.Push(y);
+ continue;
+ }
+
+ // Control stack manipulation.
+ case op_begin: {
+ uint64_t length = cur_block.getULEB128(pc);
+ if (!pc)
+ return pc.takeError();
+ llvm::StringRef block = cur_block.getBytes(pc, length);
+ if (!pc)
+ return pc.takeError();
+ control.push_back(block);
+ continue;
+ }
+ case op_if:
+ TYPE_CHECK(UInt);
+ if (data.Pop<uint64_t>() != 0) {
+ if (!cur_block.size())
+ return error("empty control stack");
+ activate_block();
+ } else
+ control.pop_back();
+ continue;
+ case op_ifelse:
+ TYPE_CHECK(UInt);
+ if (cur_block.size() < 2)
+ return error("empty control stack");
+ if (data.Pop<uint64_t>() == 0)
+ control[control.size() - 2] = control.back();
+ control.pop_back();
+ activate_block();
+ continue;
+
+ // Literals.
+ case op_lit_uint:
+ data.Push(cur_block.getULEB128(pc));
+ continue;
+ case op_lit_int:
+ data.Push(cur_block.getSLEB128(pc));
+ continue;
+ case op_lit_selector:
+ data.Push(Selectors(cur_block.getU8(pc)));
+ continue;
+ case op_lit_string: {
+ uint64_t length = cur_block.getULEB128(pc);
+ llvm::StringRef bytes = cur_block.getBytes(pc, length);
+ data.Push(bytes.str());
+ continue;
+ }
+ case op_as_uint: {
+ TYPE_CHECK(Int);
+ uint64_t casted;
+ int64_t val = data.Pop<int64_t>();
+ memcpy(&casted, &val, sizeof(val));
+ data.Push(casted);
+ continue;
+ }
+ case op_as_int: {
+ TYPE_CHECK(UInt);
+ int64_t casted;
+ uint64_t val = data.Pop<uint64_t>();
+ memcpy(&casted, &val, sizeof(val));
+ data.Push(casted);
+ continue;
+ }
+ case op_is_null: {
+ TYPE_CHECK(Object);
+ data.Push(data.Pop<ValueObjectSP>() ? (uint64_t)0 : (uint64_t)1);
+ continue;
+ }
+
+ // Arithmetic, logic, etc.
+#define BINOP_IMPL(OP, CHECK_ZERO) \
+ { \
+ TYPE_CHECK(Any, Any); \
+ auto y = data.PopAny(); \
+ if (std::holds_alternative<uint64_t>(y)) { \
+ if (CHECK_ZERO && !std::get<uint64_t>(y)) \
+ return error(#OP " by zero"); \
+ TYPE_CHECK(UInt); \
+ data.Push((uint64_t)(data.Pop<uint64_t>() OP std::get<uint64_t>(y))); \
+ } else if (std::holds_alternative<int64_t>(y)) { \
+ if (CHECK_ZERO && !std::get<int64_t>(y)) \
+ return error(#OP " by zero"); \
+ TYPE_CHECK(Int); \
+ data.Push((int64_t)(data.Pop<int64_t>() OP std::get<int64_t>(y))); \
+ } else \
+ return error("unsupported data types"); \
+ }
+#define BINOP(OP) BINOP_IMPL(OP, false)
+#define BINOP_CHECKZERO(OP) BINOP_IMPL(OP, true)
+ case op_plus:
+ BINOP(+);
+ continue;
+ case op_minus:
+ BINOP(-);
+ continue;
+ case op_mul:
+ BINOP(*);
+ continue;
+ case op_div:
+ BINOP_CHECKZERO(/);
+ continue;
+ case op_mod:
+ BINOP_CHECKZERO(%);
+ continue;
+ case op_shl:
+#define SHIFTOP(OP, LEFT) \
+ { \
+ TYPE_CHECK(Any, UInt); \
+ uint64_t y = data.Pop<uint64_t>(); \
+ if (y > 64) \
+ return error("shift out of bounds"); \
+ if (std::holds_alternative<uint64_t>(data.back())) { \
+ uint64_t x = data.Pop<uint64_t>(); \
+ data.Push(x OP y); \
+ } else if (std::holds_alternative<int64_t>(data.back())) { \
+ int64_t x = data.Pop<int64_t>(); \
+ if (x < 0 && LEFT) \
+ return error("left shift of negative value"); \
+ if (y > 64) \
+ return error("shift out of bounds"); \
+ data.Push(x OP y); \
+ } else \
+ return error("unsupported data types"); \
+ }
+ SHIFTOP(<<, true);
+ continue;
+ case op_shr:
+ SHIFTOP(>>, false);
+ continue;
+ case op_and:
+ BINOP(&);
+ continue;
+ case op_or:
+ BINOP(|);
+ continue;
+ case op_xor:
+ BINOP(^);
+ continue;
+ case op_not:
+ TYPE_CHECK(UInt);
+ data.Push(~data.Pop<uint64_t>());
+ continue;
+ case op_eq:
+ BINOP(==);
+ continue;
+ case op_neq:
+ BINOP(!=);
+ continue;
+ case op_lt:
+ BINOP(<);
+ continue;
+ case op_gt:
+ BINOP(>);
+ continue;
+ case op_le:
+ BINOP(<=);
+ continue;
+ case op_ge:
+ BINOP(>=);
+ continue;
+ case op_call: {
+ TYPE_CHECK(Selector);
+ Selectors sel = data.Pop<Selectors>();
+
+ // Shorthand to improve readability.
+#define POP_VALOBJ(VALOBJ) \
+ auto VALOBJ = data.Pop<ValueObjectSP>(); \
+ if (!VALOBJ) \
+ return error("null object");
+
+ auto sel_error = [&](const char *msg) {
+ return llvm::createStringError("{0} (opcode={1}, selector={2})", msg,
+ toString(opcode).c_str(),
+ toString(sel).c_str());
+ };
+
+ switch (sel) {
+ case sel_summary: {
+ TYPE_CHECK(Object);
+ POP_VALOBJ(valobj);
+ const char *summary = valobj->GetSummaryAsCString();
+ data.Push(summary ? std::string(valobj->GetSummaryAsCString())
+ : std::string());
+ break;
+ }
+ case sel_get_num_children: {
+ TYPE_CHECK(Object);
+ POP_VALOBJ(valobj);
+ auto result = valobj->GetNumChildren();
+ if (!result)
+ return result.takeError();
+ data.Push((uint64_t)*result);
+ break;
+ }
+ case sel_get_child_at_index: {
+ TYPE_CHECK(Object, UInt);
+ auto index = data.Pop<uint64_t>();
+ POP_VALOBJ(valobj);
+ data.Push(valobj->GetChildAtIndex(index));
+ break;
+ }
+ case sel_get_child_with_name: {
+ TYPE_CHECK(Object, String);
+ auto name = data.Pop<std::string>();
+ POP_VALOBJ(valobj);
+ data.Push(valobj->GetChildMemberWithName(name));
+ break;
+ }
+ case sel_get_child_index: {
+ TYPE_CHECK(Object, String);
+ auto name = data.Pop<std::string>();
+ POP_VALOBJ(valobj);
+ data.Push((uint64_t)valobj->GetIndexOfChildWithName(name));
+ break;
+ }
+ case sel_get_type: {
+ TYPE_CHECK(Object);
+ POP_VALOBJ(valobj);
+ // FIXME: do we need to control dynamic type resolution?
+ data.Push(valobj->GetTypeImpl().GetCompilerType(false));
+ break;
+ }
+ case sel_get_template_argument_type: {
+ TYPE_CHECK(Type, UInt);
+ auto index = data.Pop<uint64_t>();
+ auto type = data.Pop<CompilerType>();
+ // FIXME: There is more code in SBType::GetTemplateArgumentType().
+ data.Push(type.GetTypeTemplateArgument(index, true));
+ break;
+ }
+ case sel_get_value: {
+ TYPE_CHECK(Object);
+ POP_VALOBJ(valobj);
+ data.Push(std::string(valobj->GetValueAsCString()));
+ break;
+ }
+ case sel_get_value_as_unsigned: {
+ TYPE_CHECK(Object);
+ POP_VALOBJ(valobj);
+ bool success;
+ uint64_t val = valobj->GetValueAsUnsigned(0, &success);
+ data.Push(val);
+ if (!success)
+ return sel_error("failed to get value");
+ break;
+ }
+ case sel_get_value_as_signed: {
+ TYPE_CHECK(Object);
+ POP_VALOBJ(valobj);
+ bool success;
+ int64_t val = valobj->GetValueAsSigned(0, &success);
+ data.Push(val);
+ if (!success)
+ return sel_error("failed to get value");
+ break;
+ }
+ case sel_get_value_as_address: {
+ TYPE_CHECK(Object);
+ POP_VALOBJ(valobj);
+ bool success;
+ uint64_t addr = valobj->GetValueAsUnsigned(0, &success);
+ if (!success)
+ return sel_error("failed to get value");
+ if (auto process_sp = valobj->GetProcessSP())
+ addr = process_sp->FixDataAddress(addr);
+ data.Push(addr);
+ break;
+ }
+ case sel_cast: {
+ TYPE_CHECK(Object, Type);
+ auto type = data.Pop<CompilerType>();
+ POP_VALOBJ(valobj);
+ data.Push(valobj->Cast(type));
+ break;
+ }
+ case sel_strlen: {
+ TYPE_CHECK(String);
+ data.Push((uint64_t)data.Pop<std::string>().size());
+ break;
+ }
+ case sel_fmt: {
+ TYPE_CHECK(String);
+ if (auto error = FormatImpl(data))
+ return error;
+ break;
+ }
+ default:
+ return sel_error("selector not implemented");
+ }
+ continue;
+ }
+ }
+ return error("opcode not implemented");
+ }
+ return pc.takeError();
+}
+} // namespace FormatterBytecode
+
+} // namespace lldb_private
diff --git a/lldb/source/DataFormatters/FormatterBytecode.def b/lldb/source/DataFormatters/FormatterBytecode.def
new file mode 100644
index 000000000000..c6645631fa00
--- /dev/null
+++ b/lldb/source/DataFormatters/FormatterBytecode.def
@@ -0,0 +1,101 @@
+//===-- FormatterBytecode.def -----------------------------------*- 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 DEFINE_OPCODE
+#define DEFINE_OPCODE(OP, MNEMONIC, NAME)
+#endif
+#ifndef DEFINE_SELECTOR
+#define DEFINE_SELECTOR(ID, NAME)
+#endif
+#ifndef DEFINE_SIGNATURE
+#define DEFINE_SIGNATURE(ID, NAME)
+#endif
+
+// Opcodes.
+DEFINE_OPCODE(0x01, "dup", dup)
+DEFINE_OPCODE(0x02, "drop", drop)
+DEFINE_OPCODE(0x03, "pick", pick)
+DEFINE_OPCODE(0x04, "over", over)
+DEFINE_OPCODE(0x05, "swap", swap)
+DEFINE_OPCODE(0x06, "rot", rot)
+
+DEFINE_OPCODE(0x10, "{", begin)
+DEFINE_OPCODE(0x11, "if", if)
+DEFINE_OPCODE(0x12, "ifelse", ifelse)
+
+DEFINE_OPCODE(0x20, nullptr, lit_uint)
+DEFINE_OPCODE(0x21, nullptr, lit_int)
+DEFINE_OPCODE(0x22, nullptr, lit_string)
+DEFINE_OPCODE(0x23, nullptr, lit_selector)
+
+DEFINE_OPCODE(0x2a, "as_int", as_int)
+DEFINE_OPCODE(0x2b, "as_uint", as_uint)
+DEFINE_OPCODE(0x2c, "is_null", is_null)
+
+DEFINE_OPCODE(0x30, "+", plus)
+DEFINE_OPCODE(0x31, "-", minus)
+DEFINE_OPCODE(0x32, "*", mul)
+DEFINE_OPCODE(0x33, "/", div)
+DEFINE_OPCODE(0x34, "%", mod)
+DEFINE_OPCODE(0x35, "<<", shl)
+DEFINE_OPCODE(0x36, ">>", shr)
+
+DEFINE_OPCODE(0x40, "&", and)
+DEFINE_OPCODE(0x41, "|", or)
+DEFINE_OPCODE(0x42, "^", xor)
+DEFINE_OPCODE(0x43, "~", not)
+
+DEFINE_OPCODE(0x50, "=", eq)
+DEFINE_OPCODE(0x51, "!=", neq)
+DEFINE_OPCODE(0x52, "<", lt)
+DEFINE_OPCODE(0x53, ">", gt)
+DEFINE_OPCODE(0x54, "=<", le)
+DEFINE_OPCODE(0x55, ">=", ge)
+
+DEFINE_OPCODE(0x60, "call", call)
+
+// Selectors.
+DEFINE_SELECTOR(0x00, summary)
+DEFINE_SELECTOR(0x01, type_summary)
+
+DEFINE_SELECTOR(0x10, get_num_children)
+DEFINE_SELECTOR(0x11, get_child_at_index)
+DEFINE_SELECTOR(0x12, get_child_with_name)
+DEFINE_SELECTOR(0x13, get_child_index)
+DEFINE_SELECTOR(0x15, get_type)
+DEFINE_SELECTOR(0x16, get_template_argument_type)
+DEFINE_SELECTOR(0x17, cast)
+
+DEFINE_SELECTOR(0x20, get_value)
+DEFINE_SELECTOR(0x21, get_value_as_unsigned)
+DEFINE_SELECTOR(0x22, get_value_as_signed)
+DEFINE_SELECTOR(0x23, get_value_as_address)
+
+DEFINE_SELECTOR(0x40, read_memory_byte)
+DEFINE_SELECTOR(0x41, read_memory_uint32)
+DEFINE_SELECTOR(0x42, read_memory_int32)
+DEFINE_SELECTOR(0x43, read_memory_unsigned)
+DEFINE_SELECTOR(0x44, read_memory_signed)
+DEFINE_SELECTOR(0x45, read_memory_address)
+DEFINE_SELECTOR(0x46, read_memory)
+
+DEFINE_SELECTOR(0x50, fmt)
+DEFINE_SELECTOR(0x51, sprintf)
+DEFINE_SELECTOR(0x52, strlen)
+
+// Formatter signatures.
+DEFINE_SIGNATURE(0, summary)
+DEFINE_SIGNATURE(1, init)
+DEFINE_SIGNATURE(2, get_num_children)
+DEFINE_SIGNATURE(3, get_child_index)
+DEFINE_SIGNATURE(4, get_child_at_index)
+DEFINE_SIGNATURE(5, get_value)
+
+#undef DEFINE_OPCODE
+#undef DEFINE_SELECTOR
+#undef DEFINE_SIGNATURE
diff --git a/lldb/source/DataFormatters/FormatterBytecode.h b/lldb/source/DataFormatters/FormatterBytecode.h
new file mode 100644
index 000000000000..21454d9c7e23
--- /dev/null
+++ b/lldb/source/DataFormatters/FormatterBytecode.h
@@ -0,0 +1,64 @@
+//===-- FormatterBytecode.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
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/DataFormatters/TypeSummary.h"
+#include "lldb/Symbol/CompilerType.h"
+
+namespace lldb_private {
+
+namespace FormatterBytecode {
+
+enum DataType : uint8_t { Any, String, Int, UInt, Object, Type, Selector };
+
+enum OpCodes : uint8_t {
+#define DEFINE_OPCODE(OP, MNEMONIC, NAME) op_##NAME = OP,
+#include "FormatterBytecode.def"
+#undef DEFINE_OPCODE
+};
+
+enum Selectors : uint8_t {
+#define DEFINE_SELECTOR(ID, NAME) sel_##NAME = ID,
+#include "FormatterBytecode.def"
+#undef DEFINE_SELECTOR
+};
+
+enum Signatures : uint8_t {
+#define DEFINE_SIGNATURE(ID, NAME) sig_##NAME = ID,
+#include "FormatterBytecode.def"
+#undef DEFINE_SIGNATURE
+};
+
+using ControlStackElement = llvm::StringRef;
+using DataStackElement =
+ std::variant<std::string, uint64_t, int64_t, lldb::ValueObjectSP,
+ CompilerType, Selectors>;
+struct DataStack : public std::vector<DataStackElement> {
+ DataStack() = default;
+ DataStack(lldb::ValueObjectSP initial_value)
+ : std::vector<DataStackElement>({initial_value}) {}
+ void Push(DataStackElement el) { push_back(el); }
+ template <typename T> T Pop() {
+ T el = std::get<T>(back());
+ pop_back();
+ return el;
+ }
+ DataStackElement PopAny() {
+ DataStackElement el = back();
+ pop_back();
+ return el;
+ }
+};
+llvm::Error Interpret(std::vector<ControlStackElement> &control,
+ DataStack &data, Selectors sel);
+} // namespace FormatterBytecode
+
+std::string toString(FormatterBytecode::OpCodes op);
+std::string toString(FormatterBytecode::Selectors sel);
+std::string toString(FormatterBytecode::Signatures sig);
+
+} // namespace lldb_private
diff --git a/lldb/source/DataFormatters/FormatterSection.cpp b/lldb/source/DataFormatters/FormatterSection.cpp
new file mode 100644
index 000000000000..1de633f4998e
--- /dev/null
+++ b/lldb/source/DataFormatters/FormatterSection.cpp
@@ -0,0 +1,170 @@
+//===-- FormatterBytecode.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 "FormatterBytecode.h"
+#include "lldb/Core/Module.h"
+#include "lldb/DataFormatters/DataVisualization.h"
+#include "lldb/Utility/LLDBLog.h"
+
+using namespace lldb;
+
+namespace lldb_private {
+static void ForEachFormatterInModule(
+ Module &module, SectionType section_type,
+ std::function<void(llvm::DataExtractor, llvm::StringRef)> fn) {
+ auto *sections = module.GetSectionList();
+ if (!sections)
+ return;
+
+ auto section_sp = sections->FindSectionByType(section_type, true);
+ if (!section_sp)
+ return;
+
+ TypeCategoryImplSP category;
+ DataVisualization::Categories::GetCategory(ConstString("default"), category);
+
+ // The type summary record is serialized as follows.
+ //
+ // Each record contains, in order:
+ // * Version number of the record format
+ // * The remaining size of the record
+ // * The size of the type identifier
+ // * The type identifier, either a type name, or a regex
+ // * The size of the entry
+ // * The entry
+ //
+ // Integers are encoded using ULEB.
+ //
+ // Strings are encoded with first a length (ULEB), then the string contents,
+ // and lastly a null terminator. The length includes the null.
+
+ DataExtractor lldb_extractor;
+ auto section_size = section_sp->GetSectionData(lldb_extractor);
+ llvm::DataExtractor section = lldb_extractor.GetAsLLVM();
+ bool le = section.isLittleEndian();
+ uint8_t addr_size = section.getAddressSize();
+ llvm::DataExtractor::Cursor cursor(0);
+ while (cursor && cursor.tell() < section_size) {
+ while (cursor && cursor.tell() < section_size) {
+ // Skip over 0 padding.
+ if (section.getU8(cursor) == 0)
+ continue;
+ cursor.seek(cursor.tell() - 1);
+ break;
+ }
+ uint64_t version = section.getULEB128(cursor);
+ uint64_t record_size = section.getULEB128(cursor);
+ if (version == 1) {
+ llvm::DataExtractor record(section.getData().drop_front(cursor.tell()),
+ le, addr_size);
+ llvm::DataExtractor::Cursor cursor(0);
+ uint64_t type_size = record.getULEB128(cursor);
+ llvm::StringRef type_name = record.getBytes(cursor, type_size);
+ llvm::Error error = cursor.takeError();
+ if (!error)
+ fn(llvm::DataExtractor(record.getData().drop_front(cursor.tell()), le,
+ addr_size),
+ type_name);
+ else
+ LLDB_LOG_ERROR(GetLog(LLDBLog::DataFormatters), std::move(error),
+ "{0}");
+ } else {
+ // Skip unsupported record.
+ LLDB_LOG(
+ GetLog(LLDBLog::DataFormatters),
+ "Skipping unsupported embedded type summary of version {0} in {1}.",
+ version, module.GetFileSpec());
+ }
+ section.skip(cursor, record_size);
+ }
+ if (!cursor)
+ LLDB_LOG_ERROR(GetLog(LLDBLog::DataFormatters), cursor.takeError(), "{0}");
+}
+
+void LoadTypeSummariesForModule(ModuleSP module_sp) {
+ ForEachFormatterInModule(
+ *module_sp, eSectionTypeLLDBTypeSummaries,
+ [&](llvm::DataExtractor extractor, llvm::StringRef type_name) {
+ TypeCategoryImplSP category;
+ DataVisualization::Categories::GetCategory(ConstString("default"),
+ category);
+ // The type summary record is serialized as follows.
+ //
+ // * The size of the summary string
+ // * The summary string
+ //
+ // Integers are encoded using ULEB.
+ llvm::DataExtractor::Cursor cursor(0);
+ uint64_t summary_size = extractor.getULEB128(cursor);
+ llvm::StringRef summary_string =
+ extractor.getBytes(cursor, summary_size);
+ if (!cursor) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::DataFormatters), cursor.takeError(),
+ "{0}");
+ return;
+ }
+ if (type_name.empty() || summary_string.empty()) {
+ LLDB_LOG(GetLog(LLDBLog::DataFormatters),
+ "Missing string(s) in embedded type summary in {0}, "
+ "type_name={1}, summary={2}",
+ module_sp->GetFileSpec(), type_name, summary_string);
+ return;
+ }
+ TypeSummaryImpl::Flags flags;
+ auto summary_sp = std::make_shared<StringSummaryFormat>(
+ flags, summary_string.str().c_str());
+ FormatterMatchType match_type = eFormatterMatchExact;
+ if (type_name.front() == '^')
+ match_type = eFormatterMatchRegex;
+ category->AddTypeSummary(type_name, match_type, summary_sp);
+ LLDB_LOG(GetLog(LLDBLog::DataFormatters),
+ "Loaded embedded type summary for '{0}' from {1}.", type_name,
+ module_sp->GetFileSpec());
+ });
+}
+
+void LoadFormattersForModule(ModuleSP module_sp) {
+ ForEachFormatterInModule(
+ *module_sp, eSectionTypeLLDBFormatters,
+ [&](llvm::DataExtractor extractor, llvm::StringRef type_name) {
+ // * Function signature (1 byte)
+ // * Length of the program (ULEB128)
+ // * The program bytecode
+ TypeCategoryImplSP category;
+ DataVisualization::Categories::GetCategory(ConstString("default"),
+ category);
+ llvm::DataExtractor::Cursor cursor(0);
+ uint64_t flags = extractor.getULEB128(cursor);
+ while (cursor && cursor.tell() < extractor.size()) {
+ uint8_t signature = extractor.getU8(cursor);
+ uint64_t size = extractor.getULEB128(cursor);
+ llvm::StringRef bytecode = extractor.getBytes(cursor, size);
+ if (!cursor) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::DataFormatters), cursor.takeError(),
+ "{0}");
+ return;
+ }
+ if (signature == 0) {
+ auto summary_sp = std::make_shared<BytecodeSummaryFormat>(
+ TypeSummaryImpl::Flags(flags),
+ llvm::MemoryBuffer::getMemBufferCopy(bytecode));
+ FormatterMatchType match_type = eFormatterMatchExact;
+ if (type_name.front() == '^')
+ match_type = eFormatterMatchRegex;
+ category->AddTypeSummary(type_name, match_type, summary_sp);
+ LLDB_LOG(GetLog(LLDBLog::DataFormatters),
+ "Loaded embedded type summary for '{0}' from {1}.",
+ type_name, module_sp->GetFileSpec());
+ } else
+ LLDB_LOG(GetLog(LLDBLog::DataFormatters),
+ "Unsupported formatter signature {0} for '{1}' in {2}",
+ signature, type_name, module_sp->GetFileSpec());
+ }
+ });
+}
+} // namespace lldb_private
diff --git a/lldb/source/DataFormatters/TypeSummary.cpp b/lldb/source/DataFormatters/TypeSummary.cpp
index 3538c1b60c8e..2c863b364538 100644
--- a/lldb/source/DataFormatters/TypeSummary.cpp
+++ b/lldb/source/DataFormatters/TypeSummary.cpp
@@ -8,9 +8,7 @@
#include "lldb/DataFormatters/TypeSummary.h"
-
-
-
+#include "FormatterBytecode.h"
#include "lldb/lldb-enumerations.h"
#include "lldb/lldb-public.h"
@@ -58,6 +56,8 @@ std::string TypeSummaryImpl::GetSummaryKindName() {
return "python";
case Kind::eInternal:
return "c++";
+ case Kind::eBytecode:
+ return "bytecode";
}
}
@@ -230,3 +230,74 @@ std::string ScriptSummaryFormat::GetDescription() {
}
std::string ScriptSummaryFormat::GetName() { return m_script_formatter_name; }
+
+BytecodeSummaryFormat::BytecodeSummaryFormat(
+ const TypeSummaryImpl::Flags &flags,
+ std::unique_ptr<llvm::MemoryBuffer> bytecode)
+ : TypeSummaryImpl(Kind::eBytecode, flags), m_bytecode(std::move(bytecode)) {
+}
+
+bool BytecodeSummaryFormat::FormatObject(ValueObject *valobj,
+ std::string &retval,
+ const TypeSummaryOptions &options) {
+ if (!valobj)
+ return false;
+
+ TargetSP target_sp(valobj->GetTargetSP());
+
+ if (!target_sp) {
+ retval.assign("error: no target");
+ return false;
+ }
+
+ std::vector<FormatterBytecode::ControlStackElement> control(
+ {m_bytecode->getBuffer()});
+ FormatterBytecode::DataStack data({valobj->GetSP()});
+ llvm::Error error = FormatterBytecode::Interpret(
+ control, data, FormatterBytecode::sel_summary);
+ if (error) {
+ retval = llvm::toString(std::move(error));
+ return false;
+ }
+ if (!data.size()) {
+ retval = "empty stack";
+ return false;
+ }
+ auto &top = data.back();
+ retval = "";
+ llvm::raw_string_ostream os(retval);
+ if (auto s = std::get_if<std::string>(&top))
+ os << *s;
+ else if (auto u = std::get_if<uint64_t>(&top))
+ os << *u;
+ else if (auto i = std::get_if<int64_t>(&top))
+ os << *i;
+ else if (auto valobj = std::get_if<ValueObjectSP>(&top)) {
+ if (!valobj->get())
+ os << "empty object";
+ else
+ os << valobj->get()->GetValueAsCString();
+ } else if (auto type = std::get_if<CompilerType>(&top)) {
+ os << type->TypeDescription();
+ } else if (auto sel = std::get_if<FormatterBytecode::Selectors>(&top)) {
+ os << toString(*sel);
+ }
+ return true;
+}
+
+std::string BytecodeSummaryFormat::GetDescription() {
+ StreamString sstr;
+ sstr.Printf("%s%s%s%s%s%s%s\n ", Cascades() ? "" : " (not cascading)",
+ !DoesPrintChildren(nullptr) ? "" : " (show children)",
+ !DoesPrintValue(nullptr) ? " (hide value)" : "",
+ IsOneLiner() ? " (one-line printout)" : "",
+ SkipsPointers() ? " (skip pointers)" : "",
+ SkipsReferences() ? " (skip references)" : "",
+ HideNames(nullptr) ? " (hide member names)" : "");
+ // FIXME: sstr.PutCString(disassembly);
+ return std::string(sstr.GetString());
+}
+
+std::string BytecodeSummaryFormat::GetName() {
+ return "LLDB bytecode formatter";
+}