| //===-- DumpRegisterValue.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 "lldb/Core/DumpRegisterValue.h" | |
| #include "lldb/Core/DumpDataExtractor.h" | |
| #include "lldb/DataFormatters/DumpValueObjectOptions.h" | |
| #include "lldb/Utility/DataExtractor.h" | |
| #include "lldb/Utility/Endian.h" | |
| #include "lldb/Utility/RegisterType.h" | |
| #include "lldb/Utility/RegisterTypeFlags.h" | |
| #include "lldb/Utility/RegisterValue.h" | |
| #include "lldb/Utility/StreamString.h" | |
| #include "lldb/ValueObject/ValueObject.h" | |
| #include "lldb/ValueObject/ValueObjectConstResult.h" | |
| #include "lldb/lldb-private-types.h" | |
| #include "llvm/ADT/bit.h" | |
| using namespace lldb; | |
| template <typename T> | |
| static void dump_type_value(const lldb_private::RegisterTypeFlags &flags_type, | |
| lldb_private::CompilerType &fields_compiler_type, | |
| T value, | |
| lldb_private::ExecutionContextScope *exe_scope, | |
| lldb_private::Stream &strm) { | |
| lldb::ByteOrder target_order = exe_scope->CalculateProcess()->GetByteOrder(); | |
| // For the bitfield types we generate, it is expected that the fields are | |
| // in what is usually a big endian order. Most significant field first. | |
| // This is also clang's internal ordering and the order we want to print | |
| // them. On a big endian host this all matches up, for a little endian | |
| // host we have to swap the order of the fields before display. | |
| if (target_order == lldb::ByteOrder::eByteOrderLittle) { | |
| value = flags_type.ReverseFieldOrder(value); | |
| } | |
| // Then we need to match the target's endian on a byte level as well. | |
| if (lldb_private::endian::InlHostByteOrder() != target_order) | |
| value = llvm::byteswap(value); | |
| lldb_private::DataExtractor data_extractor{ | |
| &value, sizeof(T), lldb_private::endian::InlHostByteOrder(), 8}; | |
| lldb::ValueObjectSP vobj_sp = lldb_private::ValueObjectConstResult::Create( | |
| exe_scope, fields_compiler_type, lldb_private::ConstString(), | |
| data_extractor); | |
| lldb_private::DumpValueObjectOptions dump_options; | |
| lldb_private::DumpValueObjectOptions::ChildPrintingDecider decider = | |
| [](llvm::StringRef varname) { | |
| // Unnamed bit-fields are padding that we don't want to show. | |
| return varname.size(); | |
| }; | |
| dump_options.SetChildPrintingDecider(decider).SetHideRootType(true); | |
| if (llvm::Error error = vobj_sp->Dump(strm, dump_options)) | |
| strm << "error: " << toString(std::move(error)); | |
| } | |
| static void dump_type_value(lldb_private::CompilerType &type, | |
| const lldb_private::DataExtractor &data_extractor, | |
| lldb_private::ExecutionContextScope *exe_scope, | |
| lldb_private::Stream &strm) { | |
| lldb::ValueObjectSP vobj_sp = lldb_private::ValueObjectConstResult::Create( | |
| exe_scope, type, lldb_private::ConstString(), data_extractor); | |
| lldb_private::DumpValueObjectOptions dump_options; | |
| dump_options.SetHideRootType(true).SetShowSummary(false); | |
| if (llvm::Error error = vobj_sp->Dump(strm, dump_options)) | |
| strm << "error: " << toString(std::move(error)); | |
| } | |
| void lldb_private::DumpRegisterValue(const RegisterValue ®_val, Stream &s, | |
| const RegisterInfo ®_info, | |
| bool prefix_with_name, | |
| bool prefix_with_alt_name, Format format, | |
| uint32_t reg_name_right_align_at, | |
| ExecutionContextScope *exe_scope, | |
| bool print_type, TargetSP target_sp) { | |
| DataExtractor data; | |
| if (!reg_val.GetData(data)) | |
| return; | |
| bool name_printed = false; | |
| // For simplicity, alignment of the register name printing applies only in | |
| // the most common case where: | |
| // | |
| // prefix_with_name^prefix_with_alt_name is true | |
| // | |
| StreamString format_string; | |
| if (reg_name_right_align_at && (prefix_with_name ^ prefix_with_alt_name)) | |
| format_string.Printf("%%%us", reg_name_right_align_at); | |
| else | |
| format_string.Printf("%%s"); | |
| std::string fmt = std::string(format_string.GetString()); | |
| if (prefix_with_name) { | |
| if (reg_info.name) { | |
| s.Printf(fmt.c_str(), reg_info.name); | |
| name_printed = true; | |
| } else if (reg_info.alt_name) { | |
| s.Printf(fmt.c_str(), reg_info.alt_name); | |
| prefix_with_alt_name = false; | |
| name_printed = true; | |
| } | |
| } | |
| if (prefix_with_alt_name) { | |
| if (name_printed) | |
| s.PutChar('/'); | |
| if (reg_info.alt_name) { | |
| s.Printf(fmt.c_str(), reg_info.alt_name); | |
| name_printed = true; | |
| } else if (!name_printed) { | |
| // No alternate name but we were asked to display a name, so show the | |
| // main name | |
| s.Printf(fmt.c_str(), reg_info.name); | |
| name_printed = true; | |
| } | |
| } | |
| if (name_printed) | |
| s.PutCString(" = "); | |
| if (format == eFormatDefault) | |
| format = reg_info.format; | |
| DumpDataExtractor(data, &s, | |
| 0, // Offset in "data" | |
| format, // Format to use when dumping | |
| reg_info.byte_size, // item_byte_size | |
| 1, // item_count | |
| UINT32_MAX, // num_per_line | |
| LLDB_INVALID_ADDRESS, // base_addr | |
| 0, // item_bit_size | |
| 0, // item_bit_offset | |
| exe_scope); | |
| if (!print_type || !exe_scope || !target_sp) | |
| return; | |
| const RegisterTypeFlags *flags_type = | |
| llvm::dyn_cast_if_present<RegisterTypeFlags>(reg_info.register_type); | |
| if (!flags_type && | |
| !llvm::isa_and_present<RegisterTypeVector>(reg_info.register_type)) | |
| return; | |
| if (flags_type && reg_info.byte_size != 4 && reg_info.byte_size != 8) | |
| return; | |
| CompilerType register_compiler_type = target_sp->GetRegisterType(reg_info); | |
| if (!register_compiler_type.IsValid()) | |
| return; | |
| // Use a new stream so we can remove a trailing newline later. | |
| StreamString register_type_stream; | |
| if (flags_type) { | |
| if (reg_info.byte_size == 4) { | |
| dump_type_value(*flags_type, register_compiler_type, | |
| reg_val.GetAsUInt32(), exe_scope, register_type_stream); | |
| } else if (reg_info.byte_size == 8) { | |
| dump_type_value(*flags_type, register_compiler_type, | |
| reg_val.GetAsUInt64(), exe_scope, register_type_stream); | |
| } | |
| } else { | |
| lldb::ProcessSP process_sp = exe_scope->CalculateProcess(); | |
| if (!process_sp) | |
| return; | |
| // ValueObjectConstResult interprets its data using the target's layout. | |
| DataExtractor structured_data; | |
| if (!reg_val.GetData(structured_data, reg_info.byte_size, | |
| process_sp->GetByteOrder())) | |
| return; | |
| structured_data.SetAddressByteSize(process_sp->GetAddressByteSize()); | |
| dump_type_value(register_compiler_type, structured_data, exe_scope, | |
| register_type_stream); | |
| } | |
| // Registers are indented like: | |
| // (lldb) register read foo | |
| // foo = 0x12345678 | |
| // So we need to indent to match that. | |
| // First drop the extra newline that the value printer added. The register | |
| // command will add one itself. | |
| llvm::StringRef register_type_str = | |
| register_type_stream.GetString().drop_back(); | |
| // End the line that contains " foo = 0x12345678". | |
| s.EOL(); | |
| // Then split the value lines and indent each one. | |
| bool first = true; | |
| while (register_type_str.size()) { | |
| std::pair<llvm::StringRef, llvm::StringRef> split = | |
| register_type_str.split('\n'); | |
| register_type_str = split.second; | |
| // Indent as much as the stream does. | |
| s.Indent(); | |
| // Indent further to match where the register name finishes. | |
| s.Printf(fmt.c_str(), ""); | |
| // Lines after the first won't have " = " so compensate for that. | |
| if (!first) | |
| s << " "; | |
| first = false; | |
| s << split.first; | |
| // On the last line we don't want a newline because the command will add | |
| // one too. | |
| if (register_type_str.size()) | |
| s.EOL(); | |
| } | |
| } |