[Lldb-commits] [lldb] e861d05 - [lldb/Utility] Add GetDescription(Stream&) to StructureData::*

Med Ismail Bennani via lldb-commits lldb-commits at lists.llvm.org
Thu Nov 3 14:45:25 PDT 2022


Author: Med Ismail Bennani
Date: 2022-11-03T14:44:53-07:00
New Revision: e861d053dd43f2e5a63f150ee2f9d1d643ea29c1

URL: https://github.com/llvm/llvm-project/commit/e861d053dd43f2e5a63f150ee2f9d1d643ea29c1
DIFF: https://github.com/llvm/llvm-project/commit/e861d053dd43f2e5a63f150ee2f9d1d643ea29c1.diff

LOG: [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 <medismail.bennani at gmail.com>

Added: 
    lldb/unittests/Utility/Inputs/StructuredData-full.json
    lldb/unittests/Utility/Inputs/StructuredData-nested.json

Modified: 
    lldb/include/lldb/Core/StructuredDataImpl.h
    lldb/include/lldb/Utility/StructuredData.h
    lldb/source/Commands/CommandObjectProcess.cpp
    lldb/source/Utility/StructuredData.cpp
    lldb/test/API/functionalities/process_crash_info/TestProcessCrashInfo.py
    lldb/unittests/Utility/CMakeLists.txt
    lldb/unittests/Utility/StructuredDataTest.cpp

Removed: 
    


################################################################################
diff  --git a/lldb/include/lldb/Core/StructuredDataImpl.h b/lldb/include/lldb/Core/StructuredDataImpl.h
index e755c53aaa9f6..16dbc5263b285 100644
--- a/lldb/include/lldb/Core/StructuredDataImpl.h
+++ b/lldb/include/lldb/Core/StructuredDataImpl.h
@@ -80,7 +80,7 @@ class StructuredDataImpl {
         error.SetErrorString("No data to describe.");
         return error;
       }
-      m_data_sp->Dump(stream, true);
+      m_data_sp->GetDescription(stream);
       return error;
     }
     // Get the data's description.

diff  --git a/lldb/include/lldb/Utility/StructuredData.h b/lldb/include/lldb/Utility/StructuredData.h
index 9f6300f4f115b..5420c0dcf8d5a 100644
--- a/lldb/include/lldb/Utility/StructuredData.h
+++ b/lldb/include/lldb/Utility/StructuredData.h
@@ -158,6 +158,12 @@ class StructuredData {
       Serialize(jso);
     }
 
+    virtual void GetDescription(lldb_private::Stream &s) const {
+      s.IndentMore();
+      Dump(s, false);
+      s.IndentLess();
+    }
+
   private:
     lldb::StructuredDataType m_type;
   };
@@ -277,6 +283,8 @@ class StructuredData {
 
     void Serialize(llvm::json::OStream &s) const override;
 
+    void GetDescription(lldb_private::Stream &s) const override;
+
   protected:
     typedef std::vector<ObjectSP> collection;
     collection m_items;
@@ -295,6 +303,8 @@ class StructuredData {
 
     void Serialize(llvm::json::OStream &s) const override;
 
+    void GetDescription(lldb_private::Stream &s) const override;
+
   protected:
     uint64_t m_value;
   };
@@ -312,6 +322,8 @@ class StructuredData {
 
     void Serialize(llvm::json::OStream &s) const override;
 
+    void GetDescription(lldb_private::Stream &s) const override;
+
   protected:
     double m_value;
   };
@@ -329,6 +341,8 @@ class StructuredData {
 
     void Serialize(llvm::json::OStream &s) const override;
 
+    void GetDescription(lldb_private::Stream &s) const override;
+
   protected:
     bool m_value;
   };
@@ -345,6 +359,8 @@ class StructuredData {
 
     void Serialize(llvm::json::OStream &s) const override;
 
+    void GetDescription(lldb_private::Stream &s) const override;
+
   protected:
     std::string m_value;
   };
@@ -524,6 +540,8 @@ class StructuredData {
 
     void Serialize(llvm::json::OStream &s) const override;
 
+    void GetDescription(lldb_private::Stream &s) const override;
+
   protected:
     typedef std::map<ConstString, ObjectSP> collection;
     collection m_dict;
@@ -538,6 +556,8 @@ class StructuredData {
     bool IsValid() const override { return false; }
 
     void Serialize(llvm::json::OStream &s) const override;
+
+    void GetDescription(lldb_private::Stream &s) const override;
   };
 
   class Generic : public Object {
@@ -553,12 +573,15 @@ class StructuredData {
 
     void Serialize(llvm::json::OStream &s) const override;
 
+    void GetDescription(lldb_private::Stream &s) const override;
+
   private:
     void *m_object;
   };
 
   static ObjectSP ParseJSON(const std::string &json_text);
   static ObjectSP ParseJSONFromFile(const FileSpec &file, Status &error);
+  static bool IsRecordType(const ObjectSP object_sp);
 };
 
 } // namespace lldb_private

diff  --git a/lldb/source/Commands/CommandObjectProcess.cpp b/lldb/source/Commands/CommandObjectProcess.cpp
index 28a99ea3d94a5..92544c564e532 100644
--- a/lldb/source/Commands/CommandObjectProcess.cpp
+++ b/lldb/source/Commands/CommandObjectProcess.cpp
@@ -1537,8 +1537,9 @@ class CommandObjectProcessStatus : public CommandObjectParsed {
       StructuredData::DictionarySP crash_info_sp = *expected_crash_info;
 
       if (crash_info_sp) {
+        strm.EOL();
         strm.PutCString("Extended Crash Information:\n");
-        crash_info_sp->Dump(strm);
+        crash_info_sp->GetDescription(strm);
       }
     }
 

diff  --git a/lldb/source/Utility/StructuredData.cpp b/lldb/source/Utility/StructuredData.cpp
index 2e023344f3ddb..acc09289e6b98 100644
--- a/lldb/source/Utility/StructuredData.cpp
+++ b/lldb/source/Utility/StructuredData.cpp
@@ -50,6 +50,11 @@ StructuredData::ParseJSONFromFile(const FileSpec &input_spec, Status &error) {
   return StructuredData::ObjectSP();
 }
 
+bool StructuredData::IsRecordType(const ObjectSP object_sp) {
+  return object_sp->GetType() == lldb::eStructuredDataTypeArray ||
+         object_sp->GetType() == lldb::eStructuredDataTypeDictionary;
+}
+
 static StructuredData::ObjectSP ParseJSONValue(json::Value &value) {
   if (json::Object *O = value.getAsObject())
     return ParseJSONObject(O);
@@ -175,3 +180,98 @@ void StructuredData::Null::Serialize(json::OStream &s) const {
 void StructuredData::Generic::Serialize(json::OStream &s) const {
   s.value(llvm::formatv("{0:X}", m_object));
 }
+
+void StructuredData::Integer::GetDescription(lldb_private::Stream &s) const {
+  s.Printf("%" PRId64, static_cast<int64_t>(m_value));
+}
+
+void StructuredData::Float::GetDescription(lldb_private::Stream &s) const {
+  s.Printf("%f", m_value);
+}
+
+void StructuredData::Boolean::GetDescription(lldb_private::Stream &s) const {
+  s.Printf(m_value ? "True" : "False");
+}
+
+void StructuredData::String::GetDescription(lldb_private::Stream &s) const {
+  s.Printf("%s", m_value.empty() ? "\"\"" : m_value.c_str());
+}
+
+void StructuredData::Array::GetDescription(lldb_private::Stream &s) const {
+  size_t index = 0;
+  size_t indentation_level = s.GetIndentLevel();
+  for (const auto &item_sp : m_items) {
+    // Sanitize.
+    if (!item_sp)
+      continue;
+
+    // Reset original indentation level.
+    s.SetIndentLevel(indentation_level);
+    s.Indent();
+
+    // Print key
+    s.Printf("[%zu]:", index++);
+
+    // Return to new line and increase indentation if value is record type.
+    // Otherwise add spacing.
+    bool should_indent = IsRecordType(item_sp);
+    if (should_indent) {
+      s.EOL();
+      s.IndentMore();
+    } else {
+      s.PutChar(' ');
+    }
+
+    // Print value and new line if now last pair.
+    item_sp->GetDescription(s);
+    if (item_sp != *(--m_items.end()))
+      s.EOL();
+
+    // Reset indentation level if it was incremented previously.
+    if (should_indent)
+      s.IndentLess();
+  }
+}
+
+void StructuredData::Dictionary::GetDescription(lldb_private::Stream &s) const {
+  size_t indentation_level = s.GetIndentLevel();
+  for (const auto &pair : m_dict) {
+    // Sanitize.
+    if (pair.first.IsNull() || pair.first.IsEmpty() || !pair.second)
+      continue;
+
+    // Reset original indentation level.
+    s.SetIndentLevel(indentation_level);
+    s.Indent();
+
+    // Print key.
+    s.Printf("%s:", pair.first.AsCString());
+
+    // Return to new line and increase indentation if value is record type.
+    // Otherwise add spacing.
+    bool should_indent = IsRecordType(pair.second);
+    if (should_indent) {
+      s.EOL();
+      s.IndentMore();
+    } else {
+      s.PutChar(' ');
+    }
+
+    // Print value and new line if now last pair.
+    pair.second->GetDescription(s);
+    if (pair != *(--m_dict.end()))
+      s.EOL();
+
+    // Reset indentation level if it was incremented previously.
+    if (should_indent)
+      s.IndentLess();
+  }
+}
+
+void StructuredData::Null::GetDescription(lldb_private::Stream &s) const {
+  s.Printf("NULL");
+}
+
+void StructuredData::Generic::GetDescription(lldb_private::Stream &s) const {
+  s.Printf("%p", m_object);
+}

diff  --git a/lldb/test/API/functionalities/process_crash_info/TestProcessCrashInfo.py b/lldb/test/API/functionalities/process_crash_info/TestProcessCrashInfo.py
index 30190e7c4df9b..659539c28a795 100644
--- a/lldb/test/API/functionalities/process_crash_info/TestProcessCrashInfo.py
+++ b/lldb/test/API/functionalities/process_crash_info/TestProcessCrashInfo.py
@@ -37,7 +37,9 @@ def test_cli(self):
                     patterns=["Process .* launched: .*a.out"])
 
         self.expect('process status --verbose',
-                    patterns=["\"message\".*pointer being freed was not allocated"])
+                    patterns=["Extended Crash Information",
+                              "crash-info annotations",
+                              "pointer being freed was not allocated"])
 
 
     @skipIfAsan # The test process intentionally hits a memory bug.

diff  --git a/lldb/unittests/Utility/CMakeLists.txt b/lldb/unittests/Utility/CMakeLists.txt
index d697464600ff5..848a36215aa67 100644
--- a/lldb/unittests/Utility/CMakeLists.txt
+++ b/lldb/unittests/Utility/CMakeLists.txt
@@ -54,6 +54,10 @@ add_lldb_unittest(UtilityTests
     Support
   )
 
-add_unittest_inputs(UtilityTests
+set(test_inputs
   StructuredData-basic.json
+  StructuredData-nested.json
+  StructuredData-full.json
   )
+
+add_unittest_inputs(UtilityTests "${test_inputs}")

diff  --git a/lldb/unittests/Utility/Inputs/StructuredData-full.json b/lldb/unittests/Utility/Inputs/StructuredData-full.json
new file mode 100644
index 0000000000000..4e4945cd6a280
--- /dev/null
+++ b/lldb/unittests/Utility/Inputs/StructuredData-full.json
@@ -0,0 +1,15 @@
+{
+  "Array": [
+    3.14,
+    {
+      "key": "val"
+    }
+  ],
+  "Dictionary": {
+    "FalseBool": false
+  },
+  "Integer": 1,
+  "Null": null,
+  "String": "value",
+  "TrueBool": true
+}

diff  --git a/lldb/unittests/Utility/Inputs/StructuredData-nested.json b/lldb/unittests/Utility/Inputs/StructuredData-nested.json
new file mode 100644
index 0000000000000..facf461bb6c1f
--- /dev/null
+++ b/lldb/unittests/Utility/Inputs/StructuredData-nested.json
@@ -0,0 +1,14 @@
+{
+  "my_dict": [
+    {
+      "three": 3,
+      "two": 2
+    },
+    {
+      "four": {
+        "val": 4
+      }
+    },
+    1
+  ]
+}

diff  --git a/lldb/unittests/Utility/StructuredDataTest.cpp b/lldb/unittests/Utility/StructuredDataTest.cpp
index cb5e418cd958e..e732016fe43db 100644
--- a/lldb/unittests/Utility/StructuredDataTest.cpp
+++ b/lldb/unittests/Utility/StructuredDataTest.cpp
@@ -31,6 +31,73 @@ TEST(StructuredDataTest, StringDump) {
   }
 }
 
+TEST(StructuredDataTest, GetDescriptionEmpty) {
+  Status status;
+  auto object_sp = StructuredData::ParseJSON("{}");
+  ASSERT_NE(nullptr, object_sp);
+
+  StreamString S;
+  object_sp->GetDescription(S);
+  EXPECT_EQ(0, S.GetSize());
+}
+
+TEST(StructuredDataTest, GetDescriptionBasic) {
+  Status status;
+  std::string input = GetInputFilePath("StructuredData-basic.json");
+  auto object_sp = StructuredData::ParseJSONFromFile(FileSpec(input), status);
+  ASSERT_NE(nullptr, object_sp);
+
+  const std::string expected = "[0]: 1\n"
+                               "[1]: 2\n"
+                               "[2]: 3";
+
+  StreamString S;
+  object_sp->GetDescription(S);
+  EXPECT_EQ(expected, S.GetString());
+}
+
+TEST(StructuredDataTest, GetDescriptionNested) {
+  Status status;
+  std::string input = GetInputFilePath("StructuredData-nested.json");
+  auto object_sp = StructuredData::ParseJSONFromFile(FileSpec(input), status);
+  ASSERT_NE(nullptr, object_sp);
+
+  const std::string expected = "my_dict:\n"
+                               "  [0]:\n"
+                               "    three: 3\n"
+                               "    two: 2\n"
+                               "  [1]:\n"
+                               "    four:\n"
+                               "      val: 4\n"
+                               "  [2]: 1";
+
+  StreamString S;
+  object_sp->GetDescription(S);
+  EXPECT_EQ(expected, S.GetString());
+}
+
+TEST(StructuredDataTest, GetDescriptionFull) {
+  Status status;
+  std::string input = GetInputFilePath("StructuredData-full.json");
+  auto object_sp = StructuredData::ParseJSONFromFile(FileSpec(input), status);
+  ASSERT_NE(nullptr, object_sp);
+
+  const std::string expected = "Array:\n"
+                               "  [0]: 3.140000\n"
+                               "  [1]:\n"
+                               "    key: val\n"
+                               "Dictionary:\n"
+                               "  FalseBool: False\n"
+                               "Integer: 1\n"
+                               "Null: NULL\n"
+                               "String: value\n"
+                               "TrueBool: True";
+
+  StreamString S;
+  object_sp->GetDescription(S);
+  EXPECT_EQ(expected, S.GetString());
+}
+
 TEST(StructuredDataTest, ParseJSONFromFile) {
   Status status;
   auto object_sp = StructuredData::ParseJSONFromFile(


        


More information about the lldb-commits mailing list