[Lldb-commits] [lldb] [lldb] Support custom LLVM formatting for variables (PR #81196)
Dave Lee via lldb-commits
lldb-commits at lists.llvm.org
Thu Mar 21 10:59:25 PDT 2024
https://github.com/kastiglione updated https://github.com/llvm/llvm-project/pull/81196
>From 81a2034ff2b41e30a1f5b82c86b4d5d4c429ed52 Mon Sep 17 00:00:00 2001
From: Dave Lee <davelee.com at gmail.com>
Date: Thu, 8 Feb 2024 13:59:12 -0800
Subject: [PATCH 1/6] [lldb] Support custom printf formatting for variables
---
lldb/source/Core/FormatEntity.cpp | 25 +++++++++++++++++++++++--
1 file changed, 23 insertions(+), 2 deletions(-)
diff --git a/lldb/source/Core/FormatEntity.cpp b/lldb/source/Core/FormatEntity.cpp
index fa5eadc6ff4e9a..0e929203935304 100644
--- a/lldb/source/Core/FormatEntity.cpp
+++ b/lldb/source/Core/FormatEntity.cpp
@@ -883,8 +883,29 @@ static bool DumpValue(Stream &s, const SymbolContext *sc,
}
if (!is_array_range) {
- LLDB_LOGF(log,
- "[Debugger::FormatPrompt] dumping ordinary printable output");
+ if (!entry.printf_format.empty()) {
+ auto type_info = target->GetTypeInfo();
+ if (type_info & eTypeIsInteger) {
+ if (type_info & eTypeIsSigned) {
+ bool success = false;
+ auto integer = target->GetValueAsSigned(0, &success);
+ if (success) {
+ LLDB_LOGF(log, "dumping using printf format");
+ s.Printf(entry.printf_format.c_str(), integer);
+ return true;
+ }
+ } else {
+ bool success = false;
+ auto integer = target->GetValueAsUnsigned(0, &success);
+ if (success) {
+ LLDB_LOGF(log, "dumping using printf format");
+ s.Printf(entry.printf_format.c_str(), integer);
+ return true;
+ }
+ }
+ }
+ }
+ LLDB_LOGF(log, "dumping ordinary printable output");
return target->DumpPrintableRepresentation(s, val_obj_display,
custom_format);
} else {
>From 335ab1de4b39257e3bbb3bd969a0dd6991747558 Mon Sep 17 00:00:00 2001
From: Dave Lee <davelee.com at gmail.com>
Date: Tue, 13 Feb 2024 13:26:35 -0800
Subject: [PATCH 2/6] Factor out DumpValueWithPrintf; Add test
---
lldb/source/Core/FormatEntity.cpp | 45 +++++++++++--------
.../custom-printf-summary/Makefile | 2 +
.../TestCustomPrintfSummary.py | 11 +++++
.../custom-printf-summary/main.c | 13 ++++++
4 files changed, 52 insertions(+), 19 deletions(-)
create mode 100644 lldb/test/API/functionalities/data-formatter/custom-printf-summary/Makefile
create mode 100644 lldb/test/API/functionalities/data-formatter/custom-printf-summary/TestCustomPrintfSummary.py
create mode 100644 lldb/test/API/functionalities/data-formatter/custom-printf-summary/main.c
diff --git a/lldb/source/Core/FormatEntity.cpp b/lldb/source/Core/FormatEntity.cpp
index 0e929203935304..57a05507d844cf 100644
--- a/lldb/source/Core/FormatEntity.cpp
+++ b/lldb/source/Core/FormatEntity.cpp
@@ -658,6 +658,25 @@ static char ConvertValueObjectStyleToChar(
return '\0';
}
+static bool DumpValueWithPrintf(Stream &s, llvm::StringRef format,
+ ValueObject &target) {
+ auto type_info = target.GetTypeInfo();
+ if (type_info & eTypeIsInteger) {
+ if (type_info & eTypeIsSigned) {
+ if (auto integer = target.GetValueAsSigned()) {
+ s.Printf(format.data(), *integer);
+ return true;
+ }
+ } else {
+ if (auto integer = target.GetValueAsUnsigned()) {
+ s.Printf(format.data(), *integer);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
static bool DumpValue(Stream &s, const SymbolContext *sc,
const ExecutionContext *exe_ctx,
const FormatEntity::Entry &entry, ValueObject *valobj) {
@@ -884,25 +903,13 @@ static bool DumpValue(Stream &s, const SymbolContext *sc,
if (!is_array_range) {
if (!entry.printf_format.empty()) {
- auto type_info = target->GetTypeInfo();
- if (type_info & eTypeIsInteger) {
- if (type_info & eTypeIsSigned) {
- bool success = false;
- auto integer = target->GetValueAsSigned(0, &success);
- if (success) {
- LLDB_LOGF(log, "dumping using printf format");
- s.Printf(entry.printf_format.c_str(), integer);
- return true;
- }
- } else {
- bool success = false;
- auto integer = target->GetValueAsUnsigned(0, &success);
- if (success) {
- LLDB_LOGF(log, "dumping using printf format");
- s.Printf(entry.printf_format.c_str(), integer);
- return true;
- }
- }
+ if (DumpValueWithPrintf(s, entry.printf_format, *target)) {
+ LLDB_LOGF(log, "dumping using printf format");
+ return true;
+ } else {
+ LLDB_LOG(log,
+ "unsupported printf format '{0}' - for type info flags {1}",
+ entry.printf_format, target->GetTypeInfo());
}
}
LLDB_LOGF(log, "dumping ordinary printable output");
diff --git a/lldb/test/API/functionalities/data-formatter/custom-printf-summary/Makefile b/lldb/test/API/functionalities/data-formatter/custom-printf-summary/Makefile
new file mode 100644
index 00000000000000..c9319d6e6888a4
--- /dev/null
+++ b/lldb/test/API/functionalities/data-formatter/custom-printf-summary/Makefile
@@ -0,0 +1,2 @@
+C_SOURCES := main.c
+include Makefile.rules
diff --git a/lldb/test/API/functionalities/data-formatter/custom-printf-summary/TestCustomPrintfSummary.py b/lldb/test/API/functionalities/data-formatter/custom-printf-summary/TestCustomPrintfSummary.py
new file mode 100644
index 00000000000000..7d196c66caaa79
--- /dev/null
+++ b/lldb/test/API/functionalities/data-formatter/custom-printf-summary/TestCustomPrintfSummary.py
@@ -0,0 +1,11 @@
+import lldb
+from lldbsuite.test.lldbtest import *
+import lldbsuite.test.lldbutil as lldbutil
+
+
+class TestCase(TestBase):
+ def test(self):
+ self.build()
+ lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.c"))
+ self.runCmd("type summary add -s '${var.ubyte%%2.2X}${var.sbyte%%2.2X}!' Bytes")
+ self.expect("v bytes", substrs=[" = 1001!"])
diff --git a/lldb/test/API/functionalities/data-formatter/custom-printf-summary/main.c b/lldb/test/API/functionalities/data-formatter/custom-printf-summary/main.c
new file mode 100644
index 00000000000000..8f92b9dafd32f3
--- /dev/null
+++ b/lldb/test/API/functionalities/data-formatter/custom-printf-summary/main.c
@@ -0,0 +1,13 @@
+#include <stdint.h>
+#include <stdio.h>
+
+struct Bytes {
+ uint8_t ubyte;
+ int8_t sbyte;
+};
+
+int main() {
+ struct Bytes bytes = {0x10, 0x01};
+ (void)bytes;
+ printf("break here\n");
+}
>From 678ab3360f790a0a1d3c3631fe406b790e4c16dd Mon Sep 17 00:00:00 2001
From: Dave Lee <davelee.com at gmail.com>
Date: Tue, 13 Feb 2024 14:40:10 -0800
Subject: [PATCH 3/6] Use main line API
---
lldb/source/Core/FormatEntity.cpp | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/lldb/source/Core/FormatEntity.cpp b/lldb/source/Core/FormatEntity.cpp
index 57a05507d844cf..ad93da58d2502c 100644
--- a/lldb/source/Core/FormatEntity.cpp
+++ b/lldb/source/Core/FormatEntity.cpp
@@ -663,13 +663,17 @@ static bool DumpValueWithPrintf(Stream &s, llvm::StringRef format,
auto type_info = target.GetTypeInfo();
if (type_info & eTypeIsInteger) {
if (type_info & eTypeIsSigned) {
- if (auto integer = target.GetValueAsSigned()) {
- s.Printf(format.data(), *integer);
+ bool success = false;
+ auto integer = target.GetValueAsSigned(0, &success);
+ if (success) {
+ s.Printf(format.data(), integer);
return true;
}
} else {
- if (auto integer = target.GetValueAsUnsigned()) {
- s.Printf(format.data(), *integer);
+ bool success = false;
+ auto integer = target.GetValueAsUnsigned(0, &success);
+ if (success) {
+ s.Printf(format.data(), integer);
return true;
}
}
>From bb4a2782631b86f4e7205beeb77297c56359c461 Mon Sep 17 00:00:00 2001
From: Dave Lee <davelee.com at gmail.com>
Date: Wed, 14 Feb 2024 10:12:00 -0800
Subject: [PATCH 4/6] Mention printf formatting in variable.rst
---
lldb/docs/use/variable.rst | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/lldb/docs/use/variable.rst b/lldb/docs/use/variable.rst
index 8eaed6405315b4..d197c89a99e579 100644
--- a/lldb/docs/use/variable.rst
+++ b/lldb/docs/use/variable.rst
@@ -460,6 +460,15 @@ summary strings, regardless of the format they have applied to their types. To
do that, you can use %format inside an expression path, as in ${var.x->x%u},
which would display the value of x as an unsigned integer.
+Additionally, custom output can be achieved by using a printf format string
+after the ``%`` marker. When a summary string contains two ``%`` markers, then
+it is using a custom printf format. To illustrate, compare ``${var.byte%x}``
+and ``${var.byte%%x}``. The former uses lldb's hex formatting (``x``), which
+automatically includes a ``0x`` prefix, and also zero pads the value to match
+the size of the type. The latter uses printf formatting (``%x``), and will
+print only the hex value, with no ``0x`` prefix, and no padding. This raw
+control is useful when composing multiple pieces into a larger whole.
+
You can also use some other special format markers, not available for formats
themselves, but which carry a special meaning when used in this context:
>From 24a0bb43b0d5571ad35d2819cc39028eb7c21654 Mon Sep 17 00:00:00 2001
From: Dave Lee <davelee.com at gmail.com>
Date: Tue, 19 Mar 2024 16:06:05 -0700
Subject: [PATCH 5/6] Switch from printf to llvm::format
---
lldb/docs/use/variable.rst | 16 +++----
lldb/source/Core/FormatEntity.cpp | 46 ++++++++++---------
.../TestCustomPrintfSummary.py | 2 +-
3 files changed, 34 insertions(+), 30 deletions(-)
diff --git a/lldb/docs/use/variable.rst b/lldb/docs/use/variable.rst
index d197c89a99e579..e9175b25336ba9 100644
--- a/lldb/docs/use/variable.rst
+++ b/lldb/docs/use/variable.rst
@@ -460,14 +460,14 @@ summary strings, regardless of the format they have applied to their types. To
do that, you can use %format inside an expression path, as in ${var.x->x%u},
which would display the value of x as an unsigned integer.
-Additionally, custom output can be achieved by using a printf format string
-after the ``%`` marker. When a summary string contains two ``%`` markers, then
-it is using a custom printf format. To illustrate, compare ``${var.byte%x}``
-and ``${var.byte%%x}``. The former uses lldb's hex formatting (``x``), which
-automatically includes a ``0x`` prefix, and also zero pads the value to match
-the size of the type. The latter uses printf formatting (``%x``), and will
-print only the hex value, with no ``0x`` prefix, and no padding. This raw
-control is useful when composing multiple pieces into a larger whole.
+Additionally, custom output can be achieved by using an LLVM format string,
+commencing with the ``:`` marker. To illustrate, compare ``${var.byte%x}`` and
+``${var.byte:x-}``. The former uses lldb's builtin hex formatting (``x``),
+which unconditionally inserts a ``0x`` prefix, and also zero pads the value to
+match the size of the type. The latter uses ``llvm::formatv`` formatting
+(``:x-``), and will print only the hex value, with no ``0x`` prefix, and no
+padding. This raw control is useful when composing multiple pieces into a
+larger whole.
You can also use some other special format markers, not available for formats
themselves, but which carry a special meaning when used in this context:
diff --git a/lldb/source/Core/FormatEntity.cpp b/lldb/source/Core/FormatEntity.cpp
index ad93da58d2502c..fb5758bcb31471 100644
--- a/lldb/source/Core/FormatEntity.cpp
+++ b/lldb/source/Core/FormatEntity.cpp
@@ -658,27 +658,31 @@ static char ConvertValueObjectStyleToChar(
return '\0';
}
-static bool DumpValueWithPrintf(Stream &s, llvm::StringRef format,
- ValueObject &target) {
+static bool DumpValueWithLLVMFormat(Stream &s, llvm::StringRef options,
+ ValueObject &target) {
+ std::string formatted;
+ std::string llvm_format = ("{0:" + options + "}").str();
+
auto type_info = target.GetTypeInfo();
if (type_info & eTypeIsInteger) {
if (type_info & eTypeIsSigned) {
bool success = false;
auto integer = target.GetValueAsSigned(0, &success);
- if (success) {
- s.Printf(format.data(), integer);
- return true;
- }
+ if (success)
+ formatted = llvm::formatv(llvm_format.data(), integer);
} else {
bool success = false;
auto integer = target.GetValueAsUnsigned(0, &success);
- if (success) {
- s.Printf(format.data(), integer);
- return true;
- }
+ if (success)
+ formatted = llvm::formatv(llvm_format.data(), integer);
}
}
- return false;
+
+ if (formatted.empty())
+ return false;
+
+ s.Write(formatted.data(), formatted.size());
+ return true;
}
static bool DumpValue(Stream &s, const SymbolContext *sc,
@@ -751,9 +755,12 @@ static bool DumpValue(Stream &s, const SymbolContext *sc,
return RunScriptFormatKeyword(s, sc, exe_ctx, valobj, entry.string.c_str());
}
- llvm::StringRef subpath(entry.string);
+ auto split = llvm::StringRef(entry.string).split(':');
+ auto subpath = split.first;
+ auto llvm_format = split.second;
+
// simplest case ${var}, just print valobj's value
- if (entry.string.empty()) {
+ if (subpath.empty()) {
if (entry.printf_format.empty() && entry.fmt == eFormatDefault &&
entry.number == ValueObject::eValueObjectRepresentationStyleValue)
was_plain_var = true;
@@ -762,7 +769,7 @@ static bool DumpValue(Stream &s, const SymbolContext *sc,
target = valobj;
} else // this is ${var.something} or multiple .something nested
{
- if (entry.string[0] == '[')
+ if (subpath[0] == '[')
was_var_indexed = true;
ScanBracketedRange(subpath, close_bracket_index,
var_name_final_if_array_range, index_lower,
@@ -770,14 +777,11 @@ static bool DumpValue(Stream &s, const SymbolContext *sc,
Status error;
- const std::string &expr_path = entry.string;
-
- LLDB_LOGF(log, "[Debugger::FormatPrompt] symbol to expand: %s",
- expr_path.c_str());
+ LLDB_LOG(log, "[Debugger::FormatPrompt] symbol to expand: {0}", subpath);
target =
valobj
- ->GetValueForExpressionPath(expr_path.c_str(), &reason_to_stop,
+ ->GetValueForExpressionPath(subpath, &reason_to_stop,
&final_value_type, options, &what_next)
.get();
@@ -906,8 +910,8 @@ static bool DumpValue(Stream &s, const SymbolContext *sc,
}
if (!is_array_range) {
- if (!entry.printf_format.empty()) {
- if (DumpValueWithPrintf(s, entry.printf_format, *target)) {
+ if (!llvm_format.empty()) {
+ if (DumpValueWithLLVMFormat(s, llvm_format, *target)) {
LLDB_LOGF(log, "dumping using printf format");
return true;
} else {
diff --git a/lldb/test/API/functionalities/data-formatter/custom-printf-summary/TestCustomPrintfSummary.py b/lldb/test/API/functionalities/data-formatter/custom-printf-summary/TestCustomPrintfSummary.py
index 7d196c66caaa79..caf072cebdf303 100644
--- a/lldb/test/API/functionalities/data-formatter/custom-printf-summary/TestCustomPrintfSummary.py
+++ b/lldb/test/API/functionalities/data-formatter/custom-printf-summary/TestCustomPrintfSummary.py
@@ -7,5 +7,5 @@ class TestCase(TestBase):
def test(self):
self.build()
lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.c"))
- self.runCmd("type summary add -s '${var.ubyte%%2.2X}${var.sbyte%%2.2X}!' Bytes")
+ self.runCmd("type summary add -s '${var.ubyte:x-2}${var.sbyte:x-2}!' Bytes")
self.expect("v bytes", substrs=[" = 1001!"])
>From 16b8dfab53008c657c82b0ae35510c7050481bdb Mon Sep 17 00:00:00 2001
From: Dave Lee <davelee.com at gmail.com>
Date: Thu, 21 Mar 2024 10:58:57 -0700
Subject: [PATCH 6/6] Use regex to validate llvm formatting
---
lldb/source/Core/FormatEntity.cpp | 15 ++++++++++-----
.../TestCustomPrintfSummary.py | 11 +++++++++--
.../data-formatter/custom-printf-summary/main.c | 2 +-
3 files changed, 20 insertions(+), 8 deletions(-)
diff --git a/lldb/source/Core/FormatEntity.cpp b/lldb/source/Core/FormatEntity.cpp
index fb5758bcb31471..1632f90cc7f144 100644
--- a/lldb/source/Core/FormatEntity.cpp
+++ b/lldb/source/Core/FormatEntity.cpp
@@ -57,6 +57,7 @@
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Compiler.h"
+#include "llvm/Support/Regex.h"
#include "llvm/TargetParser/Triple.h"
#include <cctype>
@@ -659,20 +660,24 @@ static char ConvertValueObjectStyleToChar(
}
static bool DumpValueWithLLVMFormat(Stream &s, llvm::StringRef options,
- ValueObject &target) {
+ ValueObject &valobj) {
std::string formatted;
std::string llvm_format = ("{0:" + options + "}").str();
- auto type_info = target.GetTypeInfo();
- if (type_info & eTypeIsInteger) {
+ // Options supported by format_provider<T> for integral arithmetic types.
+ // See table in FormatProviders.h.
+ llvm::Regex int_format{"x[-+]?\\d*|n|d", llvm::Regex::IgnoreCase};
+
+ auto type_info = valobj.GetTypeInfo();
+ if (type_info & eTypeIsInteger && int_format.match(options)) {
if (type_info & eTypeIsSigned) {
bool success = false;
- auto integer = target.GetValueAsSigned(0, &success);
+ auto integer = valobj.GetValueAsSigned(0, &success);
if (success)
formatted = llvm::formatv(llvm_format.data(), integer);
} else {
bool success = false;
- auto integer = target.GetValueAsUnsigned(0, &success);
+ auto integer = valobj.GetValueAsUnsigned(0, &success);
if (success)
formatted = llvm::formatv(llvm_format.data(), integer);
}
diff --git a/lldb/test/API/functionalities/data-formatter/custom-printf-summary/TestCustomPrintfSummary.py b/lldb/test/API/functionalities/data-formatter/custom-printf-summary/TestCustomPrintfSummary.py
index caf072cebdf303..8177da29eb54f8 100644
--- a/lldb/test/API/functionalities/data-formatter/custom-printf-summary/TestCustomPrintfSummary.py
+++ b/lldb/test/API/functionalities/data-formatter/custom-printf-summary/TestCustomPrintfSummary.py
@@ -4,8 +4,15 @@
class TestCase(TestBase):
- def test(self):
+
+ def test_raw_bytes(self):
self.build()
lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.c"))
self.runCmd("type summary add -s '${var.ubyte:x-2}${var.sbyte:x-2}!' Bytes")
- self.expect("v bytes", substrs=[" = 1001!"])
+ self.expect("v bytes", substrs=[" = 3001!"])
+
+ def test_bad_format(self):
+ self.build()
+ lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.c"))
+ self.runCmd("type summary add -s '${var.ubyte:y}!' Bytes")
+ self.expect("v bytes", substrs=[" = '0'!"])
diff --git a/lldb/test/API/functionalities/data-formatter/custom-printf-summary/main.c b/lldb/test/API/functionalities/data-formatter/custom-printf-summary/main.c
index 8f92b9dafd32f3..4164aff7dbf6fa 100644
--- a/lldb/test/API/functionalities/data-formatter/custom-printf-summary/main.c
+++ b/lldb/test/API/functionalities/data-formatter/custom-printf-summary/main.c
@@ -7,7 +7,7 @@ struct Bytes {
};
int main() {
- struct Bytes bytes = {0x10, 0x01};
+ struct Bytes bytes = {0x30, 0x01};
(void)bytes;
printf("break here\n");
}
More information about the lldb-commits
mailing list