Skip to content

Commit bd6e0ce

Browse files
committed
[lldb/Utility] Add GetDescription(Stream&) to StructureData::*
This patch improves the StructuredData classes to provide a GetDescription(lldb_private::Stream&) affordance. This is very convenient compared to the Dump method because this try to pretty print the structure instead of just serializing it into a JSON. This patch also updates some parts of lldb (i.e. extended crash info) to use this new affordance instead of StructuredData::Dump. Differential Revision: https://reviews.llvm.org/D135547 Signed-off-by: Med Ismail Bennani <[email protected]>
1 parent 37e496c commit bd6e0ce

File tree

9 files changed

+230
-4
lines changed

9 files changed

+230
-4
lines changed

lldb/include/lldb/Core/StructuredDataImpl.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ class StructuredDataImpl {
8080
error.SetErrorString("No data to describe.");
8181
return error;
8282
}
83-
m_data_sp->Dump(stream, true);
83+
m_data_sp->GetDescription(stream);
8484
return error;
8585
}
8686
// Get the data's description.

lldb/include/lldb/Utility/StructuredData.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,12 @@ class StructuredData {
158158
Serialize(jso);
159159
}
160160

161+
virtual void GetDescription(lldb_private::Stream &s) const {
162+
s.IndentMore();
163+
Dump(s, false);
164+
s.IndentLess();
165+
}
166+
161167
private:
162168
lldb::StructuredDataType m_type;
163169
};
@@ -277,6 +283,8 @@ class StructuredData {
277283

278284
void Serialize(llvm::json::OStream &s) const override;
279285

286+
void GetDescription(lldb_private::Stream &s) const override;
287+
280288
protected:
281289
typedef std::vector<ObjectSP> collection;
282290
collection m_items;
@@ -295,6 +303,8 @@ class StructuredData {
295303

296304
void Serialize(llvm::json::OStream &s) const override;
297305

306+
void GetDescription(lldb_private::Stream &s) const override;
307+
298308
protected:
299309
uint64_t m_value;
300310
};
@@ -312,6 +322,8 @@ class StructuredData {
312322

313323
void Serialize(llvm::json::OStream &s) const override;
314324

325+
void GetDescription(lldb_private::Stream &s) const override;
326+
315327
protected:
316328
double m_value;
317329
};
@@ -329,6 +341,8 @@ class StructuredData {
329341

330342
void Serialize(llvm::json::OStream &s) const override;
331343

344+
void GetDescription(lldb_private::Stream &s) const override;
345+
332346
protected:
333347
bool m_value;
334348
};
@@ -345,6 +359,8 @@ class StructuredData {
345359

346360
void Serialize(llvm::json::OStream &s) const override;
347361

362+
void GetDescription(lldb_private::Stream &s) const override;
363+
348364
protected:
349365
std::string m_value;
350366
};
@@ -524,6 +540,8 @@ class StructuredData {
524540

525541
void Serialize(llvm::json::OStream &s) const override;
526542

543+
void GetDescription(lldb_private::Stream &s) const override;
544+
527545
protected:
528546
typedef std::map<ConstString, ObjectSP> collection;
529547
collection m_dict;
@@ -538,6 +556,8 @@ class StructuredData {
538556
bool IsValid() const override { return false; }
539557

540558
void Serialize(llvm::json::OStream &s) const override;
559+
560+
void GetDescription(lldb_private::Stream &s) const override;
541561
};
542562

543563
class Generic : public Object {
@@ -553,12 +573,15 @@ class StructuredData {
553573

554574
void Serialize(llvm::json::OStream &s) const override;
555575

576+
void GetDescription(lldb_private::Stream &s) const override;
577+
556578
private:
557579
void *m_object;
558580
};
559581

560582
static ObjectSP ParseJSON(const std::string &json_text);
561583
static ObjectSP ParseJSONFromFile(const FileSpec &file, Status &error);
584+
static bool IsRecordType(const ObjectSP object_sp);
562585
};
563586

564587
} // namespace lldb_private

lldb/source/Commands/CommandObjectProcess.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1550,8 +1550,9 @@ class CommandObjectProcessStatus : public CommandObjectParsed {
15501550
StructuredData::DictionarySP crash_info_sp = *expected_crash_info;
15511551

15521552
if (crash_info_sp) {
1553+
strm.EOL();
15531554
strm.PutCString("Extended Crash Information:\n");
1554-
crash_info_sp->Dump(strm);
1555+
crash_info_sp->GetDescription(strm);
15551556
}
15561557
}
15571558

lldb/source/Utility/StructuredData.cpp

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ StructuredData::ParseJSONFromFile(const FileSpec &input_spec, Status &error) {
5050
return StructuredData::ObjectSP();
5151
}
5252

53+
bool StructuredData::IsRecordType(const ObjectSP object_sp) {
54+
return object_sp->GetType() == lldb::eStructuredDataTypeArray ||
55+
object_sp->GetType() == lldb::eStructuredDataTypeDictionary;
56+
}
57+
5358
static StructuredData::ObjectSP ParseJSONValue(json::Value &value) {
5459
if (json::Object *O = value.getAsObject())
5560
return ParseJSONObject(O);
@@ -175,3 +180,98 @@ void StructuredData::Null::Serialize(json::OStream &s) const {
175180
void StructuredData::Generic::Serialize(json::OStream &s) const {
176181
s.value(llvm::formatv("{0:X}", m_object));
177182
}
183+
184+
void StructuredData::Integer::GetDescription(lldb_private::Stream &s) const {
185+
s.Printf("%" PRId64, static_cast<int64_t>(m_value));
186+
}
187+
188+
void StructuredData::Float::GetDescription(lldb_private::Stream &s) const {
189+
s.Printf("%f", m_value);
190+
}
191+
192+
void StructuredData::Boolean::GetDescription(lldb_private::Stream &s) const {
193+
s.Printf(m_value ? "True" : "False");
194+
}
195+
196+
void StructuredData::String::GetDescription(lldb_private::Stream &s) const {
197+
s.Printf("%s", m_value.empty() ? "\"\"" : m_value.c_str());
198+
}
199+
200+
void StructuredData::Array::GetDescription(lldb_private::Stream &s) const {
201+
size_t index = 0;
202+
size_t indentation_level = s.GetIndentLevel();
203+
for (const auto &item_sp : m_items) {
204+
// Sanitize.
205+
if (!item_sp)
206+
continue;
207+
208+
// Reset original indentation level.
209+
s.SetIndentLevel(indentation_level);
210+
s.Indent();
211+
212+
// Print key
213+
s.Printf("[%zu]:", index++);
214+
215+
// Return to new line and increase indentation if value is record type.
216+
// Otherwise add spacing.
217+
bool should_indent = IsRecordType(item_sp);
218+
if (should_indent) {
219+
s.EOL();
220+
s.IndentMore();
221+
} else {
222+
s.PutChar(' ');
223+
}
224+
225+
// Print value and new line if now last pair.
226+
item_sp->GetDescription(s);
227+
if (item_sp != *(--m_items.end()))
228+
s.EOL();
229+
230+
// Reset indentation level if it was incremented previously.
231+
if (should_indent)
232+
s.IndentLess();
233+
}
234+
}
235+
236+
void StructuredData::Dictionary::GetDescription(lldb_private::Stream &s) const {
237+
size_t indentation_level = s.GetIndentLevel();
238+
for (const auto &pair : m_dict) {
239+
// Sanitize.
240+
if (pair.first.IsNull() || pair.first.IsEmpty() || !pair.second)
241+
continue;
242+
243+
// Reset original indentation level.
244+
s.SetIndentLevel(indentation_level);
245+
s.Indent();
246+
247+
// Print key.
248+
s.Printf("%s:", pair.first.AsCString());
249+
250+
// Return to new line and increase indentation if value is record type.
251+
// Otherwise add spacing.
252+
bool should_indent = IsRecordType(pair.second);
253+
if (should_indent) {
254+
s.EOL();
255+
s.IndentMore();
256+
} else {
257+
s.PutChar(' ');
258+
}
259+
260+
// Print value and new line if now last pair.
261+
pair.second->GetDescription(s);
262+
if (pair != *(--m_dict.end()))
263+
s.EOL();
264+
265+
// Reset indentation level if it was incremented previously.
266+
if (should_indent)
267+
s.IndentLess();
268+
}
269+
}
270+
271+
void StructuredData::Null::GetDescription(lldb_private::Stream &s) const {
272+
s.Printf("NULL");
273+
}
274+
275+
void StructuredData::Generic::GetDescription(lldb_private::Stream &s) const {
276+
s.Printf("%p", m_object);
277+
}

lldb/test/API/functionalities/process_crash_info/TestProcessCrashInfo.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@ def test_cli(self):
3737
patterns=["Process .* launched: .*a.out"])
3838

3939
self.expect('process status --verbose',
40-
patterns=["\"message\".*pointer being freed was not allocated"])
40+
patterns=["Extended Crash Information",
41+
"crash-info annotations",
42+
"pointer being freed was not allocated"])
4143

4244

4345
@skipIfAsan # The test process intentionally hits a memory bug.

lldb/unittests/Utility/CMakeLists.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ add_lldb_unittest(UtilityTests
5555
Support
5656
)
5757

58-
add_unittest_inputs(UtilityTests
58+
set(test_inputs
5959
StructuredData-basic.json
60+
StructuredData-nested.json
61+
StructuredData-full.json
6062
)
63+
64+
add_unittest_inputs(UtilityTests "${test_inputs}")
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"Array": [
3+
3.14,
4+
{
5+
"key": "val"
6+
}
7+
],
8+
"Dictionary": {
9+
"FalseBool": false
10+
},
11+
"Integer": 1,
12+
"Null": null,
13+
"String": "value",
14+
"TrueBool": true
15+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"my_dict": [
3+
{
4+
"three": 3,
5+
"two": 2
6+
},
7+
{
8+
"four": {
9+
"val": 4
10+
}
11+
},
12+
1
13+
]
14+
}

lldb/unittests/Utility/StructuredDataTest.cpp

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,73 @@ TEST(StructuredDataTest, StringDump) {
3131
}
3232
}
3333

34+
TEST(StructuredDataTest, GetDescriptionEmpty) {
35+
Status status;
36+
auto object_sp = StructuredData::ParseJSON("{}");
37+
ASSERT_NE(nullptr, object_sp);
38+
39+
StreamString S;
40+
object_sp->GetDescription(S);
41+
EXPECT_EQ(0, S.GetSize());
42+
}
43+
44+
TEST(StructuredDataTest, GetDescriptionBasic) {
45+
Status status;
46+
std::string input = GetInputFilePath("StructuredData-basic.json");
47+
auto object_sp = StructuredData::ParseJSONFromFile(FileSpec(input), status);
48+
ASSERT_NE(nullptr, object_sp);
49+
50+
const std::string expected = "[0]: 1\n"
51+
"[1]: 2\n"
52+
"[2]: 3";
53+
54+
StreamString S;
55+
object_sp->GetDescription(S);
56+
EXPECT_EQ(expected, S.GetString());
57+
}
58+
59+
TEST(StructuredDataTest, GetDescriptionNested) {
60+
Status status;
61+
std::string input = GetInputFilePath("StructuredData-nested.json");
62+
auto object_sp = StructuredData::ParseJSONFromFile(FileSpec(input), status);
63+
ASSERT_NE(nullptr, object_sp);
64+
65+
const std::string expected = "my_dict:\n"
66+
" [0]:\n"
67+
" three: 3\n"
68+
" two: 2\n"
69+
" [1]:\n"
70+
" four:\n"
71+
" val: 4\n"
72+
" [2]: 1";
73+
74+
StreamString S;
75+
object_sp->GetDescription(S);
76+
EXPECT_EQ(expected, S.GetString());
77+
}
78+
79+
TEST(StructuredDataTest, GetDescriptionFull) {
80+
Status status;
81+
std::string input = GetInputFilePath("StructuredData-full.json");
82+
auto object_sp = StructuredData::ParseJSONFromFile(FileSpec(input), status);
83+
ASSERT_NE(nullptr, object_sp);
84+
85+
const std::string expected = "Array:\n"
86+
" [0]: 3.140000\n"
87+
" [1]:\n"
88+
" key: val\n"
89+
"Dictionary:\n"
90+
" FalseBool: False\n"
91+
"Integer: 1\n"
92+
"Null: NULL\n"
93+
"String: value\n"
94+
"TrueBool: True";
95+
96+
StreamString S;
97+
object_sp->GetDescription(S);
98+
EXPECT_EQ(expected, S.GetString());
99+
}
100+
34101
TEST(StructuredDataTest, ParseJSONFromFile) {
35102
Status status;
36103
auto object_sp = StructuredData::ParseJSONFromFile(

0 commit comments

Comments
 (0)