#define LLDB_CORE_DUMPREGISTERVALUE_H
#include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-forward.h"
#include <cstdint>
namespace lldb_private {
// The default value of 0 for reg_name_right_align_at means no alignment at
// all.
+// Set print_flags to true to print register fields if they are available.
+// If you do so, target_sp must be non-null for it to work.
void DumpRegisterValue(const RegisterValue ®_val, Stream *s,
const RegisterInfo *reg_info, bool prefix_with_name,
bool prefix_with_alt_name, lldb::Format format,
uint32_t reg_name_right_align_at = 0,
- ExecutionContextScope *exe_scope = nullptr);
+ ExecutionContextScope *exe_scope = nullptr,
+ bool print_flags = false,
+ lldb::TargetSP target_sp = nullptr);
} // namespace lldb_private
static void AutoCompleteProcessName(llvm::StringRef partial_name,
CompletionRequest &request);
+ // Register Type Provider
+ static bool RegisterPlugin(llvm::StringRef name, llvm::StringRef description,
+ RegisterTypeBuilderCreateInstance create_callback);
+
+ static bool
+ UnregisterPlugin(RegisterTypeBuilderCreateInstance create_callback);
+
+ static lldb::RegisterTypeBuilderSP GetRegisterTypeBuilder(Target &target);
+
// ScriptInterpreter
static bool RegisterPlugin(llvm::StringRef name, llvm::StringRef description,
lldb::ScriptLanguage script_lang,
const DumpValueObjectOptions &, Stream &)>
DeclPrintingHelper;
+ typedef std::function<bool(ConstString)> ChildPrintingDecider;
+
static const DumpValueObjectOptions DefaultOptions() {
static DumpValueObjectOptions g_default_options;
DumpValueObjectOptions &SetDeclPrintingHelper(DeclPrintingHelper helper);
+ DumpValueObjectOptions &SetChildPrintingDecider(ChildPrintingDecider decider);
+
DumpValueObjectOptions &SetShowTypes(bool show = false);
DumpValueObjectOptions &SetShowLocation(bool show = false);
lldb::LanguageType m_varformat_language = lldb::eLanguageTypeUnknown;
PointerDepth m_max_ptr_depth;
DeclPrintingHelper m_decl_printing_helper;
+ ChildPrintingDecider m_child_printing_decider;
PointerAsArraySettings m_pointer_as_array;
bool m_use_synthetic : 1;
bool m_scope_already_checked : 1;
RegisterFlags(std::string id, unsigned size,
const std::vector<Field> &fields);
+ // Reverse the order of the fields, keeping their values the same.
+ // For example a field from bit 31 to 30 with value 0b10 will become bits
+ // 1 to 0, with the same 0b10 value.
+ // Use this when you are going to show the register using a bitfield struct
+ // type. If that struct expects MSB first and you are on little endian where
+ // LSB would be first, this corrects that (and vice versa for big endian).
+ template <typename T> T ReverseFieldOrder(T value) const {
+ T ret = 0;
+ unsigned shift = 0;
+ for (auto field : GetFields()) {
+ ret |= field.GetValue(value) << shift;
+ shift += field.GetSizeInBits();
+ }
+
+ return ret;
+ }
+
const std::vector<Field> &GetFields() const { return m_fields; }
const std::string &GetID() const { return m_id; }
unsigned GetSize() const { return m_size; }
--- /dev/null
+//===-- RegisterTypeBuilder.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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_TARGET_REGISTER_TYPE_BUILDER_H
+#define LLDB_TARGET_REGISTER_TYPE_BUILDER_H
+
+#include "lldb/Core/PluginInterface.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+class RegisterTypeBuilder : public PluginInterface {
+public:
+ ~RegisterTypeBuilder() override = default;
+
+ virtual CompilerType GetRegisterType(const std::string &name,
+ const lldb_private::RegisterFlags &flags,
+ uint32_t byte_size) = 0;
+
+protected:
+ RegisterTypeBuilder() = default;
+
+private:
+ RegisterTypeBuilder(const RegisterTypeBuilder &) = delete;
+ const RegisterTypeBuilder &operator=(const RegisterTypeBuilder &) = delete;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_TARGET_REGISTER_TYPE_BUILDER_H
/// if none can be found.
llvm::Expected<lldb_private::Address> GetEntryPointAddress();
+ CompilerType GetRegisterType(const std::string &name,
+ const lldb_private::RegisterFlags &flags,
+ uint32_t byte_size);
+
// Target Stop Hooks
class StopHook : public UserID {
public:
class RecognizedStackFrame;
class RegisterCheckpoint;
class RegisterContext;
+class RegisterTypeBuilder;
class RegisterValue;
class RegularExpression;
class RichManglingContext;
typedef std::weak_ptr<lldb_private::Process> ProcessWP;
typedef std::shared_ptr<lldb_private::RegisterCheckpoint> RegisterCheckpointSP;
typedef std::shared_ptr<lldb_private::RegisterContext> RegisterContextSP;
+typedef std::shared_ptr<lldb_private::RegisterTypeBuilder>
+ RegisterTypeBuilderSP;
typedef std::shared_ptr<lldb_private::RegularExpression> RegularExpressionSP;
typedef std::shared_ptr<lldb_private::Queue> QueueSP;
typedef std::weak_ptr<lldb_private::Queue> QueueWP;
typedef lldb::ProcessSP (*ProcessCreateInstance)(
lldb::TargetSP target_sp, lldb::ListenerSP listener_sp,
const FileSpec *crash_file_path, bool can_connect);
+typedef lldb::RegisterTypeBuilderSP (*RegisterTypeBuilderCreateInstance)(
+ Target &target);
typedef lldb::ScriptInterpreterSP (*ScriptInterpreterCreateInstance)(
Debugger &debugger);
typedef SymbolFile *(*SymbolFileCreateInstance)(lldb::ObjectFileSP objfile_sp);
Options *GetOptions() override { return &m_option_group; }
bool DumpRegister(const ExecutionContext &exe_ctx, Stream &strm,
- RegisterContext *reg_ctx, const RegisterInfo *reg_info) {
+ RegisterContext *reg_ctx, const RegisterInfo *reg_info,
+ bool print_flags) {
if (reg_info) {
RegisterValue reg_value;
bool prefix_with_name = !prefix_with_altname;
DumpRegisterValue(reg_value, &strm, reg_info, prefix_with_name,
prefix_with_altname, m_format_options.GetFormat(), 8,
- exe_ctx.GetBestExecutionContextScope());
+ exe_ctx.GetBestExecutionContextScope(), print_flags,
+ exe_ctx.GetTargetSP());
if ((reg_info->encoding == eEncodingUint) ||
(reg_info->encoding == eEncodingSint)) {
Process *process = exe_ctx.GetProcessPtr();
if (primitive_only && reg_info && reg_info->value_regs)
continue;
- if (DumpRegister(exe_ctx, strm, reg_ctx, reg_info))
+ if (DumpRegister(exe_ctx, strm, reg_ctx, reg_info,
+ /*print_flags=*/false))
++available_count;
else
++unavailable_count;
reg_info = reg_ctx->GetRegisterInfoByName(arg_str);
if (reg_info) {
- if (!DumpRegister(m_exe_ctx, strm, reg_ctx, reg_info))
+ if (!DumpRegister(m_exe_ctx, strm, reg_ctx, reg_info,
+ /*print_flags=*/true))
strm.Printf("%-12s = error: unavailable\n", reg_info->name);
} else {
result.AppendErrorWithFormat("Invalid register name '%s'.\n",
#include "lldb/Core/DumpRegisterValue.h"
#include "lldb/Core/DumpDataExtractor.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/DataFormatters/DumpValueObjectOptions.h"
#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Endian.h"
#include "lldb/Utility/RegisterValue.h"
#include "lldb/Utility/StreamString.h"
#include "lldb/lldb-private-types.h"
using namespace lldb;
+static uint32_t swap_value(uint32_t v) { return __builtin_bswap32(v); }
+static uint64_t swap_value(uint64_t v) { return __builtin_bswap64(v); }
+
+template <typename T>
+static void dump_type_value(lldb_private::CompilerType &fields_type, T value,
+ lldb_private::ExecutionContextScope *exe_scope,
+ const lldb_private::RegisterInfo ®_info,
+ 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 = reg_info.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 = swap_value(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_type, lldb_private::ConstString(), data_extractor);
+ lldb_private::DumpValueObjectOptions dump_options;
+ lldb_private::DumpValueObjectOptions::ChildPrintingDecider decider =
+ [](lldb_private::ConstString varname) {
+ // Unnamed bit-fields are padding that we don't want to show.
+ return varname.GetLength();
+ };
+ dump_options.SetChildPrintingDecider(decider).SetHideRootType(true);
+
+ vobj_sp->Dump(strm, dump_options);
+}
+
void lldb_private::DumpRegisterValue(const RegisterValue ®_val, Stream *s,
const RegisterInfo *reg_info,
bool prefix_with_name,
bool prefix_with_alt_name, Format format,
uint32_t reg_name_right_align_at,
- ExecutionContextScope *exe_scope) {
+ ExecutionContextScope *exe_scope,
+ bool print_flags, TargetSP target_sp) {
DataExtractor data;
if (!reg_val.GetData(data))
return;
0, // item_bit_size
0, // item_bit_offset
exe_scope);
+
+ if (!print_flags || !reg_info->flags_type || !exe_scope || !target_sp ||
+ (reg_info->byte_size != 4 && reg_info->byte_size != 8))
+ return;
+
+ CompilerType fields_type = target_sp->GetRegisterType(
+ reg_info->name, *reg_info->flags_type, reg_info->byte_size);
+
+ // Use a new stream so we can remove a trailing newline later.
+ StreamString fields_stream;
+
+ if (reg_info->byte_size == 4) {
+ dump_type_value(fields_type, reg_val.GetAsUInt32(), exe_scope, *reg_info,
+ fields_stream);
+ } else {
+ dump_type_value(fields_type, reg_val.GetAsUInt64(), exe_scope, *reg_info,
+ fields_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 fields_str = fields_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 (fields_str.size()) {
+ std::pair<llvm::StringRef, llvm::StringRef> split = fields_str.split('\n');
+ fields_str = split.second;
+ // Indent as far as the register name did.
+ 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 (fields_str.size())
+ s->EOL();
+ }
}
}
}
+#pragma mark RegisterTypeBuilder
+
+struct RegisterTypeBuilderInstance
+ : public PluginInstance<RegisterTypeBuilderCreateInstance> {
+ RegisterTypeBuilderInstance(llvm::StringRef name, llvm::StringRef description,
+ CallbackType create_callback)
+ : PluginInstance<RegisterTypeBuilderCreateInstance>(name, description,
+ create_callback) {}
+};
+
+typedef PluginInstances<RegisterTypeBuilderInstance>
+ RegisterTypeBuilderInstances;
+
+static RegisterTypeBuilderInstances &GetRegisterTypeBuilderInstances() {
+ static RegisterTypeBuilderInstances g_instances;
+ return g_instances;
+}
+
+bool PluginManager::RegisterPlugin(
+ llvm::StringRef name, llvm::StringRef description,
+ RegisterTypeBuilderCreateInstance create_callback) {
+ return GetRegisterTypeBuilderInstances().RegisterPlugin(name, description,
+ create_callback);
+}
+
+bool PluginManager::UnregisterPlugin(
+ RegisterTypeBuilderCreateInstance create_callback) {
+ return GetRegisterTypeBuilderInstances().UnregisterPlugin(create_callback);
+}
+
+lldb::RegisterTypeBuilderSP
+PluginManager::GetRegisterTypeBuilder(Target &target) {
+ const auto &instances = GetRegisterTypeBuilderInstances().GetInstances();
+ // We assume that RegisterTypeBuilderClang is the only instance of this plugin
+ // type and is always present.
+ assert(instances.size());
+ return instances[0].create_callback(target);
+}
+
#pragma mark ScriptInterpreter
struct ScriptInterpreterInstance
DumpValueObjectOptions::DumpValueObjectOptions()
: m_summary_sp(), m_root_valobj_name(),
m_max_ptr_depth(PointerDepth{PointerDepth::Mode::Default, 0}),
- m_decl_printing_helper(), m_pointer_as_array(), m_use_synthetic(true),
+ m_decl_printing_helper(), m_child_printing_decider(),
+ m_pointer_as_array(), m_use_synthetic(true),
m_scope_already_checked(false), m_flat_output(false), m_ignore_cap(false),
m_show_types(false), m_show_location(false), m_use_objc(false),
m_hide_root_type(false), m_hide_root_name(false), m_hide_name(false),
return *this;
}
+DumpValueObjectOptions &
+DumpValueObjectOptions::SetChildPrintingDecider(ChildPrintingDecider decider) {
+ m_child_printing_decider = decider;
+ return *this;
+}
+
DumpValueObjectOptions &DumpValueObjectOptions::SetShowTypes(bool show) {
m_show_types = show;
return *this;
for (size_t idx = 0; idx < num_children; ++idx) {
if (ValueObjectSP child_sp = GenerateChild(synth_m_valobj, idx)) {
+ if (m_options.m_child_printing_decider &&
+ !m_options.m_child_printing_decider(child_sp->GetName()))
+ continue;
if (!any_children_printed) {
PrintChildrenPreamble(value_printed, summary_printed);
any_children_printed = true;
if (num_children) {
m_stream->PutChar('(');
+ bool did_print_children = false;
for (uint32_t idx = 0; idx < num_children; ++idx) {
lldb::ValueObjectSP child_sp(synth_m_valobj->GetChildAtIndex(idx, true));
if (child_sp)
child_sp = child_sp->GetQualifiedRepresentationIfAvailable(
m_options.m_use_dynamic, m_options.m_use_synthetic);
if (child_sp) {
- if (idx)
+ if (m_options.m_child_printing_decider &&
+ !m_options.m_child_printing_decider(child_sp->GetName()))
+ continue;
+ if (idx && did_print_children)
m_stream->PutCString(", ");
+ did_print_children = true;
if (!hide_names) {
const char *name = child_sp.get()->GetName().AsCString();
if (name && *name) {
add_subdirectory(Platform)
add_subdirectory(Process)
add_subdirectory(REPL)
+add_subdirectory(RegisterTypeBuilder)
add_subdirectory(ScriptInterpreter)
add_subdirectory(StructuredData)
add_subdirectory(SymbolFile)
--- /dev/null
+add_lldb_library(lldbPluginRegisterTypeBuilderClang PLUGIN
+ RegisterTypeBuilderClang.cpp
+
+ LINK_LIBS
+ lldbCore
+ lldbTarget
+
+ LINK_COMPONENTS
+ Support
+ )
--- /dev/null
+//===-- RegisterTypeBuilderClang.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 "clang/AST/DeclCXX.h"
+
+#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
+#include "RegisterTypeBuilderClang.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/lldb-enumerations.h"
+
+using namespace lldb_private;
+
+LLDB_PLUGIN_DEFINE(RegisterTypeBuilderClang)
+
+void RegisterTypeBuilderClang::Initialize() {
+ static llvm::once_flag g_once_flag;
+ llvm::call_once(g_once_flag, []() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance);
+ });
+}
+
+void RegisterTypeBuilderClang::Terminate() {}
+
+lldb::RegisterTypeBuilderSP
+RegisterTypeBuilderClang::CreateInstance(Target &target) {
+ return std::make_shared<RegisterTypeBuilderClang>(target);
+}
+
+RegisterTypeBuilderClang::RegisterTypeBuilderClang(Target &target)
+ : m_target(target) {}
+
+CompilerType RegisterTypeBuilderClang::GetRegisterType(
+ const std::string &name, const lldb_private::RegisterFlags &flags,
+ uint32_t byte_size) {
+ lldb::TypeSystemClangSP type_system =
+ ScratchTypeSystemClang::GetForTarget(m_target);
+ assert(type_system);
+
+ std::string register_type_name = "__lldb_register_fields_";
+ register_type_name += name;
+ // See if we have made this type before and can reuse it.
+ CompilerType fields_type =
+ type_system->GetTypeForIdentifier<clang::CXXRecordDecl>(
+ ConstString(register_type_name.c_str()));
+
+ if (!fields_type) {
+ // In most ABI, a change of field type means a change in storage unit.
+ // We want it all in one unit, so we use a field type the same as the
+ // register's size.
+ CompilerType field_uint_type =
+ type_system->GetBuiltinTypeForEncodingAndBitSize(lldb::eEncodingUint,
+ byte_size * 8);
+
+ fields_type = type_system->CreateRecordType(
+ nullptr, OptionalClangModuleID(), lldb::eAccessPublic,
+ register_type_name, clang::TTK_Struct, lldb::eLanguageTypeC);
+ type_system->StartTagDeclarationDefinition(fields_type);
+
+ // We assume that RegisterFlags has padded and sorted the fields
+ // already.
+ for (const RegisterFlags::Field &field : flags.GetFields()) {
+ type_system->AddFieldToRecordType(fields_type, field.GetName(),
+ field_uint_type, lldb::eAccessPublic,
+ field.GetSizeInBits());
+ }
+
+ type_system->CompleteTagDeclarationDefinition(fields_type);
+ // So that the size of the type matches the size of the register.
+ type_system->SetIsPacked(fields_type);
+
+ // This should be true if RegisterFlags padded correctly.
+ assert(*fields_type.GetByteSize(nullptr) == flags.GetSize());
+ }
+
+ return fields_type;
+}
--- /dev/null
+//===-- RegisterTypeBuilderClang.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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_PLUGINS_REGISTERTYPEBUILDER_REGISTERTYPEBUILDERCLANG_H
+#define LLDB_PLUGINS_REGISTERTYPEBUILDER_REGISTERTYPEBUILDERCLANG_H
+
+#include "lldb/Target/RegisterTypeBuilder.h"
+#include "lldb/Target/Target.h"
+
+namespace lldb_private {
+class RegisterTypeBuilderClang : public RegisterTypeBuilder {
+public:
+ RegisterTypeBuilderClang(Target &target);
+
+ static void Initialize();
+ static void Terminate();
+ static llvm::StringRef GetPluginNameStatic() {
+ return "register-types-clang";
+ }
+ llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); }
+ static llvm::StringRef GetPluginDescriptionStatic() {
+ return "Create register types using TypeSystemClang";
+ }
+ static lldb::RegisterTypeBuilderSP CreateInstance(Target &target);
+
+ CompilerType GetRegisterType(const std::string &name,
+ const lldb_private::RegisterFlags &flags,
+ uint32_t byte_size) override;
+
+private:
+ Target &m_target;
+};
+} // namespace lldb_private
+
+#endif // LLDB_PLUGINS_REGISTERTYPEBUILDER_REGISTERTYPEBUILDERCLANG_H
#include "lldb/Target/Language.h"
#include "lldb/Target/LanguageRuntime.h"
#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterTypeBuilder.h"
#include "lldb/Target/SectionLoadList.h"
#include "lldb/Target/StackFrame.h"
#include "lldb/Target/StackFrameRecognizer.h"
create_on_demand);
}
+CompilerType Target::GetRegisterType(const std::string &name,
+ const lldb_private::RegisterFlags &flags,
+ uint32_t byte_size) {
+ RegisterTypeBuilderSP provider = PluginManager::GetRegisterTypeBuilder(*this);
+ assert(provider);
+ return provider->GetRegisterType(name, flags, byte_size);
+}
+
std::vector<lldb::TypeSystemSP>
Target::GetScratchTypeSystems(bool create_on_demand) {
if (!m_valid)
--- /dev/null
+""" Check that register fields found in target XML are properly processed.
+
+These tests make XML out of string substitution. This can lead to some strange
+failures. Check that the final XML is valid and each child is indented more than
+the parent tag.
+"""
+
+from textwrap import dedent
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+from lldbsuite.test.gdbclientutils import *
+from lldbsuite.test.lldbgdbclient import GDBRemoteTestBase
+
+class MultiDocResponder(MockGDBServerResponder):
+ # docs is a dictionary of filename -> file content.
+ def __init__(self, docs):
+ super().__init__()
+ self.docs = docs
+
+ def qXferRead(self, obj, annex, offset, length):
+ try:
+ return self.docs[annex], False
+ except KeyError:
+ return None,
+
+ def readRegister(self, regnum):
+ return "E01"
+
+ def readRegisters(self):
+ return ''.join([
+ # Data for all registers requested by the tests below.
+ # 0x7 and 0xE are used because their lsb and msb are opposites, which
+ # is needed for a byte order test.
+ '77777777EEEEEEEE', # 64 bit x0/r0
+ '7777EEEE', # 32 bit cpsr/fpc
+ '0000000000000000', # 64 bit pc/pswa
+ ])
+
+class TestXMLRegisterFlags(GDBRemoteTestBase):
+ def setup_multidoc_test(self, docs):
+ self.server.responder = MultiDocResponder(docs)
+ target = self.dbg.CreateTarget('')
+
+ if self.TraceOn():
+ self.runCmd("log enable gdb-remote packets process")
+ self.addTearDownHook(
+ lambda: self.runCmd("log disable gdb-remote packets process"))
+
+ process = self.connect(target)
+ lldbutil.expect_state_changes(self, self.dbg.GetListener(), process,
+ [lldb.eStateStopped])
+
+ def setup_register_test(self, registers):
+ self.setup_multidoc_test(
+ # This *must* begin with the opening tag, leading whitespace is not allowed.
+ {'target.xml' : dedent("""\
+ <?xml version="1.0"?>
+ <target version="1.0">
+ <architecture>aarch64</architecture>
+ <feature name="org.gnu.gdb.aarch64.core">
+ {}
+ </feature>
+ </target>""").format(registers)})
+
+ def setup_flags_test(self, flags):
+ # pc is required here though we don't look at it in the tests.
+ # x0 is only used by some tests but always including it keeps the data ordering
+ # the same throughout.
+ self.setup_register_test("""\
+ <flags id="cpsr_flags" size="4">
+ {}
+ </flags>
+ <reg name="pc" bitsize="64"/>
+ <reg name="x0" regnum="0" bitsize="64" type="x0_flags"/>
+ <reg name="cpsr" regnum="33" bitsize="32" type="cpsr_flags"/>""".format(
+ flags))
+
+ @skipIfXmlSupportMissing
+ @skipIfRemote
+ def test_no_flags(self):
+ self.setup_flags_test("")
+ self.expect("register read cpsr", substrs=["= 0xeeee7777"])
+
+ @skipIfXmlSupportMissing
+ @skipIfRemote
+ def test_single_field_pad_msb(self):
+ self.setup_flags_test("""<field name="SP" start="0" end="0"/>""")
+ # Pads from 31 to 1.
+ self.expect("register read cpsr", substrs=["(SP = 1)"])
+
+ @skipIfXmlSupportMissing
+ @skipIfRemote
+ def test_single_field_pad_lsb(self):
+ self.setup_flags_test("""<field name="SP" start="31" end="31"/>""")
+ self.expect("register read cpsr", substrs=["(SP = 1)"])
+
+ @skipIfXmlSupportMissing
+ @skipIfRemote
+ def test_multiple_fields_sorted(self):
+ self.setup_flags_test("""<field name="SP" start="0" end="0"/>
+ <field name="EL" start="1" end="2"/>""")
+
+ # Fields should be sorted with MSB on the left.
+ self.expect("register read cpsr", substrs=["(EL = 3, SP = 1)"])
+
+ @skipIfXmlSupportMissing
+ @skipIfRemote
+ def test_ignore_invalid_start_end(self):
+ self.setup_flags_test(
+ # Is valid so is used.
+ '<field name="EL" start="2" end="3"/>'
+ # Start/end cannot be negative, ignored.
+ '<field name="SP" start="-1" end="2"/>'
+ '<field name="SP2" start="1" end="-5"/>'
+ # Start is not <= end, ignored.
+ '<field name="ABC" start="12" end="10"/>'
+ # Start cannot be >= (size of register in bits)
+ '<field name="?" start="32" end="29"/>'
+ # End cannot be >= (size of register in bits)
+ '<field name="DEF" start="30" end="35"/>')
+
+ self.expect("register read cpsr", substrs=["(EL = 1)"])
+
+ @skipIfXmlSupportMissing
+ @skipIfRemote
+ def test_field_overlap(self):
+ self.setup_flags_test(
+ '<field name="?" start="10" end="12"/>'
+ # A overlaps B
+ '<field name="A" start="0" end="3"/>'
+ '<field name="B" start="0" end="0"/>')
+
+ # Ignore the whole flags set, it is unlikely to be valid.
+ self.expect("register read cpsr", substrs=["("], matching=False)
+
+ @skipIfXmlSupportMissing
+ @skipIfRemote
+ def test_field_required_attributes(self):
+ # Fields must have a name, start and end. Any without are ignored.
+ self.setup_flags_test(
+ # Missing name
+ '<field start="0" end="0"/>'
+ # Missing start
+ '<field name="A" end="0"/>'
+ # Missing end
+ '<field name="B" start="0"/>'
+ # Valid
+ '<field name="C" start="0" end="0"/>')
+
+ self.expect("register read cpsr", substrs=["(C = 1)"])
+
+ @skipIfXmlSupportMissing
+ @skipIfRemote
+ def test_little_endian_target_order(self):
+ # We are using little endian AArch64 here.
+ self.setup_register_test("""\
+ <flags id="cpsr_flags" size="4">
+ <field name="lsb" start="0" end="0"/>
+ <field name="msb" start="31" end="31"/>
+ </flags>
+ <flags id="x0_flags" size="8">
+ <field name="lsb" start="0" end="0"/>
+ <field name="msb" start="63" end="63"/>
+ </flags>
+ <reg name="pc" bitsize="64"/>
+ <reg name="x0" regnum="0" bitsize="64" type="x0_flags"/>
+ <reg name="cpsr" regnum="33" bitsize="32" type="cpsr_flags"/>""")
+
+ # If lldb used the wrong byte ordering for the value for printing fields,
+ # these field values would flip. Since the top and bottom bits of 0x7 and 0xE
+ # are different.
+ self.expect("register read cpsr x0", substrs=[
+ " cpsr = 0xeeee7777\n"
+ " = (msb = 1, lsb = 1)\n"
+ " x0 = 0xeeeeeeee77777777\n"
+ " = (msb = 1, lsb = 1)"])
+
+ @skipIfXmlSupportMissing
+ @skipIfRemote
+ # Unlike AArch64, we do need the backend present for this test to work.
+ @skipIfLLVMTargetMissing("SystemZ")
+ def test_big_endian_target_order(self):
+ # s390x/SystemZ is big endian.
+ self.setup_multidoc_test({
+ 'target.xml' : dedent("""\
+ <?xml version="1.0"?>
+ <target version="1.0">
+ <architecture>s390x</architecture>
+ <feature name="org.gnu.gdb.s390x.core">
+ <flags id="r0_flags" size="8">
+ <field name="lsb" start="0" end="0"/>
+ <field name="msb" start="63" end="63"/>
+ </flags>
+ <flags id="fpc_flags" size="4">
+ <field name="lsb" start="0" end="0"/>
+ <field name="msb" start="31" end="31"/>
+ </flags>
+ <reg name="r0" bitsize="64" type="r0_flags"/>
+ <reg name="fpc" bitsize="32" type="fpc_flags"/>
+ <reg name="pswa" bitsize="64"/>
+ </feature>
+ </target>""")})
+
+ # If we did not swap correctly, these fields would show as 1s when run on
+ # a little endian host.
+ self.expect("register read r0 fpc", substrs=[
+ " r0 = 0x77777777eeeeeeee\n"
+ " = (msb = 0, lsb = 0)\n"
+ " fpc = 0x7777eeee\n"
+ " = (msb = 0, lsb = 0)\n"
+ ])
+
+ @skipIfXmlSupportMissing
+ @skipIfRemote
+ def test_many_flag_sets(self):
+ self.setup_register_test("""\
+ <flags id="cpsr_flags" size="4">
+ <field name="correct" start="0" end="0"/>
+ </flags>
+ <flags id="cpsr_flags_alt" size="4">
+ <field name="incorrect" start="0" end="0"/>
+ </flags>
+ <flags id="x0_flags" size="8">
+ <field name="foo" start="0" end="0"/>
+ </flags>
+ <reg name="pc" bitsize="64"/>
+ <reg name="x0" regnum="0" bitsize="64" type="x0_flags"/>
+ <reg name="cpsr" regnum="33" bitsize="32" type="cpsr_flags"/>""")
+
+ self.expect("register read cpsr x0", substrs=[
+ " cpsr = 0xeeee7777\n"
+ " = (correct = 1)\n"
+ " x0 = 0xeeeeeeee77777777\n"
+ " = (foo = 1)"])
+
+ @skipIfXmlSupportMissing
+ @skipIfRemote
+ def test_repeated_flag_set(self):
+ # The second definition of "cpsr_flags" should be ignored.
+ # This is because we assign the types to registers as we go. If we allowed
+ # the later flag set, it would destroy the first definition, making the
+ # pointer to the flags invalid.
+ self.setup_register_test("""\
+ <flags id="cpsr_flags" size="4">
+ <field name="correct" start="0" end="0"/>
+ </flags>
+ <flags id="cpsr_flags" size="4">
+ <field name="incorrect" start="0" end="0"/>
+ </flags>
+ <reg name="pc" bitsize="64"/>
+ <reg name="cpsr" regnum="33" bitsize="32" type="cpsr_flags"/>""")
+
+ self.expect("register read cpsr", substrs=["(correct = 1)"])
+
+ @skipIfXmlSupportMissing
+ @skipIfRemote
+ def test_missing_flags(self):
+ self.setup_register_test("""\
+ <reg name="pc" bitsize="64"/>
+ <reg name="cpsr" regnum="33" bitsize="32" type="cpsr_flags"/>""")
+
+ # Register prints with default formatting only if we can't find the
+ # flags type.
+ self.expect("register read cpsr", substrs=["cpsr = 0xeeee7777"])
+ self.expect("register read cpsr", substrs=["("], matching=False)
+
+ @skipIfXmlSupportMissing
+ @skipIfRemote
+ def test_flags_invalid_size(self):
+ # We're not using the size for anything yet so just check that we handle
+ # it not being a positive integer.
+ self.setup_register_test("""\
+ <flags id="cpsr_flags" size="???">
+ <field name="A" start="0" end="0"/>
+ </flags>
+ <flags id="cpsr_flags" size="-1">
+ <field name="B" start="0" end="0"/>
+ </flags>
+ <flags id="cpsr_flags" size="4">
+ <field name="C" start="0" end="0"/>
+ </flags>
+ <reg name="pc" bitsize="64"/>
+ <reg name="cpsr" regnum="33" bitsize="32" type="cpsr_flags"/>""")
+
+ # Only the final set has a valid size, use that.
+ self.expect("register read cpsr", substrs=["(C = 1)"])
+
+ @skipIfXmlSupportMissing
+ @skipIfRemote
+ def test_flags_unknown_attribute(self):
+ # Unknown attributes on flags or field are ignored.
+ self.setup_register_test("""\
+ <flags id="cpsr_flags" size="4" stuff="abcd">
+ <field name="A" start="0" abcd="???" end="0"/>
+ </flags>
+ <reg name="pc" bitsize="64"/>
+ <reg name="cpsr" regnum="33" bitsize="32" type="cpsr_flags"/>""")
+
+ self.expect("register read cpsr", substrs=["(A = 1)"])
+
+ @skipIfXmlSupportMissing
+ @skipIfRemote
+ def test_flags_requried_attributes(self):
+ # flags must have an id and size so the flags with "C" is the only valid one
+ # here.
+ self.setup_register_test("""\
+ <flags size="4">
+ <field name="A" start="0" end="0"/>
+ </flags>
+ <flags id="cpsr_flags">
+ <field name="B" start="0" end="0"/>
+ </flags>
+ <flags id="cpsr_flags" size="4">
+ <field name="C" start="0" end="0"/>
+ </flags>
+ <reg name="pc" bitsize="64"/>
+ <reg name="cpsr" regnum="33" bitsize="32" type="cpsr_flags"/>""")
+
+ self.expect("register read cpsr", substrs=["(C = 1)"])
+
+ @skipIfXmlSupportMissing
+ @skipIfRemote
+ def test_flags_set_even_if_format_set(self):
+ # lldb also sends "format". If that is set, we should still read the
+ # flags type.
+ self.setup_register_test("""\
+ <flags id="cpsr_flags" size="4">
+ <field name="B" start="0" end="0"/>
+ </flags>
+ <reg name="pc" bitsize="64"/>
+ <reg name="cpsr" regnum="33" bitsize="32" type="cpsr_flags"
+ format="example"/>""")
+
+ self.expect("register read cpsr", substrs=["(B = 1)"])
+
+ @skipIfXmlSupportMissing
+ @skipIfRemote
+ def test_flags_set_even_if_encoding_set(self):
+ # lldb also sends "encoding". If that is set, we should still read the
+ # flags type.
+ self.setup_register_test("""\
+ <flags id="cpsr_flags" size="4">
+ <field name="B" start="0" end="0"/>
+ </flags>
+ <reg name="pc" bitsize="64"/>
+ <reg name="cpsr" regnum="33" bitsize="32" type="cpsr_flags"
+ encoding="example"/>""")
+
+ self.expect("register read cpsr", substrs=["(B = 1)"])
+
+ @skipIfXmlSupportMissing
+ @skipIfRemote
+ def test_flags_set_even_if_encoding_and_format_set(self):
+ # As above but both encoding and format are set.
+ self.setup_register_test("""\
+ <flags id="cpsr_flags" size="4">
+ <field name="B" start="0" end="0"/>
+ </flags>
+ <reg name="pc" bitsize="64"/>
+ <reg name="cpsr" regnum="33" bitsize="32" type="cpsr_flags"
+ encoding="example" format="example"/>""")
+
+ self.expect("register read cpsr", substrs=["(B = 1)"])
+
+ @skipIfXmlSupportMissing
+ @skipIfRemote
+ def test_flags_multiple_lines(self):
+ # Since we use C types they follow lldb's usual decisions as to whether
+ # to print them on one line or many. Long field names will usually mean
+ # many lines.
+ self.setup_flags_test(
+ '<field name="this_is_a_long_field_0" start="0" end="0"/>'
+ '<field name="this_is_a_long_field_1" start="1" end="1"/>'
+ '<field name="this_is_a_long_field_2" start="2" end="2"/>'
+ '<field name="this_is_a_long_field_3" start="3" end="3"/>')
+
+ self.expect("register read cpsr", substrs=[
+ " cpsr = 0xeeee7777\n"
+ " = {\n"
+ " this_is_a_long_field_3 = 0\n"
+ " this_is_a_long_field_2 = 1\n"
+ " this_is_a_long_field_1 = 1\n"
+ " this_is_a_long_field_0 = 1\n"
+ " }"])
+
+ @skipIfXmlSupportMissing
+ @skipIfRemote
+ def test_flags_child_limit(self):
+ # Flags print like C types so they should follow the child limit setting.
+ self.runCmd("settings set target.max-children-count 3")
+ self.setup_flags_test(
+ '<field name="field_0" start="0" end="0"/>'
+ '<field name="field_1" start="1" end="1"/>'
+ '<field name="field_2" start="2" end="2"/>')
+
+ self.expect("register read cpsr", substrs=["= (field_2 = 1, field_1 = 1, ...)"])
+
+ @skipIfXmlSupportMissing
+ @skipIfRemote
+ def test_xml_includes(self):
+ # Certain targets e.g. s390x QEMU split their defintions over multiple
+ # files that are included into target.xml.
+ self.setup_multidoc_test({
+ # The formatting is very specific here. lldb doesn't like leading
+ # spaces, and nested tags must be indented more than their parent.
+ 'target.xml' : dedent("""\
+ <?xml version="1.0"?>
+ <target version="1.0">
+ <architecture>aarch64</architecture>
+ <xi:include href="core.xml"/>
+ </target>"""),
+ 'core.xml' : dedent("""\
+ <?xml version="1.0"?>
+ <feature name="org.gnu.gdb.aarch64.core">
+ <flags id="cpsr_flags" size="4">
+ <field name="B" start="0" end="0"/>
+ </flags>
+ <reg name="pc" bitsize="64"/>
+ <reg name="x0" regnum="0" bitsize="64" type="x0_flags"/>
+ <reg name="cpsr" regnum="33" bitsize="32" type="cpsr_flags"/>
+ </feature>
+ """),
+ })
+
+ self.expect("register read cpsr", substrs=["(B = 1)"])
+
+ @skipIfXmlSupportMissing
+ @skipIfRemote
+ def test_xml_includes_multiple(self):
+ self.setup_multidoc_test({
+ 'target.xml' : dedent("""\
+ <?xml version="1.0"?>
+ <target version="1.0">
+ <architecture>aarch64</architecture>
+ <xi:include href="core.xml"/>
+ <xi:include href="core-2.xml"/>
+ </target>"""),
+ 'core.xml' : dedent("""\
+ <?xml version="1.0"?>
+ <feature name="org.gnu.gdb.aarch64.core">
+ <flags id="x0_flags" size="4">
+ <field name="B" start="0" end="0"/>
+ </flags>
+ <reg name="pc" bitsize="64"/>
+ <reg name="x0" regnum="0" bitsize="64" type="x0_flags"/>
+ </feature>"""),
+ 'core-2.xml' : dedent("""\
+ <?xml version="1.0"?>
+ <feature name="org.gnu.gdb.aarch64.core">
+ <flags id="cpsr_flags" size="4">
+ <field name="C" start="0" end="0"/>
+ </flags>
+ <reg name="cpsr" regnum="33" bitsize="32" type="cpsr_flags"/>
+ </feature>
+ """),
+ })
+
+ self.expect("register read x0 cpsr", substrs=["(B = 1)", "(C = 1)"])
+
+ @skipIfXmlSupportMissing
+ @skipIfRemote
+ def test_xml_includes_flags_redefined(self):
+ self.setup_multidoc_test({
+ 'target.xml' : dedent("""\
+ <?xml version="1.0"?>
+ <target version="1.0">
+ <architecture>aarch64</architecture>
+ <xi:include href="core.xml"/>
+ <xi:include href="core-2.xml"/>
+ </target>"""),
+ # Treating xi:include as a textual include, my_flags is first defined
+ # in core.xml. The second definition in core-2.xml
+ # is ignored.
+ 'core.xml' : dedent("""\
+ <?xml version="1.0"?>
+ <feature name="org.gnu.gdb.aarch64.core">
+ <flags id="my_flags" size="4">
+ <field name="correct" start="0" end="0"/>
+ </flags>
+ <reg name="pc" bitsize="64"/>
+ <reg name="x0" regnum="0" bitsize="64" type="my_flags"/>
+ </feature>"""),
+ # The my_flags here is ignored, so cpsr will use the my_flags from above.
+ 'core-2.xml' : dedent("""\
+ <?xml version="1.0"?>
+ <feature name="org.gnu.gdb.aarch64.core">
+ <flags id="my_flags" size="4">
+ <field name="incorrect" start="0" end="0"/>
+ </flags>
+ <reg name="cpsr" regnum="33" bitsize="32" type="my_flags"/>
+ </feature>
+ """),
+ })
+
+ self.expect("register read x0", substrs=["(correct = 1)"])
+ self.expect("register read cpsr", substrs=["(correct = 1)"])
make_field(20, 21), make_field(12, 19), make_field(8, 11),
make_field(0, 7)});
}
+
+TEST(RegisterFieldsTest, ReverseFieldOrder) {
+ // Unchanged
+ RegisterFlags rf("", 4, {make_field(0, 31)});
+ ASSERT_EQ(0x12345678ULL, rf.ReverseFieldOrder(0x12345678));
+
+ // Swap the two halves around.
+ RegisterFlags rf2("", 4, {make_field(16, 31), make_field(0, 15)});
+ ASSERT_EQ(0x56781234ULL, rf2.ReverseFieldOrder(0x12345678));
+
+ // Many small fields.
+ RegisterFlags rf3("", 4,
+ {make_field(31, 31), make_field(30, 30), make_field(29, 29),
+ make_field(28, 28)});
+ ASSERT_EQ(0x00000005ULL, rf3.ReverseFieldOrder(0xA0000000));
+}
* LLDB is now able to show the subtype of signals found in a core file. For example
memory tagging specific segfaults such as ``SIGSEGV: sync tag check fault``.
+* LLDB can now display register fields if they are described in target XML sent
+ by a debug server such as ``gdbserver`` (``lldb-server`` does not currently produce
+ this information). Fields are only printed when reading named registers, for
+ example ``register read cpsr``. They are not shown when reading a register set,
+ ``register read -s 0``.
+
Changes to Sanitizers
---------------------
* For Darwin users that override weak symbols, note that the dynamic linker will