https://github.com/royitaqi updated https://github.com/llvm/llvm-project/pull/92843
>From 8499f16ad46b3268f35da2bfcbfa02a10aab935a Mon Sep 17 00:00:00 2001 From: Roy Shi <roy...@meta.com> Date: Mon, 20 May 2024 22:30:40 -0400 Subject: [PATCH 1/4] Add resolvedCommand to transcript, add transcript to statistics dump --- lldb/include/lldb/API/SBCommandInterpreter.h | 3 +- .../lldb/Interpreter/CommandInterpreter.h | 5 +-- .../source/Interpreter/CommandInterpreter.cpp | 6 ++++ lldb/source/Target/Statistics.cpp | 31 +++++++++++++++++++ .../commands/statistics/basic/TestStats.py | 30 ++++++++++++++++++ .../interpreter/TestCommandInterpreterAPI.py | 20 ++++++++---- 6 files changed, 86 insertions(+), 9 deletions(-) diff --git a/lldb/include/lldb/API/SBCommandInterpreter.h b/lldb/include/lldb/API/SBCommandInterpreter.h index 8ac36344b3a79..8eb4a71cb7f88 100644 --- a/lldb/include/lldb/API/SBCommandInterpreter.h +++ b/lldb/include/lldb/API/SBCommandInterpreter.h @@ -320,7 +320,8 @@ class SBCommandInterpreter { /// Returns a list of handled commands, output and error. Each element in /// the list is a dictionary with the following keys/values: - /// - "command" (string): The command that was executed. + /// - "command" (string): The command that was given by the user. + /// - "resolvedCommand" (string): The expanded command that was executed. /// - "output" (string): The output of the command. Empty ("") if no output. /// - "error" (string): The error of the command. Empty ("") if no error. /// - "seconds" (float): The time it took to execute the command. diff --git a/lldb/include/lldb/Interpreter/CommandInterpreter.h b/lldb/include/lldb/Interpreter/CommandInterpreter.h index ccc30cf4f1a82..7f420daca450a 100644 --- a/lldb/include/lldb/Interpreter/CommandInterpreter.h +++ b/lldb/include/lldb/Interpreter/CommandInterpreter.h @@ -580,7 +580,7 @@ class CommandInterpreter : public Broadcaster, void SetEchoCommentCommands(bool enable); bool GetRepeatPreviousCommand() const; - + bool GetRequireCommandOverwrite() const; const CommandObject::CommandMap &GetUserCommands() const { @@ -776,7 +776,8 @@ class CommandInterpreter : public Broadcaster, /// Contains a list of handled commands and their details. Each element in /// the list is a dictionary with the following keys/values: - /// - "command" (string): The command that was executed. + /// - "command" (string): The command that was given by the user. + /// - "resolvedCommand" (string): The expanded command that was executed. /// - "output" (string): The output of the command. Empty ("") if no output. /// - "error" (string): The error of the command. Empty ("") if no error. /// - "seconds" (float): The time it took to execute the command. diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp index 811726e30af4d..04820bd7d39f6 100644 --- a/lldb/source/Interpreter/CommandInterpreter.cpp +++ b/lldb/source/Interpreter/CommandInterpreter.cpp @@ -2011,6 +2011,12 @@ bool CommandInterpreter::HandleCommand(const char *command_line, wants_raw_input ? "True" : "False"); } + // To test whether or not transcript should be saved, `transcript_item` is + // used instead of `GetSaveTrasncript()`. This is because the latter will + // fail when the command is "settings set interpreter.save-transcript true". + if (transcript_item) + transcript_item->AddStringItem("resolvedCommand", command_string); + // Phase 2. // Take care of things like setting up the history command & calling the // appropriate Execute method on the CommandObject, with the appropriate diff --git a/lldb/source/Target/Statistics.cpp b/lldb/source/Target/Statistics.cpp index 7f866ae0ef324..fd31377abd06b 100644 --- a/lldb/source/Target/Statistics.cpp +++ b/lldb/source/Target/Statistics.cpp @@ -16,6 +16,7 @@ #include "lldb/Target/Process.h" #include "lldb/Target/Target.h" #include "lldb/Target/UnixSignals.h" +#include "lldb/Utility/StructuredData.h" using namespace lldb; using namespace lldb_private; @@ -362,6 +363,36 @@ llvm::json::Value DebuggerStats::ReportStatistics( global_stats.try_emplace("modules", std::move(json_modules)); global_stats.try_emplace("memory", std::move(json_memory)); global_stats.try_emplace("commands", std::move(cmd_stats)); + + // When transcript is available, add it to the to-be-returned statistics. + // + // NOTE: + // When the statistics is polled by an LLDB command: + // - The transcript in the returned statistics *will NOT* contain the + // returned statistics itself. + // - The returned statistics *will* be written to the internal transcript + // buffer as the output of the said LLDB command. It *will* appear in + // the next statistcs or transcript poll. + // + // For example, let's say the following commands are run in order: + // - "version" + // - "statistics dump" <- call it "A" + // - "statistics dump" <- call it "B" + // The output of "A" will contain the transcript of "version" and + // "statistics dump" (A), with the latter having empty output. The output + // of B will contain the trascnript of "version", "statistics dump" (A), + // "statistics dump" (B), with A's output populated and B's output empty. + const StructuredData::Array &transcript = + debugger.GetCommandInterpreter().GetTranscript(); + if (transcript.GetSize() != 0) { + std::string buffer; + llvm::raw_string_ostream ss(buffer); + json::OStream json_os(ss); + transcript.Serialize(json_os); + if (auto json_transcript = llvm::json::parse(ss.str())) + global_stats.try_emplace("transcript", + std::move(json_transcript.get())); + } } return std::move(global_stats); diff --git a/lldb/test/API/commands/statistics/basic/TestStats.py b/lldb/test/API/commands/statistics/basic/TestStats.py index fb6fc07d2d443..a536ee2c9bbdc 100644 --- a/lldb/test/API/commands/statistics/basic/TestStats.py +++ b/lldb/test/API/commands/statistics/basic/TestStats.py @@ -623,3 +623,33 @@ def test_had_frame_variable_errors(self): # Verify that the top level statistic that aggregates the number of # modules with debugInfoHadVariableErrors is greater than zero self.assertGreater(stats["totalModuleCountWithVariableErrors"], 0) + +def test_transcript(self): + """ + Test "statistics dump" and the transcript information. + """ + self.build() + exe = self.getBuildArtifact("a.out") + target = self.createTestTarget(file_path=exe) + self.runCmd("settings set target.save-transcript true") + self.runCmd("version") + + # Verify the output of a first "statistics dump" + debug_stats = self.get_stats() + self.assertIn("transcript", debug_stats) + transcript = debug_stats["transcript"] + self.assertEqual(len(transcript), 2) + self.assertEqual(transcript[0]["command"], "version") + self.assertEqual(transcript[1]["command"], "statistics dump") + self.assertEqual(transcript[1]["output"], "") + + # Verify the output of a second "statistics dump" + debug_stats = self.get_stats() + self.assertIn("transcript", debug_stats) + transcript = debug_stats["transcript"] + self.assertEqual(len(transcript), 3) + self.assertEqual(transcript[0]["command"], "version") + self.assertEqual(transcript[1]["command"], "statistics dump") + self.assertNotEqual(transcript[1]["output"], "") + self.assertEqual(transcript[2]["command"], "statistics dump") + self.assertEqual(transcript[2]["output"], "") diff --git a/lldb/test/API/python_api/interpreter/TestCommandInterpreterAPI.py b/lldb/test/API/python_api/interpreter/TestCommandInterpreterAPI.py index 95643eef0d344..73eef27095b6d 100644 --- a/lldb/test/API/python_api/interpreter/TestCommandInterpreterAPI.py +++ b/lldb/test/API/python_api/interpreter/TestCommandInterpreterAPI.py @@ -104,7 +104,7 @@ def getTranscriptAsPythonObject(self, ci): return json.loads(stream.GetData()) - def test_structured_transcript(self): + def test_get_transcript(self): """Test structured transcript generation and retrieval.""" ci = self.buildAndCreateTarget() @@ -118,7 +118,7 @@ def test_structured_transcript(self): res = lldb.SBCommandReturnObject() ci.HandleCommand("version", res) ci.HandleCommand("an-unknown-command", res) - ci.HandleCommand("breakpoint set -f main.c -l %d" % self.line, res) + ci.HandleCommand("br set -f main.c -l %d" % self.line, res) ci.HandleCommand("r", res) ci.HandleCommand("p a", res) ci.HandleCommand("statistics dump", res) @@ -130,6 +130,7 @@ def test_structured_transcript(self): # All commands should have expected fields. for command in transcript: self.assertIn("command", command) + self.assertIn("resolvedCommand", command) self.assertIn("output", command) self.assertIn("error", command) self.assertIn("seconds", command) @@ -146,6 +147,7 @@ def test_structured_transcript(self): # (lldb) version self.assertEqual(transcript[0]["command"], "version") + self.assertEqual(transcript[0]["resolvedCommand"], "version") self.assertIn("lldb version", transcript[0]["output"]) self.assertEqual(transcript[0]["error"], "") @@ -153,18 +155,21 @@ def test_structured_transcript(self): self.assertEqual(transcript[1], { "command": "an-unknown-command", + "resolvedCommand": "an-unknown-command", "output": "", "error": "error: 'an-unknown-command' is not a valid command.\n", }) - # (lldb) breakpoint set -f main.c -l <line> - self.assertEqual(transcript[2]["command"], "breakpoint set -f main.c -l %d" % self.line) + # (lldb) br set -f main.c -l <line> + self.assertEqual(transcript[2]["command"], "br set -f main.c -l %d" % self.line) + self.assertEqual(transcript[2]["resolvedCommand"], "breakpoint set -f main.c -l %d" % self.line) # Breakpoint 1: where = a.out`main + 29 at main.c:5:3, address = 0x0000000100000f7d self.assertIn("Breakpoint 1: where = a.out`main ", transcript[2]["output"]) self.assertEqual(transcript[2]["error"], "") # (lldb) r self.assertEqual(transcript[3]["command"], "r") + self.assertEqual(transcript[3]["resolvedCommand"], "process launch -X true --") # Process 25494 launched: '<path>/TestCommandInterpreterAPI.test_structured_transcript/a.out' (x86_64) self.assertIn("Process", transcript[3]["output"]) self.assertIn("launched", transcript[3]["output"]) @@ -174,11 +179,15 @@ def test_structured_transcript(self): self.assertEqual(transcript[4], { "command": "p a", + "resolvedCommand": "dwim-print -- a", "output": "(int) 123\n", "error": "", }) # (lldb) statistics dump + self.assertEqual(transcript[5]["command"], "statistics dump") + self.assertEqual(transcript[5]["resolvedCommand"], "statistics dump") + self.assertEqual(transcript[5]["error"], "") statistics_dump = json.loads(transcript[5]["output"]) # Dump result should be valid JSON self.assertTrue(statistics_dump is not json.JSONDecodeError) @@ -194,7 +203,6 @@ def test_save_transcript_setting_default(self): # The setting's default value should be "false" self.runCmd("settings show interpreter.save-transcript", "interpreter.save-transcript (boolean) = false\n") - # self.assertEqual(res.GetOutput(), ) def test_save_transcript_setting_off(self): ci = self.buildAndCreateTarget() @@ -220,7 +228,7 @@ def test_save_transcript_setting_on(self): self.assertEqual(len(transcript), 1) self.assertEqual(transcript[0]["command"], "version") - def test_save_transcript_returns_copy(self): + def test_get_transcript_returns_copy(self): """ Test that the returned structured data is *at least* a shallow copy. >From 1cac4fa898051537b6cc9c8472dd368436c3a511 Mon Sep 17 00:00:00 2001 From: Roy Shi <roy...@meta.com> Date: Mon, 20 May 2024 22:41:57 -0400 Subject: [PATCH 2/4] Small fixes --- lldb/include/lldb/Interpreter/CommandInterpreter.h | 2 +- .../API/python_api/interpreter/TestCommandInterpreterAPI.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lldb/include/lldb/Interpreter/CommandInterpreter.h b/lldb/include/lldb/Interpreter/CommandInterpreter.h index 7f420daca450a..6e21af7180f79 100644 --- a/lldb/include/lldb/Interpreter/CommandInterpreter.h +++ b/lldb/include/lldb/Interpreter/CommandInterpreter.h @@ -580,7 +580,7 @@ class CommandInterpreter : public Broadcaster, void SetEchoCommentCommands(bool enable); bool GetRepeatPreviousCommand() const; - + bool GetRequireCommandOverwrite() const; const CommandObject::CommandMap &GetUserCommands() const { diff --git a/lldb/test/API/python_api/interpreter/TestCommandInterpreterAPI.py b/lldb/test/API/python_api/interpreter/TestCommandInterpreterAPI.py index 73eef27095b6d..1541bcdddd1a4 100644 --- a/lldb/test/API/python_api/interpreter/TestCommandInterpreterAPI.py +++ b/lldb/test/API/python_api/interpreter/TestCommandInterpreterAPI.py @@ -118,7 +118,7 @@ def test_get_transcript(self): res = lldb.SBCommandReturnObject() ci.HandleCommand("version", res) ci.HandleCommand("an-unknown-command", res) - ci.HandleCommand("br set -f main.c -l %d" % self.line, res) + ci.HandleCommand("br s -f main.c -l %d" % self.line, res) ci.HandleCommand("r", res) ci.HandleCommand("p a", res) ci.HandleCommand("statistics dump", res) @@ -161,7 +161,7 @@ def test_get_transcript(self): }) # (lldb) br set -f main.c -l <line> - self.assertEqual(transcript[2]["command"], "br set -f main.c -l %d" % self.line) + self.assertEqual(transcript[2]["command"], "br s -f main.c -l %d" % self.line) self.assertEqual(transcript[2]["resolvedCommand"], "breakpoint set -f main.c -l %d" % self.line) # Breakpoint 1: where = a.out`main + 29 at main.c:5:3, address = 0x0000000100000f7d self.assertIn("Breakpoint 1: where = a.out`main ", transcript[2]["output"]) >From 67a1197a15682731f37890734a5c8674896e0428 Mon Sep 17 00:00:00 2001 From: Roy Shi <roy...@meta.com> Date: Mon, 20 May 2024 22:42:42 -0400 Subject: [PATCH 3/4] More small fixes --- .../API/python_api/interpreter/TestCommandInterpreterAPI.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lldb/test/API/python_api/interpreter/TestCommandInterpreterAPI.py b/lldb/test/API/python_api/interpreter/TestCommandInterpreterAPI.py index 1541bcdddd1a4..157594b4aefa1 100644 --- a/lldb/test/API/python_api/interpreter/TestCommandInterpreterAPI.py +++ b/lldb/test/API/python_api/interpreter/TestCommandInterpreterAPI.py @@ -160,7 +160,7 @@ def test_get_transcript(self): "error": "error: 'an-unknown-command' is not a valid command.\n", }) - # (lldb) br set -f main.c -l <line> + # (lldb) br s -f main.c -l <line> self.assertEqual(transcript[2]["command"], "br s -f main.c -l %d" % self.line) self.assertEqual(transcript[2]["resolvedCommand"], "breakpoint set -f main.c -l %d" % self.line) # Breakpoint 1: where = a.out`main + 29 at main.c:5:3, address = 0x0000000100000f7d >From eadf71a28c7445b727e5ea783ffb4d070b5c8b3b Mon Sep 17 00:00:00 2001 From: Roy Shi <roy...@meta.com> Date: Thu, 23 May 2024 22:26:54 -0400 Subject: [PATCH 4/4] Add timestamp into transcript, rename duration field, fix format for python files --- .../source/Interpreter/CommandInterpreter.cpp | 9 ++- .../commands/statistics/basic/TestStats.py | 57 +++++++++--------- .../interpreter/TestCommandInterpreterAPI.py | 60 ++++++++++++++----- 3 files changed, 83 insertions(+), 43 deletions(-) diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp index 04820bd7d39f6..97d581d21784f 100644 --- a/lldb/source/Interpreter/CommandInterpreter.cpp +++ b/lldb/source/Interpreter/CommandInterpreter.cpp @@ -6,6 +6,7 @@ // //===----------------------------------------------------------------------===// +#include <chrono> #include <cstdlib> #include <limits> #include <memory> @@ -1909,6 +1910,11 @@ bool CommandInterpreter::HandleCommand(const char *command_line, transcript_item = std::make_shared<StructuredData::Dictionary>(); transcript_item->AddStringItem("command", command_line); + transcript_item->AddIntegerItem( + "timestampInEpochSeconds", + std::chrono::duration_cast<std::chrono::seconds>( + std::chrono::system_clock::now().time_since_epoch()) + .count()); m_transcript.AddItem(transcript_item); } @@ -2078,7 +2084,8 @@ bool CommandInterpreter::HandleCommand(const char *command_line, transcript_item->AddStringItem("output", result.GetOutputData()); transcript_item->AddStringItem("error", result.GetErrorData()); - transcript_item->AddFloatItem("seconds", execute_time.get().count()); + transcript_item->AddFloatItem("durationInSeconds", + execute_time.get().count()); } return result.Succeeded(); diff --git a/lldb/test/API/commands/statistics/basic/TestStats.py b/lldb/test/API/commands/statistics/basic/TestStats.py index a536ee2c9bbdc..abf610d78dd9f 100644 --- a/lldb/test/API/commands/statistics/basic/TestStats.py +++ b/lldb/test/API/commands/statistics/basic/TestStats.py @@ -624,32 +624,33 @@ def test_had_frame_variable_errors(self): # modules with debugInfoHadVariableErrors is greater than zero self.assertGreater(stats["totalModuleCountWithVariableErrors"], 0) -def test_transcript(self): - """ - Test "statistics dump" and the transcript information. - """ - self.build() - exe = self.getBuildArtifact("a.out") - target = self.createTestTarget(file_path=exe) - self.runCmd("settings set target.save-transcript true") - self.runCmd("version") - # Verify the output of a first "statistics dump" - debug_stats = self.get_stats() - self.assertIn("transcript", debug_stats) - transcript = debug_stats["transcript"] - self.assertEqual(len(transcript), 2) - self.assertEqual(transcript[0]["command"], "version") - self.assertEqual(transcript[1]["command"], "statistics dump") - self.assertEqual(transcript[1]["output"], "") - - # Verify the output of a second "statistics dump" - debug_stats = self.get_stats() - self.assertIn("transcript", debug_stats) - transcript = debug_stats["transcript"] - self.assertEqual(len(transcript), 3) - self.assertEqual(transcript[0]["command"], "version") - self.assertEqual(transcript[1]["command"], "statistics dump") - self.assertNotEqual(transcript[1]["output"], "") - self.assertEqual(transcript[2]["command"], "statistics dump") - self.assertEqual(transcript[2]["output"], "") +def test_transcript(self): + """ + Test "statistics dump" and the transcript information. + """ + self.build() + exe = self.getBuildArtifact("a.out") + target = self.createTestTarget(file_path=exe) + self.runCmd("settings set target.save-transcript true") + self.runCmd("version") + + # Verify the output of a first "statistics dump" + debug_stats = self.get_stats() + self.assertIn("transcript", debug_stats) + transcript = debug_stats["transcript"] + self.assertEqual(len(transcript), 2) + self.assertEqual(transcript[0]["command"], "version") + self.assertEqual(transcript[1]["command"], "statistics dump") + self.assertEqual(transcript[1]["output"], "") + + # Verify the output of a second "statistics dump" + debug_stats = self.get_stats() + self.assertIn("transcript", debug_stats) + transcript = debug_stats["transcript"] + self.assertEqual(len(transcript), 3) + self.assertEqual(transcript[0]["command"], "version") + self.assertEqual(transcript[1]["command"], "statistics dump") + self.assertNotEqual(transcript[1]["output"], "") + self.assertEqual(transcript[2]["command"], "statistics dump") + self.assertEqual(transcript[2]["output"], "") diff --git a/lldb/test/API/python_api/interpreter/TestCommandInterpreterAPI.py b/lldb/test/API/python_api/interpreter/TestCommandInterpreterAPI.py index 157594b4aefa1..9ac56266a6eba 100644 --- a/lldb/test/API/python_api/interpreter/TestCommandInterpreterAPI.py +++ b/lldb/test/API/python_api/interpreter/TestCommandInterpreterAPI.py @@ -133,17 +133,19 @@ def test_get_transcript(self): self.assertIn("resolvedCommand", command) self.assertIn("output", command) self.assertIn("error", command) - self.assertIn("seconds", command) + self.assertIn("durationInSeconds", command) + self.assertIn("timestampInEpochSeconds", command) # The following validates individual commands in the transcript. # # Notes: # 1. Some of the asserts rely on the exact output format of the # commands. Hopefully we are not changing them any time soon. - # 2. We are removing the "seconds" field from each command, so that - # some of the validations below can be easier / more readable. + # 2. We are removing the time-related fields from each command, so + # that some of the validations below can be easier / more readable. for command in transcript: - del(command["seconds"]) + del command["durationInSeconds"] + del command["timestampInEpochSeconds"] # (lldb) version self.assertEqual(transcript[0]["command"], "version") @@ -152,17 +154,22 @@ def test_get_transcript(self): self.assertEqual(transcript[0]["error"], "") # (lldb) an-unknown-command - self.assertEqual(transcript[1], + self.assertEqual( + transcript[1], { "command": "an-unknown-command", "resolvedCommand": "an-unknown-command", "output": "", "error": "error: 'an-unknown-command' is not a valid command.\n", - }) + }, + ) # (lldb) br s -f main.c -l <line> self.assertEqual(transcript[2]["command"], "br s -f main.c -l %d" % self.line) - self.assertEqual(transcript[2]["resolvedCommand"], "breakpoint set -f main.c -l %d" % self.line) + self.assertEqual( + transcript[2]["resolvedCommand"], + "breakpoint set -f main.c -l %d" % self.line, + ) # Breakpoint 1: where = a.out`main + 29 at main.c:5:3, address = 0x0000000100000f7d self.assertIn("Breakpoint 1: where = a.out`main ", transcript[2]["output"]) self.assertEqual(transcript[2]["error"], "") @@ -176,13 +183,15 @@ def test_get_transcript(self): self.assertEqual(transcript[3]["error"], "") # (lldb) p a - self.assertEqual(transcript[4], + self.assertEqual( + transcript[4], { "command": "p a", "resolvedCommand": "dwim-print -- a", "output": "(int) 123\n", "error": "", - }) + }, + ) # (lldb) statistics dump self.assertEqual(transcript[5]["command"], "statistics dump") @@ -202,7 +211,10 @@ def test_save_transcript_setting_default(self): res = lldb.SBCommandReturnObject() # The setting's default value should be "false" - self.runCmd("settings show interpreter.save-transcript", "interpreter.save-transcript (boolean) = false\n") + self.runCmd( + "settings show interpreter.save-transcript", + "interpreter.save-transcript (boolean) = false\n", + ) def test_save_transcript_setting_off(self): ci = self.buildAndCreateTarget() @@ -247,17 +259,37 @@ def test_get_transcript_returns_copy(self): structured_data_1 = ci.GetTranscript() self.assertTrue(structured_data_1.IsValid()) self.assertEqual(structured_data_1.GetSize(), 1) - self.assertEqual(structured_data_1.GetItemAtIndex(0).GetValueForKey("command").GetStringValue(100), "version") + self.assertEqual( + structured_data_1.GetItemAtIndex(0) + .GetValueForKey("command") + .GetStringValue(100), + "version", + ) # Run some more commands and get the transcript as structured data again self.runCmd("help") structured_data_2 = ci.GetTranscript() self.assertTrue(structured_data_2.IsValid()) self.assertEqual(structured_data_2.GetSize(), 2) - self.assertEqual(structured_data_2.GetItemAtIndex(0).GetValueForKey("command").GetStringValue(100), "version") - self.assertEqual(structured_data_2.GetItemAtIndex(1).GetValueForKey("command").GetStringValue(100), "help") + self.assertEqual( + structured_data_2.GetItemAtIndex(0) + .GetValueForKey("command") + .GetStringValue(100), + "version", + ) + self.assertEqual( + structured_data_2.GetItemAtIndex(1) + .GetValueForKey("command") + .GetStringValue(100), + "help", + ) # Now, the first structured data should remain unchanged self.assertTrue(structured_data_1.IsValid()) self.assertEqual(structured_data_1.GetSize(), 1) - self.assertEqual(structured_data_1.GetItemAtIndex(0).GetValueForKey("command").GetStringValue(100), "version") + self.assertEqual( + structured_data_1.GetItemAtIndex(0) + .GetValueForKey("command") + .GetStringValue(100), + "version", + ) _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits