Jonas Devlieghere f866ef202c
[lldb] Bring more diagnostics in compliance with our coding standards (#190410)
The LLVM Coding Standards [1] specify that:

> [T]o match error message styles commonly produced by other tools,
> start the first sentence with a lowercase letter, and finish the last
> sentence without a period, if it would end in one otherwise.

Historically, that hasn't been something we've enforced in LLDB, but in
the past year or so I've started to pay more attention to this in code
reviews. This PR brings more error messages in compliance, further
increasing consistency.

I also adopted `createStringErrorV` where it improved the code as a
drive-by for lines I was already touching.

[1] https://llvm.org/docs/CodingStandards.html#error-and-warning-messages

Assisted-by: Claude Code
2026-04-05 10:41:47 -07:00

986 lines
32 KiB
C++

//===-- LibCxx.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 "LibCxx.h"
#include "Plugins/Language/CPlusPlus/CxxStringTypes.h"
#include "Plugins/Language/CPlusPlus/Generic.h"
#include "Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h"
#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Core/FormatEntity.h"
#include "lldb/DataFormatters/FormattersHelpers.h"
#include "lldb/DataFormatters/StringPrinter.h"
#include "lldb/DataFormatters/TypeSummary.h"
#include "lldb/DataFormatters/VectorIterator.h"
#include "lldb/Target/SectionLoadList.h"
#include "lldb/Target/Target.h"
#include "lldb/Utility/ConstString.h"
#include "lldb/Utility/DataBufferHeap.h"
#include "lldb/Utility/Endian.h"
#include "lldb/Utility/Status.h"
#include "lldb/Utility/Stream.h"
#include "lldb/ValueObject/ValueObject.h"
#include "lldb/ValueObject/ValueObjectConstResult.h"
#include "lldb/lldb-enumerations.h"
#include "lldb/lldb-forward.h"
#include "llvm/Support/ErrorExtras.h"
#include <optional>
#include <tuple>
using namespace lldb;
using namespace lldb_private;
using namespace lldb_private::formatters;
static void consumeInlineNamespace(llvm::StringRef &name) {
// Delete past an inline namespace, if any: __[a-zA-Z0-9_]+::
auto scratch = name;
if (scratch.consume_front("__") &&
std::isalnum(static_cast<unsigned char>(scratch[0]))) {
scratch = scratch.drop_while(
[](char c) { return std::isalnum(static_cast<unsigned char>(c)); });
if (scratch.consume_front("::")) {
// Successfully consumed a namespace.
name = scratch;
}
}
}
bool lldb_private::formatters::isStdTemplate(ConstString type_name,
llvm::StringRef type) {
llvm::StringRef name = type_name.GetStringRef();
// The type name may be prefixed with `std::__<inline-namespace>::`.
if (name.consume_front("std::"))
consumeInlineNamespace(name);
return name.consume_front(type) && name.starts_with("<");
}
lldb::ValueObjectSP lldb_private::formatters::GetChildMemberWithName(
ValueObject &obj, llvm::ArrayRef<ConstString> alternative_names) {
for (ConstString name : alternative_names) {
lldb::ValueObjectSP child_sp = obj.GetChildMemberWithName(name);
if (child_sp)
return child_sp;
}
return {};
}
lldb::ValueObjectSP
lldb_private::formatters::GetFirstValueOfLibCXXCompressedPair(
ValueObject &pair) {
ValueObjectSP value;
ValueObjectSP first_child = pair.GetChildAtIndex(0);
if (first_child)
value = first_child->GetChildMemberWithName("__value_");
if (!value) {
// pre-c88580c member name
value = pair.GetChildMemberWithName("__first_");
}
return value;
}
lldb::ValueObjectSP
lldb_private::formatters::GetSecondValueOfLibCXXCompressedPair(
ValueObject &pair) {
ValueObjectSP value;
if (pair.GetNumChildrenIgnoringErrors() > 1) {
ValueObjectSP second_child = pair.GetChildAtIndex(1);
if (second_child) {
value = second_child->GetChildMemberWithName("__value_");
}
}
if (!value) {
// pre-c88580c member name
value = pair.GetChildMemberWithName("__second_");
}
return value;
}
std::pair<lldb::ValueObjectSP, bool>
lldb_private::formatters::GetValueOrOldCompressedPair(
ValueObject &obj, llvm::StringRef child_name,
llvm::StringRef compressed_pair_name) {
auto is_old_compressed_pair = [](ValueObject &pair_obj) -> bool {
return isStdTemplate(pair_obj.GetTypeName(), "__compressed_pair");
};
ValueObjectSP node_sp(obj.GetChildMemberWithName(child_name));
if (node_sp)
return {node_sp, is_old_compressed_pair(*node_sp)};
// Try the even older __compressed_pair layout.
assert(!compressed_pair_name.empty());
node_sp = obj.GetChildMemberWithName(compressed_pair_name);
// Unrecognized layout (possibly older than LLDB supports).
if (!node_sp)
return {nullptr, false};
// Expected old compressed_pair layout, but got something else.
if (!is_old_compressed_pair(*node_sp))
return {nullptr, false};
return {node_sp, true};
}
bool lldb_private::formatters::LibcxxFunctionSummaryProvider(
ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue());
if (!valobj_sp)
return false;
ExecutionContext exe_ctx(valobj_sp->GetExecutionContextRef());
Process *process = exe_ctx.GetProcessPtr();
if (process == nullptr)
return false;
CPPLanguageRuntime *cpp_runtime = CPPLanguageRuntime::Get(*process);
if (!cpp_runtime)
return false;
CPPLanguageRuntime::LibCppStdFunctionCallableInfo callable_info =
cpp_runtime->FindLibCppStdFunctionCallableInfo(valobj_sp);
switch (callable_info.callable_case) {
case CPPLanguageRuntime::LibCppStdFunctionCallableCase::Invalid:
stream.Printf(" __f_ = %" PRIu64, callable_info.member_f_pointer_value);
return false;
break;
case CPPLanguageRuntime::LibCppStdFunctionCallableCase::Lambda:
stream.Printf(
" Lambda in File %s at Line %u",
callable_info.callable_line_entry.GetFile().GetFilename().GetCString(),
callable_info.callable_line_entry.line);
break;
case CPPLanguageRuntime::LibCppStdFunctionCallableCase::CallableObject:
stream.Printf(
" Function in File %s at Line %u",
callable_info.callable_line_entry.GetFile().GetFilename().GetCString(),
callable_info.callable_line_entry.line);
break;
case CPPLanguageRuntime::LibCppStdFunctionCallableCase::FreeOrMemberFunction:
stream.Printf(" Function = %s ",
callable_info.callable_symbol.GetName().GetCString());
break;
}
return true;
}
bool lldb_private::formatters::LibcxxSmartPointerSummaryProvider(
ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue());
if (!valobj_sp)
return false;
ValueObjectSP ptr_sp(valobj_sp->GetChildMemberWithName("__ptr_"));
ValueObjectSP ctrl_sp(valobj_sp->GetChildMemberWithName("__cntrl_"));
if (!ctrl_sp || !ptr_sp)
return false;
DumpCxxSmartPtrPointerSummary(stream, *ptr_sp, options);
bool success;
uint64_t ctrl_addr = ctrl_sp->GetValueAsUnsigned(0, &success);
// Empty control field. We're done.
if (!success || ctrl_addr == 0)
return true;
if (auto count_sp = ctrl_sp->GetChildMemberWithName("__shared_owners_")) {
bool success;
uint64_t count = count_sp->GetValueAsUnsigned(0, &success);
if (!success)
return false;
// std::shared_ptr releases the underlying resource when the
// __shared_owners_ count hits -1. So `__shared_owners_ == 0` indicates 1
// owner. Hence add +1 here.
stream.Printf(" strong=%" PRIu64, count + 1);
}
if (auto weak_count_sp =
ctrl_sp->GetChildMemberWithName("__shared_weak_owners_")) {
bool success;
uint64_t count = weak_count_sp->GetValueAsUnsigned(0, &success);
if (!success)
return false;
// Unlike __shared_owners_, __shared_weak_owners_ indicates the exact
// std::weak_ptr reference count.
stream.Printf(" weak=%" PRIu64, count);
}
return true;
}
bool lldb_private::formatters::LibcxxUniquePointerSummaryProvider(
ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue());
if (!valobj_sp)
return false;
auto [ptr_sp, is_compressed_pair] =
GetValueOrOldCompressedPair(*valobj_sp, "__ptr_", "__ptr_");
if (!ptr_sp)
return false;
if (is_compressed_pair)
ptr_sp = GetFirstValueOfLibCXXCompressedPair(*ptr_sp);
if (!ptr_sp)
return false;
DumpCxxSmartPtrPointerSummary(stream, *ptr_sp, options);
return true;
}
static std::optional<int64_t> LibcxxExtractOrderingValue(ValueObject &valobj) {
lldb::ValueObjectSP value_sp = valobj.GetChildMemberWithName("__value_");
if (!value_sp)
return std::nullopt;
bool success;
int64_t value = value_sp->GetValueAsSigned(0, &success);
if (!success)
return std::nullopt;
return value;
}
bool lldb_private::formatters::LibcxxPartialOrderingSummaryProvider(
ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
std::optional<int64_t> value = LibcxxExtractOrderingValue(valobj);
if (!value)
return false;
switch (*value) {
case -1:
stream << "less";
break;
case 0:
stream << "equivalent";
break;
case 1:
stream << "greater";
break;
case -127:
stream << "unordered";
break;
default:
return false;
}
return true;
}
bool lldb_private::formatters::LibcxxWeakOrderingSummaryProvider(
ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
std::optional<int64_t> value = LibcxxExtractOrderingValue(valobj);
if (!value)
return false;
switch (*value) {
case -1:
stream << "less";
break;
case 0:
stream << "equivalent";
break;
case 1:
stream << "greater";
break;
default:
return false;
}
return true;
}
bool lldb_private::formatters::LibcxxStrongOrderingSummaryProvider(
ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
std::optional<int64_t> value = LibcxxExtractOrderingValue(valobj);
if (!value)
return false;
switch (*value) {
case -1:
stream << "less";
break;
case 0:
stream << "equal";
break;
case 1:
stream << "greater";
break;
default:
return false;
}
return true;
}
/*
(lldb) fr var ibeg --raw --ptr-depth 1 -T
(std::__1::__wrap_iter<int *>) ibeg = {
(std::__1::__wrap_iter<int *>::iterator_type) __i = 0x00000001001037a0 {
(int) *__i = 1
}
}
*/
SyntheticChildrenFrontEnd *
lldb_private::formatters::LibCxxVectorIteratorSyntheticFrontEndCreator(
CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
return (valobj_sp ? new VectorIteratorSyntheticFrontEnd(
valobj_sp, {ConstString("__i_"), ConstString("__i")})
: nullptr);
}
lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::
LibcxxSharedPtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
: SyntheticChildrenFrontEnd(*valobj_sp), m_cntrl(nullptr),
m_ptr_obj(nullptr) {
if (valobj_sp)
Update();
}
llvm::Expected<uint32_t> lldb_private::formatters::
LibcxxSharedPtrSyntheticFrontEnd::CalculateNumChildren() {
return (m_cntrl ? 1 : 0);
}
lldb::ValueObjectSP
lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::GetChildAtIndex(
uint32_t idx) {
if (!m_cntrl || !m_ptr_obj)
return lldb::ValueObjectSP();
ValueObjectSP valobj_sp = m_backend.GetSP();
if (!valobj_sp)
return lldb::ValueObjectSP();
if (idx == 0)
return m_ptr_obj->GetSP();
if (idx == 1) {
Status status;
ValueObjectSP value_sp = m_ptr_obj->Dereference(status);
if (status.Success())
return value_sp;
}
return lldb::ValueObjectSP();
}
lldb::ChildCacheState
lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::Update() {
m_cntrl = nullptr;
m_ptr_obj = nullptr;
ValueObjectSP valobj_sp = m_backend.GetSP();
if (!valobj_sp)
return lldb::ChildCacheState::eRefetch;
TargetSP target_sp(valobj_sp->GetTargetSP());
if (!target_sp)
return lldb::ChildCacheState::eRefetch;
auto ptr_obj_sp = valobj_sp->GetChildMemberWithName("__ptr_");
if (!ptr_obj_sp)
return lldb::ChildCacheState::eRefetch;
auto cast_ptr_sp = GetDesugaredSmartPointerValue(*ptr_obj_sp, *valobj_sp);
if (!cast_ptr_sp)
return lldb::ChildCacheState::eRefetch;
m_ptr_obj = cast_ptr_sp->Clone(ConstString("pointer")).get();
lldb::ValueObjectSP cntrl_sp(valobj_sp->GetChildMemberWithName("__cntrl_"));
m_cntrl = cntrl_sp.get(); // need to store the raw pointer to avoid a circular
// dependency
return lldb::ChildCacheState::eRefetch;
}
llvm::Expected<size_t>
lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::
GetIndexOfChildWithName(ConstString name) {
if (name == "pointer")
return 0;
if (name == "object" || name == "$$dereference$$")
return 1;
return llvm::createStringErrorV("type has no child named '{0}'", name);
}
lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::
~LibcxxSharedPtrSyntheticFrontEnd() = default;
SyntheticChildrenFrontEnd *
lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEndCreator(
CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
return (valobj_sp ? new LibcxxSharedPtrSyntheticFrontEnd(valobj_sp)
: nullptr);
}
lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::
LibcxxUniquePtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
: SyntheticChildrenFrontEnd(*valobj_sp) {
if (valobj_sp)
Update();
}
lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::
~LibcxxUniquePtrSyntheticFrontEnd() = default;
SyntheticChildrenFrontEnd *
lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEndCreator(
CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
return (valobj_sp ? new LibcxxUniquePtrSyntheticFrontEnd(valobj_sp)
: nullptr);
}
llvm::Expected<uint32_t> lldb_private::formatters::
LibcxxUniquePtrSyntheticFrontEnd::CalculateNumChildren() {
if (m_value_ptr_sp)
return m_deleter_sp ? 2 : 1;
return 0;
}
lldb::ValueObjectSP
lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::GetChildAtIndex(
uint32_t idx) {
if (!m_value_ptr_sp)
return lldb::ValueObjectSP();
if (idx == 0)
return m_value_ptr_sp;
if (idx == 1)
return m_deleter_sp;
if (idx == 2) {
Status status;
auto value_sp = m_value_ptr_sp->Dereference(status);
if (status.Success()) {
return value_sp;
}
}
return lldb::ValueObjectSP();
}
lldb::ChildCacheState
lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::Update() {
ValueObjectSP valobj_sp = m_backend.GetSP();
if (!valobj_sp)
return lldb::ChildCacheState::eRefetch;
auto [ptr_sp, is_compressed_pair] =
GetValueOrOldCompressedPair(*valobj_sp, "__ptr_", "__ptr_");
if (!ptr_sp)
return lldb::ChildCacheState::eRefetch;
// Retrieve the actual pointer and the deleter, and clone them to give them
// user-friendly names.
if (is_compressed_pair) {
if (ValueObjectSP value_pointer_sp =
GetFirstValueOfLibCXXCompressedPair(*ptr_sp))
m_value_ptr_sp = value_pointer_sp->Clone(ConstString("pointer"));
if (ValueObjectSP deleter_sp =
GetSecondValueOfLibCXXCompressedPair(*ptr_sp))
m_deleter_sp = deleter_sp->Clone(ConstString("deleter"));
} else {
m_value_ptr_sp = ptr_sp->Clone(ConstString("pointer"));
if (ValueObjectSP deleter_sp =
valobj_sp->GetChildMemberWithName("__deleter_"))
if (deleter_sp->GetNumChildrenIgnoringErrors() > 0)
m_deleter_sp = deleter_sp->Clone(ConstString("deleter"));
}
return lldb::ChildCacheState::eRefetch;
}
llvm::Expected<size_t>
lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::
GetIndexOfChildWithName(ConstString name) {
if (name == "pointer")
return 0;
if (name == "deleter")
return 1;
if (name == "obj" || name == "object" || name == "$$dereference$$")
return 2;
return llvm::createStringErrorV("type has no child named '{0}'", name);
}
/// The field layout in a libc++ string (cap, side, data or data, size, cap).
namespace {
enum class StringLayout { CSD, DSC };
}
static ValueObjectSP ExtractLibCxxStringData(ValueObject &valobj) {
auto [valobj_r_sp, is_compressed_pair] =
GetValueOrOldCompressedPair(valobj, "__rep_", "__r_");
if (!valobj_r_sp)
return nullptr;
if (is_compressed_pair)
return GetFirstValueOfLibCXXCompressedPair(*valobj_r_sp);
return valobj_r_sp;
}
/// Determine the size in bytes of \p valobj (a libc++ std::string object) and
/// extract its data payload. Return the size + payload pair.
// TODO: Support big-endian architectures.
static std::optional<std::pair<uint64_t, ValueObjectSP>>
ExtractLibcxxStringInfo(ValueObject &valobj) {
ValueObjectSP valobj_rep_sp = ExtractLibCxxStringData(valobj);
if (!valobj_rep_sp || !valobj_rep_sp->GetError().Success())
return {};
ValueObjectSP l = valobj_rep_sp->GetChildMemberWithName("__l");
if (!l)
return {};
auto index_or_err = l->GetIndexOfChildWithName("__data_");
if (!index_or_err) {
LLDB_LOG_ERROR(GetLog(LLDBLog::DataFormatters), index_or_err.takeError(),
"{0}");
return {};
}
StringLayout layout =
*index_or_err == 0 ? StringLayout::DSC : StringLayout::CSD;
bool short_mode = false; // this means the string is in short-mode and the
// data is stored inline
bool using_bitmasks = true; // Whether the class uses bitmasks for the mode
// flag (pre-D123580).
uint64_t size;
uint64_t size_mode_value = 0;
ValueObjectSP short_sp = valobj_rep_sp->GetChildMemberWithName("__s");
if (!short_sp)
return {};
ValueObjectSP is_long = short_sp->GetChildMemberWithName("__is_long_");
ValueObjectSP size_sp = short_sp->GetChildMemberWithName("__size_");
if (!size_sp)
return {};
if (is_long) {
using_bitmasks = false;
short_mode = !is_long->GetValueAsUnsigned(/*fail_value=*/0);
size = size_sp->GetValueAsUnsigned(/*fail_value=*/0);
} else {
// The string mode is encoded in the size field.
size_mode_value = size_sp->GetValueAsUnsigned(0);
uint8_t mode_mask = layout == StringLayout::DSC ? 0x80 : 1;
short_mode = (size_mode_value & mode_mask) == 0;
}
if (short_mode) {
ValueObjectSP location_sp = short_sp->GetChildMemberWithName("__data_");
if (using_bitmasks)
size = (layout == StringLayout::DSC) ? size_mode_value
: ((size_mode_value >> 1) % 256);
if (!location_sp)
return {};
// When the small-string optimization takes place, the data must fit in the
// inline string buffer (23 bytes on x86_64/Darwin). If it doesn't, it's
// likely that the string isn't initialized and we're reading garbage.
ExecutionContext exe_ctx(location_sp->GetExecutionContextRef());
const std::optional<uint64_t> max_bytes =
llvm::expectedToOptional(location_sp->GetCompilerType().GetByteSize(
exe_ctx.GetBestExecutionContextScope()));
if (!max_bytes || size > *max_bytes)
return {};
return std::make_pair(size, location_sp);
}
// we can use the layout_decider object as the data pointer
ValueObjectSP location_sp = l->GetChildMemberWithName("__data_");
ValueObjectSP size_vo = l->GetChildMemberWithName("__size_");
ValueObjectSP capacity_vo = l->GetChildMemberWithName("__cap_");
if (!size_vo || !location_sp || !capacity_vo)
return {};
size = size_vo->GetValueAsUnsigned(LLDB_INVALID_OFFSET);
uint64_t capacity = capacity_vo->GetValueAsUnsigned(LLDB_INVALID_OFFSET);
if (!using_bitmasks && layout == StringLayout::CSD)
capacity *= 2;
if (size == LLDB_INVALID_OFFSET || capacity == LLDB_INVALID_OFFSET ||
capacity < size)
return {};
return std::make_pair(size, location_sp);
}
bool lldb_private::formatters::LibcxxWStringSummaryProvider(
ValueObject &valobj, Stream &stream,
const TypeSummaryOptions &summary_options) {
auto string_info = ExtractLibcxxStringInfo(valobj);
if (!string_info)
return false;
uint64_t size;
ValueObjectSP location_sp;
std::tie(size, location_sp) = *string_info;
auto wchar_t_size = GetWCharByteSize(valobj);
if (!wchar_t_size)
return false;
switch (*wchar_t_size) {
case 1:
return StringBufferSummaryProvider<StringPrinter::StringElementType::UTF8>(
stream, summary_options, location_sp, size, "L");
case 2:
return StringBufferSummaryProvider<StringPrinter::StringElementType::UTF16>(
stream, summary_options, location_sp, size, "L");
case 4:
return StringBufferSummaryProvider<StringPrinter::StringElementType::UTF32>(
stream, summary_options, location_sp, size, "L");
}
return false;
}
template <StringPrinter::StringElementType element_type>
static bool
LibcxxStringSummaryProvider(ValueObject &valobj, Stream &stream,
const TypeSummaryOptions &summary_options,
std::string prefix_token) {
auto string_info = ExtractLibcxxStringInfo(valobj);
if (!string_info)
return false;
uint64_t size;
ValueObjectSP location_sp;
std::tie(size, location_sp) = *string_info;
return StringBufferSummaryProvider<element_type>(
stream, summary_options, location_sp, size, prefix_token);
}
template <StringPrinter::StringElementType element_type>
static bool formatStringImpl(ValueObject &valobj, Stream &stream,
const TypeSummaryOptions &summary_options,
std::string prefix_token) {
StreamString scratch_stream;
const bool success = LibcxxStringSummaryProvider<element_type>(
valobj, scratch_stream, summary_options, prefix_token);
if (success)
stream << scratch_stream.GetData();
else
stream << "Summary Unavailable";
return true;
}
bool lldb_private::formatters::LibcxxStringSummaryProviderASCII(
ValueObject &valobj, Stream &stream,
const TypeSummaryOptions &summary_options) {
return formatStringImpl<StringPrinter::StringElementType::ASCII>(
valobj, stream, summary_options, "");
}
bool lldb_private::formatters::LibcxxStringSummaryProviderUTF16(
ValueObject &valobj, Stream &stream,
const TypeSummaryOptions &summary_options) {
return formatStringImpl<StringPrinter::StringElementType::UTF16>(
valobj, stream, summary_options, "u");
}
bool lldb_private::formatters::LibcxxStringSummaryProviderUTF32(
ValueObject &valobj, Stream &stream,
const TypeSummaryOptions &summary_options) {
return formatStringImpl<StringPrinter::StringElementType::UTF32>(
valobj, stream, summary_options, "U");
}
static std::tuple<bool, ValueObjectSP, size_t>
LibcxxExtractStringViewData(ValueObject& valobj) {
auto dataobj = GetChildMemberWithName(
valobj, {ConstString("__data_"), ConstString("__data")});
auto sizeobj = GetChildMemberWithName(
valobj, {ConstString("__size_"), ConstString("__size")});
if (!dataobj || !sizeobj)
return std::make_tuple<bool,ValueObjectSP,size_t>(false, {}, {});
if (!dataobj->GetError().Success() || !sizeobj->GetError().Success())
return std::make_tuple<bool,ValueObjectSP,size_t>(false, {}, {});
bool success{false};
uint64_t size = sizeobj->GetValueAsUnsigned(0, &success);
if (!success)
return std::make_tuple<bool,ValueObjectSP,size_t>(false, {}, {});
return std::make_tuple(true,dataobj,size);
}
template <StringPrinter::StringElementType element_type>
static bool formatStringViewImpl(ValueObject &valobj, Stream &stream,
const TypeSummaryOptions &summary_options,
std::string prefix_token) {
bool success;
ValueObjectSP dataobj;
size_t size;
std::tie(success, dataobj, size) = LibcxxExtractStringViewData(valobj);
if (!success) {
stream << "Summary Unavailable";
return true;
}
return StringBufferSummaryProvider<element_type>(stream, summary_options,
dataobj, size, prefix_token);
}
bool lldb_private::formatters::LibcxxStringViewSummaryProviderASCII(
ValueObject &valobj, Stream &stream,
const TypeSummaryOptions &summary_options) {
return formatStringViewImpl<StringPrinter::StringElementType::ASCII>(
valobj, stream, summary_options, "");
}
bool lldb_private::formatters::LibcxxStringViewSummaryProviderUTF16(
ValueObject &valobj, Stream &stream,
const TypeSummaryOptions &summary_options) {
return formatStringViewImpl<StringPrinter::StringElementType::UTF16>(
valobj, stream, summary_options, "u");
}
bool lldb_private::formatters::LibcxxStringViewSummaryProviderUTF32(
ValueObject &valobj, Stream &stream,
const TypeSummaryOptions &summary_options) {
return formatStringViewImpl<StringPrinter::StringElementType::UTF32>(
valobj, stream, summary_options, "U");
}
bool lldb_private::formatters::LibcxxWStringViewSummaryProvider(
ValueObject &valobj, Stream &stream,
const TypeSummaryOptions &summary_options) {
bool success;
ValueObjectSP dataobj;
size_t size;
std::tie(success, dataobj, size) = LibcxxExtractStringViewData(valobj);
if (!success) {
stream << "Summary Unavailable";
return true;
}
auto wchar_t_size = GetWCharByteSize(valobj);
if (!wchar_t_size)
return false;
switch (*wchar_t_size) {
case 1:
return StringBufferSummaryProvider<StringPrinter::StringElementType::UTF8>(
stream, summary_options, dataobj, size, "L");
case 2:
return StringBufferSummaryProvider<StringPrinter::StringElementType::UTF16>(
stream, summary_options, dataobj, size, "L");
case 4:
return StringBufferSummaryProvider<StringPrinter::StringElementType::UTF32>(
stream, summary_options, dataobj, size, "L");
}
return false;
}
static bool
LibcxxChronoTimePointSecondsSummaryProvider(ValueObject &valobj, Stream &stream,
const TypeSummaryOptions &options,
const char *fmt) {
ValueObjectSP ptr_sp = valobj.GetChildMemberWithName("__d_");
if (!ptr_sp)
return false;
ptr_sp = ptr_sp->GetChildMemberWithName("__rep_");
if (!ptr_sp)
return false;
#ifndef _WIN32
// The date time in the chrono library is valid in the range
// [-32767-01-01T00:00:00Z, 32767-12-31T23:59:59Z]. A 64-bit time_t has a
// larger range, the function strftime is not able to format the entire range
// of time_t. The exact point has not been investigated; it's limited to
// chrono's range.
const std::time_t chrono_timestamp_min =
-1'096'193'779'200; // -32767-01-01T00:00:00Z
const std::time_t chrono_timestamp_max =
971'890'963'199; // 32767-12-31T23:59:59Z
#else
const std::time_t chrono_timestamp_min = -43'200; // 1969-12-31T12:00:00Z
const std::time_t chrono_timestamp_max =
32'536'850'399; // 3001-01-19T21:59:59
#endif
const std::time_t seconds = ptr_sp->GetValueAsSigned(0);
if (seconds < chrono_timestamp_min || seconds > chrono_timestamp_max)
stream.Printf("timestamp=%" PRId64 " s", static_cast<int64_t>(seconds));
else {
std::array<char, 128> str;
std::size_t size =
std::strftime(str.data(), str.size(), fmt, gmtime(&seconds));
if (size == 0)
return false;
stream.Printf("date/time=%s timestamp=%" PRId64 " s", str.data(),
static_cast<int64_t>(seconds));
}
return true;
}
bool lldb_private::formatters::LibcxxChronoSysSecondsSummaryProvider(
ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
return LibcxxChronoTimePointSecondsSummaryProvider(valobj, stream, options,
"%FT%H:%M:%SZ");
}
bool lldb_private::formatters::LibcxxChronoLocalSecondsSummaryProvider(
ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
return LibcxxChronoTimePointSecondsSummaryProvider(valobj, stream, options,
"%FT%H:%M:%S");
}
static bool
LibcxxChronoTimepointDaysSummaryProvider(ValueObject &valobj, Stream &stream,
const TypeSummaryOptions &options,
const char *fmt) {
ValueObjectSP ptr_sp = valobj.GetChildMemberWithName("__d_");
if (!ptr_sp)
return false;
ptr_sp = ptr_sp->GetChildMemberWithName("__rep_");
if (!ptr_sp)
return false;
#ifndef _WIN32
// The date time in the chrono library is valid in the range
// [-32767-01-01Z, 32767-12-31Z]. A 32-bit time_t has a larger range, the
// function strftime is not able to format the entire range of time_t. The
// exact point has not been investigated; it's limited to chrono's range.
const int chrono_timestamp_min = -12'687'428; // -32767-01-01Z
const int chrono_timestamp_max = 11'248'737; // 32767-12-31Z
#else
const int chrono_timestamp_min = 0; // 1970-01-01Z
const int chrono_timestamp_max = 376'583; // 3001-01-19Z
#endif
const int days = ptr_sp->GetValueAsSigned(0);
if (days < chrono_timestamp_min || days > chrono_timestamp_max)
stream.Printf("timestamp=%d days", days);
else {
const std::time_t seconds = std::time_t(86400) * days;
std::array<char, 128> str;
std::size_t size =
std::strftime(str.data(), str.size(), fmt, gmtime(&seconds));
if (size == 0)
return false;
stream.Printf("date=%s timestamp=%d days", str.data(), days);
}
return true;
}
bool lldb_private::formatters::LibcxxChronoSysDaysSummaryProvider(
ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
return LibcxxChronoTimepointDaysSummaryProvider(valobj, stream, options,
"%FZ");
}
bool lldb_private::formatters::LibcxxChronoLocalDaysSummaryProvider(
ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
return LibcxxChronoTimepointDaysSummaryProvider(valobj, stream, options,
"%F");
}
bool lldb_private::formatters::LibcxxChronoMonthSummaryProvider(
ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
// FIXME: These are the names used in the C++20 ostream operator. Since LLVM
// uses C++17 it's not possible to use the ostream operator directly.
static const std::array<std::string_view, 12> months = {
"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"};
ValueObjectSP ptr_sp = valobj.GetChildMemberWithName("__m_");
if (!ptr_sp)
return false;
const unsigned month = ptr_sp->GetValueAsUnsigned(0);
if (month >= 1 && month <= 12)
stream << "month=" << months[month - 1];
else
stream.Printf("month=%u", month);
return true;
}
bool lldb_private::formatters::LibcxxChronoWeekdaySummaryProvider(
ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
// FIXME: These are the names used in the C++20 ostream operator. Since LLVM
// uses C++17 it's not possible to use the ostream operator directly.
static const std::array<std::string_view, 7> weekdays = {
"Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"};
ValueObjectSP ptr_sp = valobj.GetChildMemberWithName("__wd_");
if (!ptr_sp)
return false;
const unsigned weekday = ptr_sp->GetValueAsUnsigned(0);
if (weekday < 7)
stream << "weekday=" << weekdays[weekday];
else
stream.Printf("weekday=%u", weekday);
return true;
}
bool lldb_private::formatters::LibcxxChronoYearMonthDaySummaryProvider(
ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
ValueObjectSP ptr_sp = valobj.GetChildMemberWithName("__y_");
if (!ptr_sp)
return false;
ptr_sp = ptr_sp->GetChildMemberWithName("__y_");
if (!ptr_sp)
return false;
int year = ptr_sp->GetValueAsSigned(0);
ptr_sp = valobj.GetChildMemberWithName("__m_");
if (!ptr_sp)
return false;
ptr_sp = ptr_sp->GetChildMemberWithName("__m_");
if (!ptr_sp)
return false;
const unsigned month = ptr_sp->GetValueAsUnsigned(0);
ptr_sp = valobj.GetChildMemberWithName("__d_");
if (!ptr_sp)
return false;
ptr_sp = ptr_sp->GetChildMemberWithName("__d_");
if (!ptr_sp)
return false;
const unsigned day = ptr_sp->GetValueAsUnsigned(0);
stream << "date=";
if (year < 0) {
stream << '-';
year = -year;
}
stream.Printf("%04d-%02u-%02u", year, month, day);
return true;
}