Skip to content

Commit e07a421

Browse files
committed
[lldb] Show register fields using bitfield struct types
This change uses the information from target.xml sent by the GDB stub to produce C types that we can use to print register fields. lldb-server *does not* produce this information yet. This will only work with GDB stubs that do. gdbserver or qemu are 2 I know of. Testing is added that uses a mocked lldb-server. ``` (lldb) register read cpsr x0 fpcr fpsr x1 cpsr = 0x60001000 = (N = 0, Z = 1, C = 1, V = 0, TCO = 0, DIT = 0, UAO = 0, PAN = 0, SS = 0, IL = 0, SSBS = 1, BTYPE = 0, D = 0, A = 0, I = 0, F = 0, nRW = 0, EL = 0, SP = 0) ``` Only "register read" will display fields, and only when we are not printing a register block. For example, cpsr is a 32 bit register. Using the target's scratch type system we construct a type: ``` struct __attribute__((__packed__)) cpsr { uint32_t N : 1; uint32_t Z : 1; ... uint32_t EL : 2; uint32_t SP : 1; }; ``` If this register had unallocated bits in it, those would have been filled in by RegisterFlags as anonymous fields. A new option "SetChildPrintingDecider" is added so we can disable printing those. Important things about this type: * It is packed so that sizeof(struct cpsr) == sizeof(the real register). (this will hold for all flags types we create) * Each field has the same storage type, which is the same as the type of the raw register value. This prevents fields being spilt over into more storage units, as is allowed by most ABIs. * Each bitfield size matches that of its register field. * The most significant field is first. The last point is required because the most significant bit (MSB) being on the left/top of a print out matches what you'd expect to see in an architecture manual. In addition, having lldb print a different field order on big/little endian hosts is not acceptable. As a consequence, if the target is little endian we have to reverse the order of the fields in the value. The value of each field remains the same. For example 0b01 doesn't become 0b10, it just shifts up or down. This is needed because clang's type system assumes that for a struct like the one above, the least significant bit (LSB) will be first for a little endian target. We need the MSB to be first. Finally, if lldb's host is a different endian to the target we have to byte swap the host endian value to match the endian of the target's typesystem. | Host Endian | Target Endian | Field Order Swap | Byte Order Swap | |-------------|---------------|------------------|-----------------| | Little | Little | Yes | No | | Big | Little | Yes | Yes | | Little | Big | No | Yes | | Big | Big | No | No | Testing was done as follows: * Little -> Little * LE AArch64 native debug. * Big -> Little * s390x lldb running under QEMU, connected to LE AArch64 target. * Little -> Big * LE AArch64 lldb connected to QEMU's GDB stub, which is running an s390x program. * Big -> Big * s390x lldb running under QEMU, connected to another QEMU's GDB stub, which is running an s390x program. As we are not allowed to link core code to plugins directly, I have added a new plugin RegisterTypeBuilder. There is one implementation of this, RegisterTypeBuilderClang, which uses TypeSystemClang to build the CompilerType from the register fields. Reviewed By: jasonmolenda Differential Revision: https://reviews.llvm.org/D145580
1 parent 842879b commit e07a421

21 files changed

+902
-8
lines changed

lldb/include/lldb/Core/DumpRegisterValue.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#define LLDB_CORE_DUMPREGISTERVALUE_H
1111

1212
#include "lldb/lldb-enumerations.h"
13+
#include "lldb/lldb-forward.h"
1314
#include <cstdint>
1415

1516
namespace lldb_private {
@@ -21,11 +22,15 @@ class Stream;
2122

2223
// The default value of 0 for reg_name_right_align_at means no alignment at
2324
// all.
25+
// Set print_flags to true to print register fields if they are available.
26+
// If you do so, target_sp must be non-null for it to work.
2427
void DumpRegisterValue(const RegisterValue &reg_val, Stream *s,
2528
const RegisterInfo *reg_info, bool prefix_with_name,
2629
bool prefix_with_alt_name, lldb::Format format,
2730
uint32_t reg_name_right_align_at = 0,
28-
ExecutionContextScope *exe_scope = nullptr);
31+
ExecutionContextScope *exe_scope = nullptr,
32+
bool print_flags = false,
33+
lldb::TargetSP target_sp = nullptr);
2934

3035
} // namespace lldb_private
3136

lldb/include/lldb/Core/PluginManager.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,15 @@ class PluginManager {
253253
static void AutoCompleteProcessName(llvm::StringRef partial_name,
254254
CompletionRequest &request);
255255

256+
// Register Type Provider
257+
static bool RegisterPlugin(llvm::StringRef name, llvm::StringRef description,
258+
RegisterTypeBuilderCreateInstance create_callback);
259+
260+
static bool
261+
UnregisterPlugin(RegisterTypeBuilderCreateInstance create_callback);
262+
263+
static lldb::RegisterTypeBuilderSP GetRegisterTypeBuilder(Target &target);
264+
256265
// ScriptInterpreter
257266
static bool RegisterPlugin(llvm::StringRef name, llvm::StringRef description,
258267
lldb::ScriptLanguage script_lang,

lldb/include/lldb/DataFormatters/DumpValueObjectOptions.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ class DumpValueObjectOptions {
5353
const DumpValueObjectOptions &, Stream &)>
5454
DeclPrintingHelper;
5555

56+
typedef std::function<bool(ConstString)> ChildPrintingDecider;
57+
5658
static const DumpValueObjectOptions DefaultOptions() {
5759
static DumpValueObjectOptions g_default_options;
5860

@@ -70,6 +72,8 @@ class DumpValueObjectOptions {
7072

7173
DumpValueObjectOptions &SetDeclPrintingHelper(DeclPrintingHelper helper);
7274

75+
DumpValueObjectOptions &SetChildPrintingDecider(ChildPrintingDecider decider);
76+
7377
DumpValueObjectOptions &SetShowTypes(bool show = false);
7478

7579
DumpValueObjectOptions &SetShowLocation(bool show = false);
@@ -136,6 +140,7 @@ class DumpValueObjectOptions {
136140
lldb::LanguageType m_varformat_language = lldb::eLanguageTypeUnknown;
137141
PointerDepth m_max_ptr_depth;
138142
DeclPrintingHelper m_decl_printing_helper;
143+
ChildPrintingDecider m_child_printing_decider;
139144
PointerAsArraySettings m_pointer_as_array;
140145
bool m_use_synthetic : 1;
141146
bool m_scope_already_checked : 1;

lldb/include/lldb/Target/RegisterFlags.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,23 @@ class RegisterFlags {
7171
RegisterFlags(std::string id, unsigned size,
7272
const std::vector<Field> &fields);
7373

74+
// Reverse the order of the fields, keeping their values the same.
75+
// For example a field from bit 31 to 30 with value 0b10 will become bits
76+
// 1 to 0, with the same 0b10 value.
77+
// Use this when you are going to show the register using a bitfield struct
78+
// type. If that struct expects MSB first and you are on little endian where
79+
// LSB would be first, this corrects that (and vice versa for big endian).
80+
template <typename T> T ReverseFieldOrder(T value) const {
81+
T ret = 0;
82+
unsigned shift = 0;
83+
for (auto field : GetFields()) {
84+
ret |= field.GetValue(value) << shift;
85+
shift += field.GetSizeInBits();
86+
}
87+
88+
return ret;
89+
}
90+
7491
const std::vector<Field> &GetFields() const { return m_fields; }
7592
const std::string &GetID() const { return m_id; }
7693
unsigned GetSize() const { return m_size; }
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//===-- RegisterTypeBuilder.h -----------------------------------*- C++ -*-===//
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+
#ifndef LLDB_TARGET_REGISTER_TYPE_BUILDER_H
10+
#define LLDB_TARGET_REGISTER_TYPE_BUILDER_H
11+
12+
#include "lldb/Core/PluginInterface.h"
13+
#include "lldb/lldb-private.h"
14+
15+
namespace lldb_private {
16+
17+
class RegisterTypeBuilder : public PluginInterface {
18+
public:
19+
~RegisterTypeBuilder() override = default;
20+
21+
virtual CompilerType GetRegisterType(const std::string &name,
22+
const lldb_private::RegisterFlags &flags,
23+
uint32_t byte_size) = 0;
24+
25+
protected:
26+
RegisterTypeBuilder() = default;
27+
28+
private:
29+
RegisterTypeBuilder(const RegisterTypeBuilder &) = delete;
30+
const RegisterTypeBuilder &operator=(const RegisterTypeBuilder &) = delete;
31+
};
32+
33+
} // namespace lldb_private
34+
35+
#endif // LLDB_TARGET_REGISTER_TYPE_BUILDER_H

lldb/include/lldb/Target/Target.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1243,6 +1243,10 @@ class Target : public std::enable_shared_from_this<Target>,
12431243
/// if none can be found.
12441244
llvm::Expected<lldb_private::Address> GetEntryPointAddress();
12451245

1246+
CompilerType GetRegisterType(const std::string &name,
1247+
const lldb_private::RegisterFlags &flags,
1248+
uint32_t byte_size);
1249+
12461250
// Target Stop Hooks
12471251
class StopHook : public UserID {
12481252
public:

lldb/include/lldb/lldb-forward.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ class REPL;
175175
class RecognizedStackFrame;
176176
class RegisterCheckpoint;
177177
class RegisterContext;
178+
class RegisterTypeBuilder;
178179
class RegisterValue;
179180
class RegularExpression;
180181
class RichManglingContext;
@@ -371,6 +372,8 @@ typedef std::shared_ptr<lldb_private::ProcessLaunchInfo> ProcessLaunchInfoSP;
371372
typedef std::weak_ptr<lldb_private::Process> ProcessWP;
372373
typedef std::shared_ptr<lldb_private::RegisterCheckpoint> RegisterCheckpointSP;
373374
typedef std::shared_ptr<lldb_private::RegisterContext> RegisterContextSP;
375+
typedef std::shared_ptr<lldb_private::RegisterTypeBuilder>
376+
RegisterTypeBuilderSP;
374377
typedef std::shared_ptr<lldb_private::RegularExpression> RegularExpressionSP;
375378
typedef std::shared_ptr<lldb_private::Queue> QueueSP;
376379
typedef std::weak_ptr<lldb_private::Queue> QueueWP;

lldb/include/lldb/lldb-private-interfaces.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ typedef lldb::PlatformSP (*PlatformCreateInstance)(bool force,
8282
typedef lldb::ProcessSP (*ProcessCreateInstance)(
8383
lldb::TargetSP target_sp, lldb::ListenerSP listener_sp,
8484
const FileSpec *crash_file_path, bool can_connect);
85+
typedef lldb::RegisterTypeBuilderSP (*RegisterTypeBuilderCreateInstance)(
86+
Target &target);
8587
typedef lldb::ScriptInterpreterSP (*ScriptInterpreterCreateInstance)(
8688
Debugger &debugger);
8789
typedef SymbolFile *(*SymbolFileCreateInstance)(lldb::ObjectFileSP objfile_sp);

lldb/source/Commands/CommandObjectRegister.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ class CommandObjectRegisterRead : public CommandObjectParsed {
8484
Options *GetOptions() override { return &m_option_group; }
8585

8686
bool DumpRegister(const ExecutionContext &exe_ctx, Stream &strm,
87-
RegisterContext *reg_ctx, const RegisterInfo *reg_info) {
87+
RegisterContext *reg_ctx, const RegisterInfo *reg_info,
88+
bool print_flags) {
8889
if (reg_info) {
8990
RegisterValue reg_value;
9091

@@ -95,7 +96,8 @@ class CommandObjectRegisterRead : public CommandObjectParsed {
9596
bool prefix_with_name = !prefix_with_altname;
9697
DumpRegisterValue(reg_value, &strm, reg_info, prefix_with_name,
9798
prefix_with_altname, m_format_options.GetFormat(), 8,
98-
exe_ctx.GetBestExecutionContextScope());
99+
exe_ctx.GetBestExecutionContextScope(), print_flags,
100+
exe_ctx.GetTargetSP());
99101
if ((reg_info->encoding == eEncodingUint) ||
100102
(reg_info->encoding == eEncodingSint)) {
101103
Process *process = exe_ctx.GetProcessPtr();
@@ -142,7 +144,8 @@ class CommandObjectRegisterRead : public CommandObjectParsed {
142144
if (primitive_only && reg_info && reg_info->value_regs)
143145
continue;
144146

145-
if (DumpRegister(exe_ctx, strm, reg_ctx, reg_info))
147+
if (DumpRegister(exe_ctx, strm, reg_ctx, reg_info,
148+
/*print_flags=*/false))
146149
++available_count;
147150
else
148151
++unavailable_count;
@@ -218,7 +221,8 @@ class CommandObjectRegisterRead : public CommandObjectParsed {
218221
reg_info = reg_ctx->GetRegisterInfoByName(arg_str);
219222

220223
if (reg_info) {
221-
if (!DumpRegister(m_exe_ctx, strm, reg_ctx, reg_info))
224+
if (!DumpRegister(m_exe_ctx, strm, reg_ctx, reg_info,
225+
/*print_flags=*/true))
222226
strm.Printf("%-12s = error: unavailable\n", reg_info->name);
223227
} else {
224228
result.AppendErrorWithFormat("Invalid register name '%s'.\n",

lldb/source/Core/DumpRegisterValue.cpp

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,63 @@
88

99
#include "lldb/Core/DumpRegisterValue.h"
1010
#include "lldb/Core/DumpDataExtractor.h"
11+
#include "lldb/Core/ValueObject.h"
12+
#include "lldb/Core/ValueObjectConstResult.h"
13+
#include "lldb/DataFormatters/DumpValueObjectOptions.h"
1114
#include "lldb/Utility/DataExtractor.h"
15+
#include "lldb/Utility/Endian.h"
1216
#include "lldb/Utility/RegisterValue.h"
1317
#include "lldb/Utility/StreamString.h"
1418
#include "lldb/lldb-private-types.h"
1519

1620
using namespace lldb;
1721

22+
static uint32_t swap_value(uint32_t v) { return __builtin_bswap32(v); }
23+
static uint64_t swap_value(uint64_t v) { return __builtin_bswap64(v); }
24+
25+
template <typename T>
26+
static void dump_type_value(lldb_private::CompilerType &fields_type, T value,
27+
lldb_private::ExecutionContextScope *exe_scope,
28+
const lldb_private::RegisterInfo &reg_info,
29+
lldb_private::Stream &strm) {
30+
lldb::ByteOrder target_order = exe_scope->CalculateProcess()->GetByteOrder();
31+
32+
// For the bitfield types we generate, it is expected that the fields are
33+
// in what is usually a big endian order. Most significant field first.
34+
// This is also clang's internal ordering and the order we want to print
35+
// them. On a big endian host this all matches up, for a little endian
36+
// host we have to swap the order of the fields before display.
37+
if (target_order == lldb::ByteOrder::eByteOrderLittle) {
38+
value = reg_info.flags_type->ReverseFieldOrder(value);
39+
}
40+
41+
// Then we need to match the target's endian on a byte level as well.
42+
if (lldb_private::endian::InlHostByteOrder() != target_order)
43+
value = swap_value(value);
44+
45+
lldb_private::DataExtractor data_extractor{
46+
&value, sizeof(T), lldb_private::endian::InlHostByteOrder(), 8};
47+
48+
lldb::ValueObjectSP vobj_sp = lldb_private::ValueObjectConstResult::Create(
49+
exe_scope, fields_type, lldb_private::ConstString(), data_extractor);
50+
lldb_private::DumpValueObjectOptions dump_options;
51+
lldb_private::DumpValueObjectOptions::ChildPrintingDecider decider =
52+
[](lldb_private::ConstString varname) {
53+
// Unnamed bit-fields are padding that we don't want to show.
54+
return varname.GetLength();
55+
};
56+
dump_options.SetChildPrintingDecider(decider).SetHideRootType(true);
57+
58+
vobj_sp->Dump(strm, dump_options);
59+
}
60+
1861
void lldb_private::DumpRegisterValue(const RegisterValue &reg_val, Stream *s,
1962
const RegisterInfo *reg_info,
2063
bool prefix_with_name,
2164
bool prefix_with_alt_name, Format format,
2265
uint32_t reg_name_right_align_at,
23-
ExecutionContextScope *exe_scope) {
66+
ExecutionContextScope *exe_scope,
67+
bool print_flags, TargetSP target_sp) {
2468
DataExtractor data;
2569
if (!reg_val.GetData(data))
2670
return;
@@ -76,4 +120,55 @@ void lldb_private::DumpRegisterValue(const RegisterValue &reg_val, Stream *s,
76120
0, // item_bit_size
77121
0, // item_bit_offset
78122
exe_scope);
123+
124+
if (!print_flags || !reg_info->flags_type || !exe_scope || !target_sp ||
125+
(reg_info->byte_size != 4 && reg_info->byte_size != 8))
126+
return;
127+
128+
CompilerType fields_type = target_sp->GetRegisterType(
129+
reg_info->name, *reg_info->flags_type, reg_info->byte_size);
130+
131+
// Use a new stream so we can remove a trailing newline later.
132+
StreamString fields_stream;
133+
134+
if (reg_info->byte_size == 4) {
135+
dump_type_value(fields_type, reg_val.GetAsUInt32(), exe_scope, *reg_info,
136+
fields_stream);
137+
} else {
138+
dump_type_value(fields_type, reg_val.GetAsUInt64(), exe_scope, *reg_info,
139+
fields_stream);
140+
}
141+
142+
// Registers are indented like:
143+
// (lldb) register read foo
144+
// foo = 0x12345678
145+
// So we need to indent to match that.
146+
147+
// First drop the extra newline that the value printer added. The register
148+
// command will add one itself.
149+
llvm::StringRef fields_str = fields_stream.GetString().drop_back();
150+
151+
// End the line that contains " foo = 0x12345678".
152+
s->EOL();
153+
154+
// Then split the value lines and indent each one.
155+
bool first = true;
156+
while (fields_str.size()) {
157+
std::pair<llvm::StringRef, llvm::StringRef> split = fields_str.split('\n');
158+
fields_str = split.second;
159+
// Indent as far as the register name did.
160+
s->Printf(fmt.c_str(), "");
161+
162+
// Lines after the first won't have " = " so compensate for that.
163+
if (!first)
164+
(*s) << " ";
165+
first = false;
166+
167+
(*s) << split.first;
168+
169+
// On the last line we don't want a newline because the command will add
170+
// one too.
171+
if (fields_str.size())
172+
s->EOL();
173+
}
79174
}

lldb/source/Core/PluginManager.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -875,6 +875,45 @@ void PluginManager::AutoCompleteProcessName(llvm::StringRef name,
875875
}
876876
}
877877

878+
#pragma mark RegisterTypeBuilder
879+
880+
struct RegisterTypeBuilderInstance
881+
: public PluginInstance<RegisterTypeBuilderCreateInstance> {
882+
RegisterTypeBuilderInstance(llvm::StringRef name, llvm::StringRef description,
883+
CallbackType create_callback)
884+
: PluginInstance<RegisterTypeBuilderCreateInstance>(name, description,
885+
create_callback) {}
886+
};
887+
888+
typedef PluginInstances<RegisterTypeBuilderInstance>
889+
RegisterTypeBuilderInstances;
890+
891+
static RegisterTypeBuilderInstances &GetRegisterTypeBuilderInstances() {
892+
static RegisterTypeBuilderInstances g_instances;
893+
return g_instances;
894+
}
895+
896+
bool PluginManager::RegisterPlugin(
897+
llvm::StringRef name, llvm::StringRef description,
898+
RegisterTypeBuilderCreateInstance create_callback) {
899+
return GetRegisterTypeBuilderInstances().RegisterPlugin(name, description,
900+
create_callback);
901+
}
902+
903+
bool PluginManager::UnregisterPlugin(
904+
RegisterTypeBuilderCreateInstance create_callback) {
905+
return GetRegisterTypeBuilderInstances().UnregisterPlugin(create_callback);
906+
}
907+
908+
lldb::RegisterTypeBuilderSP
909+
PluginManager::GetRegisterTypeBuilder(Target &target) {
910+
const auto &instances = GetRegisterTypeBuilderInstances().GetInstances();
911+
// We assume that RegisterTypeBuilderClang is the only instance of this plugin
912+
// type and is always present.
913+
assert(instances.size());
914+
return instances[0].create_callback(target);
915+
}
916+
878917
#pragma mark ScriptInterpreter
879918

880919
struct ScriptInterpreterInstance

lldb/source/DataFormatters/DumpValueObjectOptions.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ using namespace lldb_private;
1616
DumpValueObjectOptions::DumpValueObjectOptions()
1717
: m_summary_sp(), m_root_valobj_name(),
1818
m_max_ptr_depth(PointerDepth{PointerDepth::Mode::Default, 0}),
19-
m_decl_printing_helper(), m_pointer_as_array(), m_use_synthetic(true),
19+
m_decl_printing_helper(), m_child_printing_decider(),
20+
m_pointer_as_array(), m_use_synthetic(true),
2021
m_scope_already_checked(false), m_flat_output(false), m_ignore_cap(false),
2122
m_show_types(false), m_show_location(false), m_use_objc(false),
2223
m_hide_root_type(false), m_hide_root_name(false), m_hide_name(false),
@@ -50,6 +51,12 @@ DumpValueObjectOptions::SetDeclPrintingHelper(DeclPrintingHelper helper) {
5051
return *this;
5152
}
5253

54+
DumpValueObjectOptions &
55+
DumpValueObjectOptions::SetChildPrintingDecider(ChildPrintingDecider decider) {
56+
m_child_printing_decider = decider;
57+
return *this;
58+
}
59+
5360
DumpValueObjectOptions &DumpValueObjectOptions::SetShowTypes(bool show) {
5461
m_show_types = show;
5562
return *this;

0 commit comments

Comments
 (0)