| 1 | //===-- TypeFormat.cpp ----------------------------------------------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #include "lldb/DataFormatters/TypeFormat.h" |
| 10 | |
| 11 | |
| 12 | |
| 13 | |
| 14 | #include "lldb/lldb-enumerations.h" |
| 15 | #include "lldb/lldb-public.h" |
| 16 | |
| 17 | #include "lldb/Core/DumpDataExtractor.h" |
| 18 | #include "lldb/DataFormatters/FormatManager.h" |
| 19 | #include "lldb/Symbol/CompilerType.h" |
| 20 | #include "lldb/Symbol/SymbolContext.h" |
| 21 | #include "lldb/Symbol/SymbolFile.h" |
| 22 | #include "lldb/Symbol/TypeList.h" |
| 23 | #include "lldb/Target/Target.h" |
| 24 | #include "lldb/Utility/DataExtractor.h" |
| 25 | #include "lldb/Utility/StreamString.h" |
| 26 | #include <optional> |
| 27 | |
| 28 | using namespace lldb; |
| 29 | using namespace lldb_private; |
| 30 | |
| 31 | TypeFormatImpl::TypeFormatImpl(const Flags &flags) : m_flags(flags) {} |
| 32 | |
| 33 | TypeFormatImpl::~TypeFormatImpl() = default; |
| 34 | |
| 35 | TypeFormatImpl_Format::TypeFormatImpl_Format(lldb::Format f, |
| 36 | const TypeFormatImpl::Flags &flags) |
| 37 | : TypeFormatImpl(flags), m_format(f) {} |
| 38 | |
| 39 | TypeFormatImpl_Format::~TypeFormatImpl_Format() = default; |
| 40 | |
| 41 | bool TypeFormatImpl_Format::FormatObject(ValueObject *valobj, |
| 42 | std::string &dest) const { |
| 43 | if (!valobj) |
| 44 | return false; |
| 45 | if (valobj->CanProvideValue()) { |
| 46 | Value &value(valobj->GetValue()); |
| 47 | const Value::ContextType context_type = value.GetContextType(); |
| 48 | ExecutionContext exe_ctx(valobj->GetExecutionContextRef()); |
| 49 | DataExtractor data; |
| 50 | |
| 51 | if (context_type == Value::ContextType::RegisterInfo) { |
| 52 | const RegisterInfo *reg_info = value.GetRegisterInfo(); |
| 53 | if (reg_info) { |
| 54 | Status error; |
| 55 | valobj->GetData(data, error); |
| 56 | if (error.Fail()) |
| 57 | return false; |
| 58 | |
| 59 | StreamString reg_sstr; |
| 60 | DumpDataExtractor(DE: data, s: ®_sstr, offset: 0, item_format: GetFormat(), item_byte_size: reg_info->byte_size, |
| 61 | item_count: 1, UINT32_MAX, LLDB_INVALID_ADDRESS, item_bit_size: 0, item_bit_offset: 0, |
| 62 | exe_scope: exe_ctx.GetBestExecutionContextScope()); |
| 63 | dest = std::string(reg_sstr.GetString()); |
| 64 | } |
| 65 | } else { |
| 66 | CompilerType compiler_type = value.GetCompilerType(); |
| 67 | if (compiler_type) { |
| 68 | // put custom bytes to display in the DataExtractor to override the |
| 69 | // default value logic |
| 70 | if (GetFormat() == eFormatCString) { |
| 71 | lldb_private::Flags type_flags(compiler_type.GetTypeInfo( |
| 72 | pointee_or_element_compiler_type: nullptr)); // disambiguate w.r.t. TypeFormatImpl::Flags |
| 73 | if (type_flags.Test(bit: eTypeIsPointer) && |
| 74 | !type_flags.Test(bit: eTypeIsObjC)) { |
| 75 | // if we are dumping a pointer as a c-string, get the pointee data |
| 76 | // as a string |
| 77 | TargetSP target_sp(valobj->GetTargetSP()); |
| 78 | if (target_sp) { |
| 79 | size_t max_len = target_sp->GetMaximumSizeOfStringSummary(); |
| 80 | Status error; |
| 81 | WritableDataBufferSP buffer_sp( |
| 82 | new DataBufferHeap(max_len + 1, 0)); |
| 83 | Address address(valobj->GetPointerValue().address); |
| 84 | target_sp->ReadCStringFromMemory( |
| 85 | addr: address, dst: (char *)buffer_sp->GetBytes(), dst_max_len: max_len, result_error&: error); |
| 86 | if (error.Success()) |
| 87 | data.SetData(data_sp: buffer_sp); |
| 88 | } |
| 89 | } |
| 90 | } else { |
| 91 | Status error; |
| 92 | valobj->GetData(data, error); |
| 93 | if (error.Fail()) |
| 94 | return false; |
| 95 | } |
| 96 | |
| 97 | ExecutionContextScope *exe_scope = |
| 98 | exe_ctx.GetBestExecutionContextScope(); |
| 99 | auto size_or_err = compiler_type.GetByteSize(exe_scope); |
| 100 | if (!size_or_err) { |
| 101 | LLDB_LOG_ERRORV( |
| 102 | GetLog(LLDBLog::Types), size_or_err.takeError(), |
| 103 | "Cannot get size of type while formatting object: {0}" ); |
| 104 | return false; |
| 105 | } |
| 106 | StreamString sstr; |
| 107 | compiler_type.DumpTypeValue( |
| 108 | s: &sstr, // The stream to use for display |
| 109 | format: GetFormat(), // Format to display this type with |
| 110 | data, // Data to extract from |
| 111 | data_offset: 0, // Byte offset into "m_data" |
| 112 | data_byte_size: *size_or_err, // Byte size of item in "m_data" |
| 113 | bitfield_bit_size: valobj->GetBitfieldBitSize(), // Bitfield bit size |
| 114 | bitfield_bit_offset: valobj->GetBitfieldBitOffset(), // Bitfield bit offset |
| 115 | exe_scope); |
| 116 | // Given that we do not want to set the ValueObject's m_error for a |
| 117 | // formatting error (or else we wouldn't be able to reformat until a |
| 118 | // next update), an empty string is treated as a "false" return from |
| 119 | // here, but that's about as severe as we get |
| 120 | // CompilerType::DumpTypeValue() should always return something, even |
| 121 | // if that something is an error message |
| 122 | dest = std::string(sstr.GetString()); |
| 123 | } |
| 124 | } |
| 125 | return !dest.empty(); |
| 126 | } else |
| 127 | return false; |
| 128 | } |
| 129 | |
| 130 | std::string TypeFormatImpl_Format::GetDescription() { |
| 131 | StreamString sstr; |
| 132 | sstr.Printf(format: "%s%s%s%s" , FormatManager::GetFormatAsCString(format: GetFormat()), |
| 133 | Cascades() ? "" : " (not cascading)" , |
| 134 | SkipsPointers() ? " (skip pointers)" : "" , |
| 135 | SkipsReferences() ? " (skip references)" : "" ); |
| 136 | return std::string(sstr.GetString()); |
| 137 | } |
| 138 | |
| 139 | TypeFormatImpl_EnumType::TypeFormatImpl_EnumType( |
| 140 | ConstString type_name, const TypeFormatImpl::Flags &flags) |
| 141 | : TypeFormatImpl(flags), m_enum_type(type_name), m_types() {} |
| 142 | |
| 143 | TypeFormatImpl_EnumType::~TypeFormatImpl_EnumType() = default; |
| 144 | |
| 145 | bool TypeFormatImpl_EnumType::FormatObject(ValueObject *valobj, |
| 146 | std::string &dest) const { |
| 147 | dest.clear(); |
| 148 | if (!valobj) |
| 149 | return false; |
| 150 | if (!valobj->CanProvideValue()) |
| 151 | return false; |
| 152 | ProcessSP process_sp; |
| 153 | TargetSP target_sp; |
| 154 | void *valobj_key = (process_sp = valobj->GetProcessSP()).get(); |
| 155 | if (!valobj_key) |
| 156 | valobj_key = (target_sp = valobj->GetTargetSP()).get(); |
| 157 | else |
| 158 | target_sp = process_sp->GetTarget().shared_from_this(); |
| 159 | if (!valobj_key) |
| 160 | return false; |
| 161 | auto iter = m_types.find(x: valobj_key), end = m_types.end(); |
| 162 | CompilerType valobj_enum_type; |
| 163 | if (iter == end) { |
| 164 | // probably a redundant check |
| 165 | if (!target_sp) |
| 166 | return false; |
| 167 | const ModuleList &images(target_sp->GetImages()); |
| 168 | TypeQuery query(m_enum_type.GetStringRef()); |
| 169 | TypeResults results; |
| 170 | images.FindTypes(search_first: nullptr, query, results); |
| 171 | if (results.GetTypeMap().Empty()) |
| 172 | return false; |
| 173 | for (lldb::TypeSP type_sp : results.GetTypeMap().Types()) { |
| 174 | if (!type_sp) |
| 175 | continue; |
| 176 | if ((type_sp->GetForwardCompilerType().GetTypeInfo() & |
| 177 | eTypeIsEnumeration) == eTypeIsEnumeration) { |
| 178 | valobj_enum_type = type_sp->GetFullCompilerType(); |
| 179 | m_types.emplace(args&: valobj_key, args&: valobj_enum_type); |
| 180 | break; |
| 181 | } |
| 182 | } |
| 183 | } else |
| 184 | valobj_enum_type = iter->second; |
| 185 | if (!valobj_enum_type.IsValid()) |
| 186 | return false; |
| 187 | DataExtractor data; |
| 188 | Status error; |
| 189 | valobj->GetData(data, error); |
| 190 | if (error.Fail()) |
| 191 | return false; |
| 192 | ExecutionContext exe_ctx(valobj->GetExecutionContextRef()); |
| 193 | StreamString sstr; |
| 194 | valobj_enum_type.DumpTypeValue(s: &sstr, format: lldb::eFormatEnum, data, data_offset: 0, |
| 195 | data_byte_size: data.GetByteSize(), bitfield_bit_size: 0, bitfield_bit_offset: 0, |
| 196 | exe_scope: exe_ctx.GetBestExecutionContextScope()); |
| 197 | if (!sstr.GetString().empty()) |
| 198 | dest = std::string(sstr.GetString()); |
| 199 | return !dest.empty(); |
| 200 | } |
| 201 | |
| 202 | std::string TypeFormatImpl_EnumType::GetDescription() { |
| 203 | StreamString sstr; |
| 204 | sstr.Printf(format: "as type %s%s%s%s" , m_enum_type.AsCString(value_if_empty: "<invalid type>" ), |
| 205 | Cascades() ? "" : " (not cascading)" , |
| 206 | SkipsPointers() ? " (skip pointers)" : "" , |
| 207 | SkipsReferences() ? " (skip references)" : "" ); |
| 208 | return std::string(sstr.GetString()); |
| 209 | } |
| 210 | |