blob: 1b47b57f6c5a7ebab37b61325daa45015938c182 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/developer/debug/zxdb/expr/pretty_type_manager.h"
#include <lib/syslog/cpp/macros.h>
#include <limits>
#include "src/developer/debug/zxdb/common/string_util.h"
#include "src/developer/debug/zxdb/expr/format_node.h"
#include "src/developer/debug/zxdb/expr/pretty_rust_tuple.h"
#include "src/developer/debug/zxdb/expr/pretty_std_string.h"
#include "src/developer/debug/zxdb/expr/pretty_tree.h"
#include "src/developer/debug/zxdb/expr/pretty_type.h"
#include "src/developer/debug/zxdb/symbols/collection.h"
namespace zxdb {
namespace {
using GetterList = std::initializer_list<std::pair<std::string, std::string>>;
// Used for internal hardcoded type globs, this parses the given identifier string and asserts if it
// can't be parsed. Since the built-in globs should always be parseable, this helps clean up the
// syntax.
IdentifierGlob InternalGlob(const char* glob) {
IdentifierGlob result;
Err err = result.Init(glob);
FX_CHECK(!err.has_error()) << "Internal pretty-printer parse failure for\" " << glob
<< "\": " << err.msg();
return result;
}
} // namespace
PrettyTypeManager::PrettyTypeManager() {
AddDefaultCppPrettyTypes();
AddDefaultRustPrettyTypes();
AddDefaultFuchsiaCppPrettyTypes();
}
PrettyTypeManager::~PrettyTypeManager() = default;
void PrettyTypeManager::Add(ExprLanguage lang, IdentifierGlob glob,
std::unique_ptr<PrettyType> pretty) {
switch (lang) {
case ExprLanguage::kC:
cpp_.emplace_back(std::move(glob), std::move(pretty));
break;
case ExprLanguage::kRust:
rust_.emplace_back(std::move(glob), std::move(pretty));
break;
}
}
PrettyType* PrettyTypeManager::GetForType(const Type* in_type) const {
if (!in_type)
return nullptr;
// Strip const-volatile qualifiers for the name comparison, but don't follow typedefs or make
// the type concrete. Typedefs will change the name and some pretty-printers are defined for
// typedefs of other values. We need to maintain the original name for this comparison.
const Type* type = in_type->StripCV();
ParsedIdentifier type_ident = ToParsedIdentifier(type->GetIdentifier());
// Pick the language-specific lookup.
const auto* lookup = type->GetLanguage() == DwarfLang::kRust ? &rust_ : &cpp_;
// Tracks the best one found so far. Lower scores are better.
int best_score = std::numeric_limits<int>::max();
PrettyType* best_type = nullptr;
for (const auto& [glob, pretty_ptr] : *lookup) {
if (auto match = glob.Matches(type_ident)) {
if (*match < best_score) {
// Got a new best match.
best_score = *match;
best_type = pretty_ptr.get();
}
}
}
if (best_type) {
return best_type;
}
if (type->GetLanguage() == DwarfLang::kRust) {
const Collection* coll = type->As<Collection>();
if (!coll) {
return nullptr;
}
auto special = coll->GetSpecialType();
if (special == Collection::kRustTuple || special == Collection::kRustTupleStruct) {
return rust_tuple_type_.get();
}
}
return nullptr;
}
bool PrettyTypeManager::Format(FormatNode* node, const Type* type, const FormatOptions& options,
const fxl::RefPtr<EvalContext>& context,
fit::deferred_callback& cb) const {
if (PrettyType* pretty = GetForType(type)) {
pretty->Format(node, options, context, std::move(cb));
return true;
}
return false;
}
void PrettyTypeManager::AddDefaultCppPrettyTypes() {
// std::string
//
// Because of the weirdness of std::string's definition, we need to check for both the typedef
// source and the resolved value. The typedef won't always map to something.
//
// Furthermore, different versions of the compiler or library have included the "__2" on the
// template type names or not, so we also encode both variants there.
cpp_.emplace_back(
InternalGlob(
"std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char> >"),
std::make_unique<PrettyStdString>());
cpp_.emplace_back(
InternalGlob("std::__2::basic_string<char, std::char_traits<char>, std::allocator<char> >"),
std::make_unique<PrettyStdString>());
cpp_.emplace_back(InternalGlob("std::__2::string"), std::make_unique<PrettyStdString>());
// std::string_view. Like std::string, we encode variants for both "__2" and not for the nested
// template type names.
PrettyHeapString pretty_string_view("__data", "__size",
GetterList{{"back", "__data[__size - 1]"},
{"data", "__data"},
{"front", "*__data"},
{"size", "__size"},
{"length", "__size"},
{"empty", "__size == 0"}});
cpp_.emplace_back(InternalGlob("std::__2::basic_string_view<char, std::char_traits<char> >"),
std::make_unique<PrettyHeapString>(pretty_string_view));
cpp_.emplace_back(InternalGlob("std::__2::basic_string_view<char, std::__2::char_traits<char> >"),
std::make_unique<PrettyHeapString>(pretty_string_view));
// std::vector
//
// Note that we don't have vector<bool> yet but need to add a pretty-printer for it to
// preferentially match over the non-bool version (the longest match will be taken). This will
// result in errors but it will be better than misleading results.
cpp_.emplace_back(
InternalGlob("std::__2::vector<*>"),
std::make_unique<PrettyArray>("__begin_", "__end_ - __begin_",
GetterList{{"size", "__end_ - __begin_"},
{"capacity", "__end_cap_.__value_ - __begin_"},
{"data", "__begin_"},
{"empty", "__end_ == __begin_"},
{"front", "*__begin_"},
{"back", "__begin_[__end_ - __begin_ - 1]"}}));
cpp_.emplace_back(InternalGlob("std::__2::vector<bool, *>"),
std::make_unique<PrettyArray>("vector_bool_printer_not_implemented_yet",
"vector_bool_printer_not_implemented_yet"));
// Smart pointers.
cpp_.emplace_back(InternalGlob("std::__2::unique_ptr<*>"),
std::make_unique<PrettyPointer>("__ptr_.__value_"));
cpp_.emplace_back(InternalGlob("std::__2::shared_ptr<*>"),
std::make_unique<PrettyPointer>("__ptr_"));
cpp_.emplace_back(InternalGlob("std::__2::weak_ptr<*>"),
std::make_unique<PrettyPointer>("__ptr_"));
cpp_.emplace_back(InternalGlob("std::__2::optional<*>"),
std::make_unique<PrettyOptional>(
"std::optional", "__engaged_", "__val_", "std::nullopt",
GetterList{{"value", "__val_"}, {"has_value", "__engaged_"}}));
cpp_.emplace_back(
InternalGlob("std::__2::variant<*>"),
std::make_unique<PrettyRecursiveVariant>(
"std::variant", "__impl.__data", "__impl.__index", "__tail", "__head.__value",
"std::variant::valueless_by_exception()", GetterList({{"index", "__impl.__index"}})));
// Trees (std::set and std::map).
cpp_.emplace_back(InternalGlob("std::__2::set<*>"), std::make_unique<PrettyTree>("std::set"));
cpp_.emplace_back(InternalGlob("std::__2::map<*>"), std::make_unique<PrettyTree>("std::map"));
cpp_.emplace_back(InternalGlob("std::__2::__tree_iterator<*>"),
std::make_unique<PrettyTreeIterator>());
cpp_.emplace_back(InternalGlob("std::__2::__tree_const_iterator<*>"),
std::make_unique<PrettyTreeIterator>());
cpp_.emplace_back(InternalGlob("std::__2::__map_iterator<*>"),
std::make_unique<PrettyMapIterator>());
cpp_.emplace_back(InternalGlob("std::__2::__map_const_iterator<*>"),
std::make_unique<PrettyMapIterator>());
// std::atomic
cpp_.emplace_back(
InternalGlob("std::__2::atomic<*>"),
std::make_unique<PrettyWrappedValue>("std::atomic", "(", ")", "__a_.__a_value"));
// std::mutex. std::mutex has a member __m_ which is a __libcpp_mutex_t a.k.a. pthread_mutex_t.
// Our pthread implementation stores the owning thread handle (not koid) in the "_m_lock" member.
// Valid handles always have the low bit set. This is cleared to mark the contested state so we
// need to set it back to get the valid handle. This is delicate but the information is extremely
// useful for certain kinds of debugging.
cpp_.emplace_back(InternalGlob("std::__2::mutex"),
std::make_unique<PrettyStruct>(GetterList{
{"owning_thread_handle", "__m_._m_lock ? (__m_._m_lock | 1) : 0"}}));
// These locking primitives cause a lot of useless variable spew so just hide the internals. We
// can probably provide some more useful information with some research about their workings.
cpp_.emplace_back(InternalGlob("std::__2::condition_variable"),
std::make_unique<PrettyStruct>(GetterList{}));
cpp_.emplace_back(InternalGlob("std::__2::shared_mutex"),
std::make_unique<PrettyStruct>(GetterList{}));
// Streams. Show istringstreams as their current input location, ostringstreams and stringstreams
// as their full string contents. All other streams get elided as their contents is very long and
// not very interesting.
cpp_.emplace_back(InternalGlob("std::__2::basic_ostringstream<*>"),
std::make_unique<PrettyStruct>(GetterList{{"str", "__sb_.__str_"}}));
cpp_.emplace_back(InternalGlob("std::__2::basic_stringstream<*>"),
std::make_unique<PrettyStruct>(GetterList{{"str", "__sb_.__str_"}}));
cpp_.emplace_back(InternalGlob("std::__2::basic_istringstream<*>"),
std::make_unique<PrettyStruct>(GetterList{{"next", "__sb_.__ninp_"}}));
cpp_.emplace_back(InternalGlob("std::__2::basic_stringbuf<*>"),
std::make_unique<PrettyStruct>(GetterList{{"buf", "__str_"}}));
cpp_.emplace_back(InternalGlob("std::__2::basic_ostream<*>"),
std::make_unique<PrettyStruct>(GetterList{}));
cpp_.emplace_back(InternalGlob("std::__2::basic_istream<*>"),
std::make_unique<PrettyStruct>(GetterList{}));
cpp_.emplace_back(InternalGlob("std::__2::basic_istream<*>"),
std::make_unique<PrettyStruct>(GetterList{}));
cpp_.emplace_back(InternalGlob("std::__2::basic_streambuf<*>"),
std::make_unique<PrettyStruct>(GetterList{}));
}
void PrettyTypeManager::AddDefaultRustPrettyTypes() {
rust_tuple_type_ = std::make_unique<PrettyRustTuple>();
// Rust's "&str" type won't parse as an identifier, construct an Identifier manually.
rust_.emplace_back(IdentifierGlob(ParsedIdentifier(IdentifierQualification::kRelative,
ParsedIdentifierComponent("&str"))),
std::make_unique<PrettyHeapString>("data_ptr", "length",
GetterList{{"as_ptr", "data_ptr"},
{"as_mut_ptr", "data_ptr"},
{"len", "length"},
{"is_empty", "length == 0"}}));
rust_.emplace_back(
InternalGlob("alloc::string::String"),
std::make_unique<PrettyHeapString>("vec.buf.ptr.pointer as *u8", "vec.len",
GetterList{{"as_ptr", "vec.buf.ptr.pointer as *u8"},
{"as_mut_ptr", "vec.buf.ptr.pointer as *u8"},
{"len", "vec.len"},
{"capacity", "vec.buf.cap"},
{"is_empty", "vec.len == 0"}}));
rust_.emplace_back(InternalGlob("alloc::vec::Vec<*>"),
std::make_unique<PrettyArray>("buf.ptr.pointer", "len",
GetterList{{"as_ptr", "buf.ptr.pointer"},
{"as_mut_ptr", "buf.ptr.pointer"},
{"len", "len"},
{"capacity", "buf.cap"},
{"is_empty", "len == 0"}}));
// A BinaryHeap is a wrapper around a "Vec" named "data".
rust_.emplace_back(InternalGlob("alloc::collections::binary_heap::BinaryHeap<*>"),
std::make_unique<PrettyArray>("data.buf.ptr.pointer", "data.len",
GetterList{{"len", "data.len"},
{"capacity", "data.buf.cap"},
{"is_empty", "data.len == 0"}}));
// Smart pointers.
rust_.emplace_back(
InternalGlob("alloc::sync::Arc<*>"),
std::make_unique<PrettyPointer>("ptr.pointer",
GetterList{{"weak_count", "ptr.pointer->weak.v.value"},
{"strong_count", "ptr.pointer->strong.v.value"}}));
rust_.emplace_back(
InternalGlob("core::ptr::non_null::NonNull<*>"),
std::make_unique<PrettyPointer>("pointer", GetterList{{"as_ptr", "ptr.pointer"},
{"as_ref", "*ptr.pointer"},
{"as_mut", "*ptr.pointer"}}));
// Rust's wrapper for zx_status_t
rust_.emplace_back(InternalGlob("fuchsia_zircon_status::Status"),
std::make_unique<PrettyRustZirconStatus>());
}
void PrettyTypeManager::AddDefaultFuchsiaCppPrettyTypes() {
// Zircon.
cpp_.emplace_back(InternalGlob("zx_status_t"), std::make_unique<PrettyZxStatusT>());
// fbl
#define FBL_STRING_LENGTH_EXPRESSION \
"*reinterpret_cast<size_t*>(data_ - kDataFieldOffset + kLengthFieldOffset)"
cpp_.emplace_back(
InternalGlob("fbl::String"),
std::make_unique<PrettyHeapString>("data_", FBL_STRING_LENGTH_EXPRESSION,
GetterList{{"data", "data_"},
{"c_str", "data_"},
{"length", FBL_STRING_LENGTH_EXPRESSION},
{"size", FBL_STRING_LENGTH_EXPRESSION},
{"empty", "!" FBL_STRING_LENGTH_EXPRESSION}}));
cpp_.emplace_back(InternalGlob("cpp20::span<*>"),
std::make_unique<PrettyArray>(
"ptr_", "size_",
GetterList{{"size", "size_"}, {"data", "ptr_"}, {"empty", "size_ == 0"}}));
cpp_.emplace_back(InternalGlob("fbl::Vector<*>"),
std::make_unique<PrettyArray>("ptr_", "size_",
GetterList{{"size", "size_"},
{"get", "ptr_"},
{"capacity", "capacity_"},
{"is_empty", "size_ == 0"}}));
cpp_.emplace_back(InternalGlob("fbl::RefPtr<*>"),
std::make_unique<PrettyPointer>("ptr_", GetterList{{"get", "ptr_"}}));
cpp_.emplace_back(
InternalGlob("fbl::RefCounted<*>"),
std::make_unique<PrettyStruct>(GetterList{{"ref_count_", "ref_count_.__a_.__a_value"}}));
// fit
cpp_.emplace_back(InternalGlob("fit::variant<*>"),
std::make_unique<PrettyRecursiveVariant>(
"fit::variant", "storage_.base_", "storage_.index_", "rest", "value",
"fit::variant::empty", GetterList({{"index", "storage_.index_"}})));
// fxl
cpp_.emplace_back(InternalGlob("fxl::RefPtr<*>"),
std::make_unique<PrettyPointer>("ptr_", GetterList{{"get", "ptr_"}}));
cpp_.emplace_back(
InternalGlob("fxl::RefCountedThreadSafe<*>"),
std::make_unique<PrettyStruct>(GetterList{{"ref_count_", "ref_count_.__a_.__a_value"}}));
// stdcompat
cpp_.emplace_back(
InternalGlob("cpp17::optional<*>"),
std::make_unique<PrettyOptional>(
"cpp17::optional", "storage_.index_ == 0", "storage_.base_.value", "cpp17::nullopt",
GetterList{{"value", "storage_.base_.value"}, {"has_value", "storage_.index_ == 0"}}));
}
} // namespace zxdb