[Lldb-commits] [lldb] Summarize std::sting's when created from data. (PR #89110)
Jacob Lalonde via lldb-commits
lldb-commits at lists.llvm.org
Wed Apr 17 10:40:43 PDT 2024
https://github.com/Jlalond created https://github.com/llvm/llvm-project/pull/89110
std::string children are normally formatted as their summary [my_string_prop] = "hello world". Sbvalues/ValueObjects created from data do not follow the same summary formatter path and instead hit the value object printer. This change fixes that and adds tests for the future.
Added tests in TestDataFormatterStdString.py and a formatter for a custom struct.
>From e0316188d22605c670079e37855d3d8b5c944cee Mon Sep 17 00:00:00 2001
From: Jacob John Lalonde <jalalonde at fb.com>
Date: Wed, 10 Apr 2024 14:33:40 -0700
Subject: [PATCH] Fix bug where an sbvalue containing a std::string created
from data would not use the built in summary provider, but default to the
valueobjectprinter
std::string children are normally formatted as their summary [my_string_prop] = "hello world".
Sbvalues created from data do not follow the same summary formatter path and instead hit the value object printer.
This change fixes that and adds tests for the future.
Added tests in TestDataFormatterStdString.py and a formatter for a custom struct.
---
.../Plugins/Language/CPlusPlus/LibStdcpp.cpp | 31 ++++++++++--
.../string/ConvertToDataFormatter.py | 50 +++++++++++++++++++
.../string/TestDataFormatterStdString.py | 28 +++++++++++
.../libstdcpp/string/main.cpp | 13 +++++
4 files changed, 118 insertions(+), 4 deletions(-)
create mode 100644 lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/string/ConvertToDataFormatter.py
diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp
index 86bb575af5ca34..0da01e9ca07fb6 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp
@@ -254,13 +254,13 @@ bool lldb_private::formatters::LibStdcppStringSummaryProvider(
} else
addr_of_string =
valobj.GetAddressOf(scalar_is_load_addr, &addr_type);
- if (addr_of_string != LLDB_INVALID_ADDRESS) {
+ if (addr_of_string != LLDB_INVALID_ADDRESS ||
+ addr_type == eAddressTypeHost) {
switch (addr_type) {
case eAddressTypeLoad: {
ProcessSP process_sp(valobj.GetProcessSP());
if (!process_sp)
return false;
-
StringPrinter::ReadStringAndDumpToStreamOptions options(valobj);
Status error;
lldb::addr_t addr_of_data =
@@ -287,8 +287,31 @@ bool lldb_private::formatters::LibStdcppStringSummaryProvider(
} else
return true;
} break;
- case eAddressTypeHost:
- break;
+ case eAddressTypeHost: {
+ // We have the host address of our std::string
+ // But we need to read the pointee data from the debugged process.
+ ProcessSP process_sp(valobj.GetProcessSP());
+ DataExtractor data;
+ Status error;
+ valobj.GetData(data, error);
+ if (error.Fail())
+ return false;
+ // We want to read the address from std::string, which is the first 8 bytes.
+ lldb::offset_t offset = 0;
+ lldb::addr_t addr = data.GetAddress(&offset);
+ if (!addr)
+ {
+ stream.Printf("nullptr");
+ return true;
+ }
+
+ std::string contents;
+ process_sp->ReadCStringFromMemory(addr, contents, error);
+ if (error.Fail())
+ return false;
+ stream.Printf("%s", contents.c_str());
+ return true;
+ } break;
case eAddressTypeInvalid:
case eAddressTypeFile:
break;
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/string/ConvertToDataFormatter.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/string/ConvertToDataFormatter.py
new file mode 100644
index 00000000000000..57e42c920f8294
--- /dev/null
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/string/ConvertToDataFormatter.py
@@ -0,0 +1,50 @@
+# coding=utf8
+"""
+Helper formmater to verify Std::String by created via SBData
+"""
+
+import lldb
+
+class SyntheticFormatter:
+ def __init__(self, valobj, dict):
+ self.valobj = valobj
+
+ def num_children(self):
+ return 6
+
+ def has_children(self):
+ return True
+
+ def get_child_at_index(self, index):
+ name = None
+ match index:
+ case 0:
+ name = "short_string"
+ case 1:
+ name = "long_string"
+ case 2:
+ name = "short_string_ptr"
+ case 3:
+ name = "long_string_ptr"
+ case 4:
+ name = "short_string_ref"
+ case 5:
+ name = "long_string_ref"
+ case _:
+ return None
+
+ child = self.valobj.GetChildMemberWithName(name)
+ valType = child.GetType()
+ return self.valobj.CreateValueFromData(name,
+ child.GetData(),
+ valType)
+
+
+def __lldb_init_module(debugger, dict):
+ typeName = "string_container"
+ debugger.HandleCommand(
+ 'type synthetic add -x "'
+ + typeName
+ + '" --python-class '
+ + f"{__name__}.SyntheticFormatter"
+ )
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/string/TestDataFormatterStdString.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/string/TestDataFormatterStdString.py
index 0b78fb7dc3911c..2adfe47f9a3cfd 100644
--- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/string/TestDataFormatterStdString.py
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/string/TestDataFormatterStdString.py
@@ -37,6 +37,8 @@ def test_with_run_command(self):
substrs=["stopped", "stop reason = breakpoint"],
)
+ self.runCmd("command script import ./ConvertToDataFormatter.py", check=True)
+
# This is the function to remove the custom formats in order to have a
# clean slate for the next test case.
def cleanup():
@@ -60,6 +62,7 @@ def cleanup():
var_rQ = self.frame().FindVariable("rQ")
var_pq = self.frame().FindVariable("pq")
var_pQ = self.frame().FindVariable("pQ")
+ var_str_container = self.frame().FindVariable("sc")
self.assertEqual(var_wempty.GetSummary(), 'L""', "wempty summary wrong")
self.assertEqual(
@@ -89,6 +92,31 @@ def cleanup():
"pQ summary wrong",
)
+ self.assertEqual(
+ var_str_container.GetChildAtIndex(0).GetSummary(),
+ '"u22"',
+ "string container child wrong")
+ self.assertEqual(
+ var_str_container.GetChildAtIndex(1).GetSummary(),
+ '"quite a long std::string with lots of info inside it inside a struct"',
+ "string container child wrong")
+ self.assertEqual(
+ var_str_container.GetChildAtIndex(2).GetSummary(),
+ '"u22"',
+ "string container child wrong")
+ self.assertEqual(
+ var_str_container.GetChildAtIndex(3).GetSummary(),
+ '"quite a long std::string with lots of info inside it inside a struct"',
+ "string container child wrong")
+ self.assertEqual(
+ var_str_container.GetChildAtIndex(4).GetSummary(),
+ '"u22"',
+ "string container child wrong")
+ self.assertEqual(
+ var_str_container.GetChildAtIndex(5).GetSummary(),
+ '"quite a long std::string with lots of info inside it inside a struct"',
+ "string container child wrong")
+
self.runCmd("next")
self.assertEqual(var_S.GetSummary(), 'L"!!!!!"', "new S summary wrong")
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/string/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/string/main.cpp
index eefb96c4573e4e..b169a2e6945568 100644
--- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/string/main.cpp
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/string/main.cpp
@@ -1,5 +1,14 @@
#include <string>
+struct string_container {
+ std::string short_string;
+ std::string long_string;
+ std::string *short_string_ptr = &short_string;
+ std::string *long_string_ptr = &long_string;
+ std::string &short_string_ref = short_string;
+ std::string &long_string_ref = long_string;
+};
+
int main()
{
std::wstring wempty(L"");
@@ -11,6 +20,10 @@ int main()
std::string Q("quite a long std::strin with lots of info inside it");
auto &rq = q, &rQ = Q;
std::string *pq = &q, *pQ = &Q;
+ string_container sc;
+ sc.short_string = "u22";
+ sc.long_string = "quite a long std::string with lots of info inside it inside a struct";
+
S.assign(L"!!!!!"); // Set break point at this line.
return 0;
}
More information about the lldb-commits
mailing list