Skip to content

Commit 8530b1c

Browse files
authored
[lldb] Support custom LLVM formatting for variables (#91868)
Re-apply #81196, with a fix that handles the absence of llvm formatting: 3ba650e b47d425f
1 parent e1ed138 commit 8530b1c

File tree

5 files changed

+106
-10
lines changed

5 files changed

+106
-10
lines changed

lldb/docs/use/variable.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,15 @@ summary strings, regardless of the format they have applied to their types. To
460460
do that, you can use %format inside an expression path, as in ${var.x->x%u},
461461
which would display the value of x as an unsigned integer.
462462

463+
Additionally, custom output can be achieved by using an LLVM format string,
464+
commencing with the ``:`` marker. To illustrate, compare ``${var.byte%x}`` and
465+
``${var.byte:x-}``. The former uses lldb's builtin hex formatting (``x``),
466+
which unconditionally inserts a ``0x`` prefix, and also zero pads the value to
467+
match the size of the type. The latter uses ``llvm::formatv`` formatting
468+
(``:x-``), and will print only the hex value, with no ``0x`` prefix, and no
469+
padding. This raw control is useful when composing multiple pieces into a
470+
larger whole.
471+
463472
You can also use some other special format markers, not available for formats
464473
themselves, but which carry a special meaning when used in this context:
465474

lldb/source/Core/FormatEntity.cpp

Lines changed: 62 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
#include "llvm/ADT/STLExtras.h"
5858
#include "llvm/ADT/StringRef.h"
5959
#include "llvm/Support/Compiler.h"
60+
#include "llvm/Support/Regex.h"
6061
#include "llvm/TargetParser/Triple.h"
6162

6263
#include <cctype>
@@ -658,6 +659,37 @@ static char ConvertValueObjectStyleToChar(
658659
return '\0';
659660
}
660661

662+
/// Options supported by format_provider<T> for integral arithmetic types.
663+
/// See table in FormatProviders.h.
664+
static llvm::Regex LLVMFormatPattern{"x[-+]?\\d*|n|d", llvm::Regex::IgnoreCase};
665+
666+
static bool DumpValueWithLLVMFormat(Stream &s, llvm::StringRef options,
667+
ValueObject &valobj) {
668+
std::string formatted;
669+
std::string llvm_format = ("{0:" + options + "}").str();
670+
671+
auto type_info = valobj.GetTypeInfo();
672+
if ((type_info & eTypeIsInteger) && LLVMFormatPattern.match(options)) {
673+
if (type_info & eTypeIsSigned) {
674+
bool success = false;
675+
int64_t integer = valobj.GetValueAsSigned(0, &success);
676+
if (success)
677+
formatted = llvm::formatv(llvm_format.data(), integer);
678+
} else {
679+
bool success = false;
680+
uint64_t integer = valobj.GetValueAsUnsigned(0, &success);
681+
if (success)
682+
formatted = llvm::formatv(llvm_format.data(), integer);
683+
}
684+
}
685+
686+
if (formatted.empty())
687+
return false;
688+
689+
s.Write(formatted.data(), formatted.size());
690+
return true;
691+
}
692+
661693
static bool DumpValue(Stream &s, const SymbolContext *sc,
662694
const ExecutionContext *exe_ctx,
663695
const FormatEntity::Entry &entry, ValueObject *valobj) {
@@ -728,9 +760,12 @@ static bool DumpValue(Stream &s, const SymbolContext *sc,
728760
return RunScriptFormatKeyword(s, sc, exe_ctx, valobj, entry.string.c_str());
729761
}
730762

731-
llvm::StringRef subpath(entry.string);
763+
auto split = llvm::StringRef(entry.string).split(':');
764+
auto subpath = split.first;
765+
auto llvm_format = split.second;
766+
732767
// simplest case ${var}, just print valobj's value
733-
if (entry.string.empty()) {
768+
if (subpath.empty()) {
734769
if (entry.printf_format.empty() && entry.fmt == eFormatDefault &&
735770
entry.number == ValueObject::eValueObjectRepresentationStyleValue)
736771
was_plain_var = true;
@@ -739,22 +774,19 @@ static bool DumpValue(Stream &s, const SymbolContext *sc,
739774
target = valobj;
740775
} else // this is ${var.something} or multiple .something nested
741776
{
742-
if (entry.string[0] == '[')
777+
if (subpath[0] == '[')
743778
was_var_indexed = true;
744779
ScanBracketedRange(subpath, close_bracket_index,
745780
var_name_final_if_array_range, index_lower,
746781
index_higher);
747782

748783
Status error;
749784

750-
const std::string &expr_path = entry.string;
751-
752-
LLDB_LOGF(log, "[Debugger::FormatPrompt] symbol to expand: %s",
753-
expr_path.c_str());
785+
LLDB_LOG(log, "[Debugger::FormatPrompt] symbol to expand: {0}", subpath);
754786

755787
target =
756788
valobj
757-
->GetValueForExpressionPath(expr_path.c_str(), &reason_to_stop,
789+
->GetValueForExpressionPath(subpath, &reason_to_stop,
758790
&final_value_type, options, &what_next)
759791
.get();
760792

@@ -883,8 +915,18 @@ static bool DumpValue(Stream &s, const SymbolContext *sc,
883915
}
884916

885917
if (!is_array_range) {
886-
LLDB_LOGF(log,
887-
"[Debugger::FormatPrompt] dumping ordinary printable output");
918+
if (!llvm_format.empty()) {
919+
if (DumpValueWithLLVMFormat(s, llvm_format, *target)) {
920+
LLDB_LOGF(log, "dumping using llvm format");
921+
return true;
922+
} else {
923+
LLDB_LOG(
924+
log,
925+
"empty output using llvm format '{0}' - with type info flags {1}",
926+
entry.printf_format, target->GetTypeInfo());
927+
}
928+
}
929+
LLDB_LOGF(log, "dumping ordinary printable output");
888930
return target->DumpPrintableRepresentation(s, val_obj_display,
889931
custom_format);
890932
} else {
@@ -2227,6 +2269,16 @@ static Status ParseInternal(llvm::StringRef &format, Entry &parent_entry,
22272269
if (error.Fail())
22282270
return error;
22292271

2272+
llvm::StringRef entry_string(entry.string);
2273+
if (entry_string.contains(':')) {
2274+
auto [_, llvm_format] = entry_string.split(':');
2275+
if (!llvm_format.empty() && !LLVMFormatPattern.match(llvm_format)) {
2276+
error.SetErrorStringWithFormat("invalid llvm format: '%s'",
2277+
llvm_format.data());
2278+
return error;
2279+
}
2280+
}
2281+
22302282
if (verify_is_thread_id) {
22312283
if (entry.type != Entry::Type::ThreadID &&
22322284
entry.type != Entry::Type::ThreadProtocolID) {
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
C_SOURCES := main.c
2+
include Makefile.rules
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import lldb
2+
from lldbsuite.test.lldbtest import *
3+
import lldbsuite.test.lldbutil as lldbutil
4+
5+
6+
class TestCase(TestBase):
7+
def test_raw_bytes(self):
8+
self.build()
9+
lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.c"))
10+
self.runCmd("type summary add -s '${var.ubyte:x-2}${var.sbyte:x-2}!' Bytes")
11+
self.expect("v bytes", substrs=[" = 3001!"])
12+
13+
def test_bad_format(self):
14+
self.build()
15+
lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.c"))
16+
self.expect(
17+
"type summary add -s '${var.ubyte:y}!' Bytes",
18+
error=True,
19+
substrs=["invalid llvm format"],
20+
)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#include <stdint.h>
2+
#include <stdio.h>
3+
4+
struct Bytes {
5+
uint8_t ubyte;
6+
int8_t sbyte;
7+
};
8+
9+
int main() {
10+
struct Bytes bytes = {0x30, 0x01};
11+
(void)bytes;
12+
printf("break here\n");
13+
}

0 commit comments

Comments
 (0)