[Lldb-commits] [lldb] Fix a test failure in PR #92843 (PR #94067)
via lldb-commits
lldb-commits at lists.llvm.org
Fri May 31 16:40:01 PDT 2024
https://github.com/royitaqi updated https://github.com/llvm/llvm-project/pull/94067
>From 8499f16ad46b3268f35da2bfcbfa02a10aab935a Mon Sep 17 00:00:00 2001
From: Roy Shi <royshi at meta.com>
Date: Mon, 20 May 2024 22:30:40 -0400
Subject: [PATCH 01/13] 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 <royshi at meta.com>
Date: Mon, 20 May 2024 22:41:57 -0400
Subject: [PATCH 02/13] 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 <royshi at meta.com>
Date: Mon, 20 May 2024 22:42:42 -0400
Subject: [PATCH 03/13] 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 <royshi at meta.com>
Date: Thu, 23 May 2024 22:26:54 -0400
Subject: [PATCH 04/13] 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",
+ )
>From d2389e3d722a71313186eb99b91043c3d9b672cf Mon Sep 17 00:00:00 2001
From: Roy Shi <royshi at meta.com>
Date: Thu, 23 May 2024 23:05:07 -0400
Subject: [PATCH 05/13] Fix test and comment
---
lldb/include/lldb/API/SBCommandInterpreter.h | 4 +-
.../lldb/Interpreter/CommandInterpreter.h | 6 +-
.../commands/statistics/basic/TestStats.py | 64 ++++++++++---------
3 files changed, 42 insertions(+), 32 deletions(-)
diff --git a/lldb/include/lldb/API/SBCommandInterpreter.h b/lldb/include/lldb/API/SBCommandInterpreter.h
index 8eb4a71cb7f88..a2d28b2d17968 100644
--- a/lldb/include/lldb/API/SBCommandInterpreter.h
+++ b/lldb/include/lldb/API/SBCommandInterpreter.h
@@ -324,7 +324,9 @@ class SBCommandInterpreter {
/// - "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.
+ /// - "durationInSeconds" (float): The time it took to execute the command.
+ /// - "timestampInEpochSeconds" (int): The timestamp when the command is
+ /// executed.
SBStructuredData GetTranscript();
protected:
diff --git a/lldb/include/lldb/Interpreter/CommandInterpreter.h b/lldb/include/lldb/Interpreter/CommandInterpreter.h
index 6e21af7180f79..e59948a333bfd 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 {
@@ -780,7 +780,9 @@ class CommandInterpreter : public Broadcaster,
/// - "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.
+ /// - "durationInSeconds" (float): The time it took to execute the command.
+ /// - "timestampInEpochSeconds" (int): The timestamp when the command is
+ /// executed.
///
/// Turn on settings `interpreter.save-transcript` for LLDB to populate
/// this list. Otherwise this list is empty.
diff --git a/lldb/test/API/commands/statistics/basic/TestStats.py b/lldb/test/API/commands/statistics/basic/TestStats.py
index abf610d78dd9f..4b0d3256f0c7e 100644
--- a/lldb/test/API/commands/statistics/basic/TestStats.py
+++ b/lldb/test/API/commands/statistics/basic/TestStats.py
@@ -624,33 +624,39 @@ 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 interpreter.save-transcript true")
+ self.runCmd("version")
-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"], "")
+ # Verify the output of a first "statistics dump"
+ debug_stats = self.get_stats()
+ self.assertIn("transcript", debug_stats)
+ transcript = debug_stats["transcript"]
+ print("DEBUG1")
+ print(transcript)
+ self.assertEqual(len(transcript), 2)
+ self.assertEqual(transcript[0]["resolvedCommand"], "version")
+ self.assertEqual(transcript[1]["resolvedCommand"], "statistics dump")
+ # The first "statistics dump" in the transcript should have no output
+ self.assertNotIn("output", transcript[1])
+
+ # Verify the output of a second "statistics dump"
+ debug_stats = self.get_stats()
+ self.assertIn("transcript", debug_stats)
+ transcript = debug_stats["transcript"]
+ print("DEBUG2")
+ print(transcript)
+ self.assertEqual(len(transcript), 3)
+ self.assertEqual(transcript[0]["resolvedCommand"], "version")
+ self.assertEqual(transcript[1]["resolvedCommand"], "statistics dump")
+ # The first "statistics dump" in the transcript should have output now
+ self.assertIn("output", transcript[1])
+ self.assertEqual(transcript[2]["resolvedCommand"], "statistics dump")
+ # The second "statistics dump" in the transcript should have no output
+ self.assertNotIn("output", transcript[2])
>From a2ea0ef10eeba6f1c2be187b85a5f7bc2dc41fa6 Mon Sep 17 00:00:00 2001
From: Roy Shi <royshi at meta.com>
Date: Thu, 23 May 2024 23:12:24 -0400
Subject: [PATCH 06/13] Fix more format
---
.../lldb/Interpreter/CommandInterpreter.h | 2 +-
.../interpreter/TestCommandInterpreterAPI.py | 45 ++++---------------
2 files changed, 10 insertions(+), 37 deletions(-)
diff --git a/lldb/include/lldb/Interpreter/CommandInterpreter.h b/lldb/include/lldb/Interpreter/CommandInterpreter.h
index e59948a333bfd..3b9f920f12b8d 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 9ac56266a6eba..17793e7ab00a9 100644
--- a/lldb/test/API/python_api/interpreter/TestCommandInterpreterAPI.py
+++ b/lldb/test/API/python_api/interpreter/TestCommandInterpreterAPI.py
@@ -154,15 +154,13 @@ 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)
@@ -183,15 +181,13 @@ 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")
@@ -211,10 +207,7 @@ 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()
@@ -259,37 +252,17 @@ 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")
>From 83df90b8ee2375bb78acf314b92b8de349fc9699 Mon Sep 17 00:00:00 2001
From: Roy Shi <royshi at meta.com>
Date: Thu, 23 May 2024 23:22:58 -0400
Subject: [PATCH 07/13] Remove debug prints
---
lldb/test/API/commands/statistics/basic/TestStats.py | 4 ----
1 file changed, 4 deletions(-)
diff --git a/lldb/test/API/commands/statistics/basic/TestStats.py b/lldb/test/API/commands/statistics/basic/TestStats.py
index 4b0d3256f0c7e..844aa7d0e9a68 100644
--- a/lldb/test/API/commands/statistics/basic/TestStats.py
+++ b/lldb/test/API/commands/statistics/basic/TestStats.py
@@ -638,8 +638,6 @@ def test_transcript(self):
debug_stats = self.get_stats()
self.assertIn("transcript", debug_stats)
transcript = debug_stats["transcript"]
- print("DEBUG1")
- print(transcript)
self.assertEqual(len(transcript), 2)
self.assertEqual(transcript[0]["resolvedCommand"], "version")
self.assertEqual(transcript[1]["resolvedCommand"], "statistics dump")
@@ -650,8 +648,6 @@ def test_transcript(self):
debug_stats = self.get_stats()
self.assertIn("transcript", debug_stats)
transcript = debug_stats["transcript"]
- print("DEBUG2")
- print(transcript)
self.assertEqual(len(transcript), 3)
self.assertEqual(transcript[0]["resolvedCommand"], "version")
self.assertEqual(transcript[1]["resolvedCommand"], "statistics dump")
>From 685771c5fc2da0e581294cb014f60ab426e2ca61 Mon Sep 17 00:00:00 2001
From: Roy Shi <royshi at meta.com>
Date: Thu, 30 May 2024 10:36:58 -0700
Subject: [PATCH 08/13] Split "resolvedCommand" into "commandName" and
"commandArguments"
---
lldb/include/lldb/API/SBCommandInterpreter.h | 6 ++++-
.../lldb/Interpreter/CommandInterpreter.h | 3 ++-
.../source/Interpreter/CommandInterpreter.cpp | 14 ++++++-----
.../interpreter/TestCommandInterpreterAPI.py | 23 +++++++++++--------
4 files changed, 28 insertions(+), 18 deletions(-)
diff --git a/lldb/include/lldb/API/SBCommandInterpreter.h b/lldb/include/lldb/API/SBCommandInterpreter.h
index a2d28b2d17968..639309aa32bfc 100644
--- a/lldb/include/lldb/API/SBCommandInterpreter.h
+++ b/lldb/include/lldb/API/SBCommandInterpreter.h
@@ -321,12 +321,16 @@ 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 given by the user.
- /// - "resolvedCommand" (string): The expanded command that was executed.
+ /// - "commandName" (string): The name of the executed command.
+ /// - "commandArguments" (string): The arguments of the executed command.
/// - "output" (string): The output of the command. Empty ("") if no output.
/// - "error" (string): The error of the command. Empty ("") if no error.
/// - "durationInSeconds" (float): The time it took to execute the command.
/// - "timestampInEpochSeconds" (int): The timestamp when the command is
/// executed.
+ ///
+ /// Turn on settings `interpreter.save-transcript` for LLDB to populate
+ /// this list. Otherwise this list is empty.
SBStructuredData GetTranscript();
protected:
diff --git a/lldb/include/lldb/Interpreter/CommandInterpreter.h b/lldb/include/lldb/Interpreter/CommandInterpreter.h
index 3b9f920f12b8d..8863523b2e31f 100644
--- a/lldb/include/lldb/Interpreter/CommandInterpreter.h
+++ b/lldb/include/lldb/Interpreter/CommandInterpreter.h
@@ -777,7 +777,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 given by the user.
- /// - "resolvedCommand" (string): The expanded command that was executed.
+ /// - "commandName" (string): The name of the executed command.
+ /// - "commandArguments" (string): The arguments of the executed command.
/// - "output" (string): The output of the command. Empty ("") if no output.
/// - "error" (string): The error of the command. Empty ("") if no error.
/// - "durationInSeconds" (float): The time it took to execute the command.
diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp
index 97d581d21784f..f7a088bfe9551 100644
--- a/lldb/source/Interpreter/CommandInterpreter.cpp
+++ b/lldb/source/Interpreter/CommandInterpreter.cpp
@@ -2017,12 +2017,6 @@ 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
@@ -2068,6 +2062,14 @@ bool CommandInterpreter::HandleCommand(const char *command_line,
log, "HandleCommand, command line after removing command name(s): '%s'",
remainder.c_str());
+ // 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("commandName", cmd_obj->GetCommandName());
+ transcript_item->AddStringItem("commandArguments", remainder);
+ }
+
ElapsedTime elapsed(execute_time);
cmd_obj->Execute(remainder.c_str(), result);
}
diff --git a/lldb/test/API/python_api/interpreter/TestCommandInterpreterAPI.py b/lldb/test/API/python_api/interpreter/TestCommandInterpreterAPI.py
index 17793e7ab00a9..b8a8f2b108b19 100644
--- a/lldb/test/API/python_api/interpreter/TestCommandInterpreterAPI.py
+++ b/lldb/test/API/python_api/interpreter/TestCommandInterpreterAPI.py
@@ -130,7 +130,8 @@ def test_get_transcript(self):
# All commands should have expected fields.
for command in transcript:
self.assertIn("command", command)
- self.assertIn("resolvedCommand", command)
+ # Unresolved commands don't have "commandName"/"commandArguments".
+ # We will validate these fields below, instead of here.
self.assertIn("output", command)
self.assertIn("error", command)
self.assertIn("durationInSeconds", command)
@@ -149,7 +150,8 @@ def test_get_transcript(self):
# (lldb) version
self.assertEqual(transcript[0]["command"], "version")
- self.assertEqual(transcript[0]["resolvedCommand"], "version")
+ self.assertEqual(transcript[0]["commandName"], "version")
+ self.assertEqual(transcript[0]["commandArguments"], "")
self.assertIn("lldb version", transcript[0]["output"])
self.assertEqual(transcript[0]["error"], "")
@@ -157,24 +159,23 @@ def test_get_transcript(self):
self.assertEqual(transcript[1],
{
"command": "an-unknown-command",
- "resolvedCommand": "an-unknown-command",
+ # Unresolved commands don't have "commandName"/"commandArguments"
"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]["commandName"], "breakpoint set")
+ self.assertEqual(transcript[2]["commandArguments"], "-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 --")
+ self.assertEqual(transcript[3]["commandName"], "process launch")
+ self.assertEqual(transcript[3]["commandArguments"], "-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"])
@@ -184,14 +185,16 @@ def test_get_transcript(self):
self.assertEqual(transcript[4],
{
"command": "p a",
- "resolvedCommand": "dwim-print -- a",
+ "commandName": "dwim-print",
+ "commandArguments": "-- 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]["commandName"], "statistics dump")
+ self.assertEqual(transcript[5]["commandArguments"], "")
self.assertEqual(transcript[5]["error"], "")
statistics_dump = json.loads(transcript[5]["output"])
# Dump result should be valid JSON
>From 2132829a8d9973d6212ba8cee815fe2273ffa3f6 Mon Sep 17 00:00:00 2001
From: Roy Shi <royshi at meta.com>
Date: Thu, 30 May 2024 13:23:22 -0700
Subject: [PATCH 09/13] Add argument "statistics dump --transcript". Default
off.
---
lldb/include/lldb/Target/Statistics.h | 1 +
lldb/source/Commands/CommandObjectStats.cpp | 3 ++
lldb/source/Commands/Options.td | 2 ++
lldb/source/Target/Statistics.cpp | 8 +++--
.../commands/statistics/basic/TestStats.py | 30 ++++++++++++++-----
5 files changed, 33 insertions(+), 11 deletions(-)
diff --git a/lldb/include/lldb/Target/Statistics.h b/lldb/include/lldb/Target/Statistics.h
index c4f17b503a1f9..c04d529290fff 100644
--- a/lldb/include/lldb/Target/Statistics.h
+++ b/lldb/include/lldb/Target/Statistics.h
@@ -133,6 +133,7 @@ struct ConstStringStats {
struct StatisticsOptions {
bool summary_only = false;
bool load_all_debug_info = false;
+ bool include_transcript = false;
};
/// A class that represents statistics for a since lldb_private::Target.
diff --git a/lldb/source/Commands/CommandObjectStats.cpp b/lldb/source/Commands/CommandObjectStats.cpp
index a92bb5d1165ee..1935b0fdfadfb 100644
--- a/lldb/source/Commands/CommandObjectStats.cpp
+++ b/lldb/source/Commands/CommandObjectStats.cpp
@@ -81,6 +81,9 @@ class CommandObjectStatsDump : public CommandObjectParsed {
case 'f':
m_stats_options.load_all_debug_info = true;
break;
+ case 't':
+ m_stats_options.include_transcript = true;
+ break;
default:
llvm_unreachable("Unimplemented option");
}
diff --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td
index 62bbfdc117834..08e3f53dd6d57 100644
--- a/lldb/source/Commands/Options.td
+++ b/lldb/source/Commands/Options.td
@@ -1425,4 +1425,6 @@ let Command = "statistics dump" in {
Desc<"Dump the total possible debug info statistics. "
"Force loading all the debug information if not yet loaded, and collect "
"statistics with those.">;
+ def statistics_dump_transcript: Option<"transcript", "t">, Group<1>,
+ Desc<"Include transcript.">;
}
diff --git a/lldb/source/Target/Statistics.cpp b/lldb/source/Target/Statistics.cpp
index fd31377abd06b..be0848573f812 100644
--- a/lldb/source/Target/Statistics.cpp
+++ b/lldb/source/Target/Statistics.cpp
@@ -226,6 +226,7 @@ llvm::json::Value DebuggerStats::ReportStatistics(
const bool summary_only = options.summary_only;
const bool load_all_debug_info = options.load_all_debug_info;
+ const bool include_transcript = options.include_transcript;
json::Array json_targets;
json::Array json_modules;
@@ -363,16 +364,17 @@ 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));
+ }
+ if (include_transcript) {
// 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.
+ // returned statistics itself (otherwise infinite recursion).
// - 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.
+ // buffer. It *will* appear in the next statistcs or transcript poll.
//
// For example, let's say the following commands are run in order:
// - "version"
diff --git a/lldb/test/API/commands/statistics/basic/TestStats.py b/lldb/test/API/commands/statistics/basic/TestStats.py
index 844aa7d0e9a68..40f6b0babff89 100644
--- a/lldb/test/API/commands/statistics/basic/TestStats.py
+++ b/lldb/test/API/commands/statistics/basic/TestStats.py
@@ -624,7 +624,7 @@ def test_had_frame_variable_errors(self):
# modules with debugInfoHadVariableErrors is greater than zero
self.assertGreater(stats["totalModuleCountWithVariableErrors"], 0)
- def test_transcript(self):
+ def test_transcript_happy_path(self):
"""
Test "statistics dump" and the transcript information.
"""
@@ -635,24 +635,38 @@ def test_transcript(self):
self.runCmd("version")
# Verify the output of a first "statistics dump"
- debug_stats = self.get_stats()
+ debug_stats = self.get_stats("--transcript")
self.assertIn("transcript", debug_stats)
transcript = debug_stats["transcript"]
self.assertEqual(len(transcript), 2)
- self.assertEqual(transcript[0]["resolvedCommand"], "version")
- self.assertEqual(transcript[1]["resolvedCommand"], "statistics dump")
+ self.assertEqual(transcript[0]["commandName"], "version")
+ self.assertEqual(transcript[1]["commandName"], "statistics dump")
# The first "statistics dump" in the transcript should have no output
self.assertNotIn("output", transcript[1])
# Verify the output of a second "statistics dump"
- debug_stats = self.get_stats()
+ debug_stats = self.get_stats("--transcript")
self.assertIn("transcript", debug_stats)
transcript = debug_stats["transcript"]
self.assertEqual(len(transcript), 3)
- self.assertEqual(transcript[0]["resolvedCommand"], "version")
- self.assertEqual(transcript[1]["resolvedCommand"], "statistics dump")
+ self.assertEqual(transcript[0]["commandName"], "version")
+ self.assertEqual(transcript[1]["commandName"], "statistics dump")
# The first "statistics dump" in the transcript should have output now
self.assertIn("output", transcript[1])
- self.assertEqual(transcript[2]["resolvedCommand"], "statistics dump")
+ self.assertEqual(transcript[2]["commandName"], "statistics dump")
# The second "statistics dump" in the transcript should have no output
self.assertNotIn("output", transcript[2])
+
+ def test_transcript_should_not_exist_when_not_asked_for(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 interpreter.save-transcript true")
+ self.runCmd("version")
+
+ # Verify the output of a first "statistics dump"
+ debug_stats = self.get_stats() # Not with "--transcript"
+ self.assertNotIn("transcript", debug_stats)
>From 47a670c1ef44ea4bb42263ff1f682b9511722c7b Mon Sep 17 00:00:00 2001
From: Roy Shi <royshi at meta.com>
Date: Fri, 31 May 2024 09:32:54 -0700
Subject: [PATCH 10/13] Update description for argument --transcript
---
lldb/source/Commands/Options.td | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td
index 08e3f53dd6d57..194902abdce49 100644
--- a/lldb/source/Commands/Options.td
+++ b/lldb/source/Commands/Options.td
@@ -1426,5 +1426,7 @@ let Command = "statistics dump" in {
"Force loading all the debug information if not yet loaded, and collect "
"statistics with those.">;
def statistics_dump_transcript: Option<"transcript", "t">, Group<1>,
- Desc<"Include transcript.">;
+ Desc<"If the setting interpreter.save-transcript is enabled and this "
+ "option is specified, include a JSON array with all commands the user and/"
+ "or scripts executed during a debug session.">;
}
>From 2adec1671267645c08d155388e40dec521234766 Mon Sep 17 00:00:00 2001
From: Roy Shi <royshi at meta.com>
Date: Fri, 31 May 2024 11:29:56 -0700
Subject: [PATCH 11/13] Fix python format
---
lldb/test/API/commands/statistics/basic/TestStats.py | 2 +-
.../API/python_api/interpreter/TestCommandInterpreterAPI.py | 4 +++-
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/lldb/test/API/commands/statistics/basic/TestStats.py b/lldb/test/API/commands/statistics/basic/TestStats.py
index 40f6b0babff89..ebe6166eb45e5 100644
--- a/lldb/test/API/commands/statistics/basic/TestStats.py
+++ b/lldb/test/API/commands/statistics/basic/TestStats.py
@@ -668,5 +668,5 @@ def test_transcript_should_not_exist_when_not_asked_for(self):
self.runCmd("version")
# Verify the output of a first "statistics dump"
- debug_stats = self.get_stats() # Not with "--transcript"
+ debug_stats = self.get_stats() # Not with "--transcript"
self.assertNotIn("transcript", debug_stats)
diff --git a/lldb/test/API/python_api/interpreter/TestCommandInterpreterAPI.py b/lldb/test/API/python_api/interpreter/TestCommandInterpreterAPI.py
index b8a8f2b108b19..6814ccec16c48 100644
--- a/lldb/test/API/python_api/interpreter/TestCommandInterpreterAPI.py
+++ b/lldb/test/API/python_api/interpreter/TestCommandInterpreterAPI.py
@@ -167,7 +167,9 @@ def test_get_transcript(self):
# (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]["commandName"], "breakpoint set")
- self.assertEqual(transcript[2]["commandArguments"], "-f main.c -l %d" % self.line)
+ self.assertEqual(
+ transcript[2]["commandArguments"], "-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"], "")
>From ddd361b5d16d1ccdc80b2424024de49311ac7e06 Mon Sep 17 00:00:00 2001
From: Roy Shi <royshi at meta.com>
Date: Fri, 31 May 2024 15:25:38 -0700
Subject: [PATCH 12/13] Fix build break
---
.../API/python_api/interpreter/TestCommandInterpreterAPI.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/lldb/test/API/python_api/interpreter/TestCommandInterpreterAPI.py b/lldb/test/API/python_api/interpreter/TestCommandInterpreterAPI.py
index 6814ccec16c48..e4b7eee91c46a 100644
--- a/lldb/test/API/python_api/interpreter/TestCommandInterpreterAPI.py
+++ b/lldb/test/API/python_api/interpreter/TestCommandInterpreterAPI.py
@@ -177,7 +177,8 @@ def test_get_transcript(self):
# (lldb) r
self.assertEqual(transcript[3]["command"], "r")
self.assertEqual(transcript[3]["commandName"], "process launch")
- self.assertEqual(transcript[3]["commandArguments"], "-X true --")
+ # Not checking `commandArguments`, because it can be different on different platforms/configurations.
+ # On macOS, it's "-X true --". On Linux, it's "-c/bin/bash --".
# Process 25494 launched: '<path>/TestCommandInterpreterAPI.test_structured_transcript/a.out' (x86_64)
self.assertIn("Process", transcript[3]["output"])
self.assertIn("launched", transcript[3]["output"])
>From f67598244ee67cebb844792a499c87c050747d19 Mon Sep 17 00:00:00 2001
From: Roy Shi <royshi at meta.com>
Date: Fri, 31 May 2024 16:39:41 -0700
Subject: [PATCH 13/13] Avoid creating or launching targets when possible
---
.../interpreter/TestCommandInterpreterAPI.py | 42 ++++++++-----------
1 file changed, 17 insertions(+), 25 deletions(-)
diff --git a/lldb/test/API/python_api/interpreter/TestCommandInterpreterAPI.py b/lldb/test/API/python_api/interpreter/TestCommandInterpreterAPI.py
index e4b7eee91c46a..1029bdc3096d0 100644
--- a/lldb/test/API/python_api/interpreter/TestCommandInterpreterAPI.py
+++ b/lldb/test/API/python_api/interpreter/TestCommandInterpreterAPI.py
@@ -107,6 +107,7 @@ def getTranscriptAsPythonObject(self, ci):
def test_get_transcript(self):
"""Test structured transcript generation and retrieval."""
ci = self.buildAndCreateTarget()
+ self.assertTrue(ci, VALID_COMMAND_INTERPRETER)
# Make sure the "save-transcript" setting is on
self.runCmd("settings set interpreter.save-transcript true")
@@ -119,7 +120,6 @@ def test_get_transcript(self):
ci.HandleCommand("version", res)
ci.HandleCommand("an-unknown-command", 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)
total_number_of_commands = 6
@@ -174,32 +174,22 @@ def test_get_transcript(self):
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]["commandName"], "process launch")
- # Not checking `commandArguments`, because it can be different on different platforms/configurations.
- # On macOS, it's "-X true --". On Linux, it's "-c/bin/bash --".
- # Process 25494 launched: '<path>/TestCommandInterpreterAPI.test_structured_transcript/a.out' (x86_64)
- self.assertIn("Process", transcript[3]["output"])
- self.assertIn("launched", transcript[3]["output"])
- self.assertEqual(transcript[3]["error"], "")
-
# (lldb) p a
- self.assertEqual(transcript[4],
+ self.assertEqual(transcript[3],
{
"command": "p a",
"commandName": "dwim-print",
"commandArguments": "-- a",
- "output": "(int) 123\n",
- "error": "",
+ "output": "",
+ "error": "error: <user expression 0>:1:1: use of undeclared identifier 'a'\n 1 | a\n | ^\n",
})
# (lldb) statistics dump
- self.assertEqual(transcript[5]["command"], "statistics dump")
- self.assertEqual(transcript[5]["commandName"], "statistics dump")
- self.assertEqual(transcript[5]["commandArguments"], "")
- self.assertEqual(transcript[5]["error"], "")
- statistics_dump = json.loads(transcript[5]["output"])
+ self.assertEqual(transcript[4]["command"], "statistics dump")
+ self.assertEqual(transcript[4]["commandName"], "statistics dump")
+ self.assertEqual(transcript[4]["commandArguments"], "")
+ self.assertEqual(transcript[4]["error"], "")
+ statistics_dump = json.loads(transcript[4]["output"])
# Dump result should be valid JSON
self.assertTrue(statistics_dump is not json.JSONDecodeError)
# Dump result should contain expected fields
@@ -209,14 +199,15 @@ def test_get_transcript(self):
self.assertIn("targets", statistics_dump)
def test_save_transcript_setting_default(self):
- ci = self.buildAndCreateTarget()
- res = lldb.SBCommandReturnObject()
+ ci = self.dbg.GetCommandInterpreter()
+ self.assertTrue(ci, VALID_COMMAND_INTERPRETER)
# The setting's default value should be "false"
self.runCmd("settings show interpreter.save-transcript", "interpreter.save-transcript (boolean) = false\n")
def test_save_transcript_setting_off(self):
- ci = self.buildAndCreateTarget()
+ ci = self.dbg.GetCommandInterpreter()
+ self.assertTrue(ci, VALID_COMMAND_INTERPRETER)
# Make sure the setting is off
self.runCmd("settings set interpreter.save-transcript false")
@@ -227,8 +218,8 @@ def test_save_transcript_setting_off(self):
self.assertEqual(transcript, [])
def test_save_transcript_setting_on(self):
- ci = self.buildAndCreateTarget()
- res = lldb.SBCommandReturnObject()
+ ci = self.dbg.GetCommandInterpreter()
+ self.assertTrue(ci, VALID_COMMAND_INTERPRETER)
# Make sure the setting is on
self.runCmd("settings set interpreter.save-transcript true")
@@ -248,7 +239,8 @@ def test_get_transcript_returns_copy(self):
because there is no logic in the command interpreter to modify a
transcript item (representing a command) after it has been returned.
"""
- ci = self.buildAndCreateTarget()
+ ci = self.dbg.GetCommandInterpreter()
+ self.assertTrue(ci, VALID_COMMAND_INTERPRETER)
# Make sure the setting is on
self.runCmd("settings set interpreter.save-transcript true")
More information about the lldb-commits
mailing list