Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new Python API SBCommandInterpreter::GetTranscript() #90703

Merged
merged 19 commits into from
May 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions lldb/include/lldb/API/SBCommandInterpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,14 @@ class SBCommandInterpreter {

SBStructuredData GetStatistics();

/// 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.
/// - "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.
SBStructuredData GetTranscript();

protected:
friend class lldb_private::CommandPluginInterfaceImplementation;

Expand Down
18 changes: 18 additions & 0 deletions lldb/include/lldb/Interpreter/CommandInterpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "lldb/Utility/Log.h"
#include "lldb/Utility/StreamString.h"
#include "lldb/Utility/StringList.h"
#include "lldb/Utility/StructuredData.h"
#include "lldb/lldb-forward.h"
#include "lldb/lldb-private.h"

Expand Down Expand Up @@ -560,6 +561,9 @@ class CommandInterpreter : public Broadcaster,
bool GetPromptOnQuit() const;
void SetPromptOnQuit(bool enable);

bool GetSaveTranscript() const;
void SetSaveTranscript(bool enable);

bool GetSaveSessionOnQuit() const;
void SetSaveSessionOnQuit(bool enable);

Expand Down Expand Up @@ -647,6 +651,7 @@ class CommandInterpreter : public Broadcaster,
}

llvm::json::Value GetStatistics();
const StructuredData::Array &GetTranscript() const;

protected:
friend class Debugger;
Expand Down Expand Up @@ -765,7 +770,20 @@ class CommandInterpreter : public Broadcaster,
typedef llvm::StringMap<uint64_t> CommandUsageMap;
CommandUsageMap m_command_usages;

/// Turn on settings `interpreter.save-transcript` for LLDB to populate
/// this stream. Otherwise this stream is empty.
StreamString m_transcript_stream;
royitaqi marked this conversation as resolved.
Show resolved Hide resolved

/// Contains a list of handled commands and their details. Each element in
royitaqi marked this conversation as resolved.
Show resolved Hide resolved
/// the list is a dictionary with the following keys/values:
/// - "command" (string): The 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.
///
/// Turn on settings `interpreter.save-transcript` for LLDB to populate
/// this list. Otherwise this list is empty.
StructuredData::Array m_transcript;
};

} // namespace lldb_private
Expand Down
16 changes: 16 additions & 0 deletions lldb/source/API/SBCommandInterpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
//
//===----------------------------------------------------------------------===//

#include "lldb/Utility/StructuredData.h"
#include "lldb/lldb-types.h"

#include "lldb/Interpreter/CommandInterpreter.h"
Expand Down Expand Up @@ -571,6 +572,21 @@ SBStructuredData SBCommandInterpreter::GetStatistics() {
return data;
}

SBStructuredData SBCommandInterpreter::GetTranscript() {
LLDB_INSTRUMENT_VA(this);

SBStructuredData data;
if (IsValid())
// A deep copy is performed by `std::make_shared` on the
// `StructuredData::Array`, via its implicitly-declared copy constructor.
// This ensures thread-safety between the user changing the returned
// `SBStructuredData` and the `CommandInterpreter` changing its internal
// `m_transcript`.
data.m_impl_up->SetObjectSP(
std::make_shared<StructuredData::Array>(m_opaque_ptr->GetTranscript()));
return data;
}

lldb::SBCommand SBCommandInterpreter::AddMultiwordCommand(const char *name,
const char *help) {
LLDB_INSTRUMENT_VA(this, name, help);
Expand Down
43 changes: 39 additions & 4 deletions lldb/source/Interpreter/CommandInterpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
#include "lldb/Utility/Log.h"
#include "lldb/Utility/State.h"
#include "lldb/Utility/Stream.h"
#include "lldb/Utility/StructuredData.h"
#include "lldb/Utility/Timer.h"

#include "lldb/Host/Config.h"
Expand Down Expand Up @@ -161,6 +162,17 @@ void CommandInterpreter::SetPromptOnQuit(bool enable) {
SetPropertyAtIndex(idx, enable);
}

bool CommandInterpreter::GetSaveTranscript() const {
const uint32_t idx = ePropertySaveTranscript;
return GetPropertyAtIndexAs<bool>(
idx, g_interpreter_properties[idx].default_uint_value != 0);
}

void CommandInterpreter::SetSaveTranscript(bool enable) {
const uint32_t idx = ePropertySaveTranscript;
SetPropertyAtIndex(idx, enable);
}

bool CommandInterpreter::GetSaveSessionOnQuit() const {
const uint32_t idx = ePropertySaveSessionOnQuit;
return GetPropertyAtIndexAs<bool>(
Expand Down Expand Up @@ -1889,7 +1901,16 @@ bool CommandInterpreter::HandleCommand(const char *command_line,
else
add_to_history = (lazy_add_to_history == eLazyBoolYes);

m_transcript_stream << "(lldb) " << command_line << '\n';
// The same `transcript_item` will be used below to add output and error of
// the command.
StructuredData::DictionarySP transcript_item;
if (GetSaveTranscript()) {
m_transcript_stream << "(lldb) " << command_line << '\n';

transcript_item = std::make_shared<StructuredData::Dictionary>();
transcript_item->AddStringItem("command", command_line);
m_transcript.AddItem(transcript_item);
}

bool empty_command = false;
bool comment_command = false;
Expand Down Expand Up @@ -1994,7 +2015,7 @@ bool CommandInterpreter::HandleCommand(const char *command_line,
// Take care of things like setting up the history command & calling the
// appropriate Execute method on the CommandObject, with the appropriate
// arguments.

StatsDuration execute_time;
if (cmd_obj != nullptr) {
bool generate_repeat_command = add_to_history;
// If we got here when empty_command was true, then this command is a
Expand Down Expand Up @@ -2035,14 +2056,24 @@ bool CommandInterpreter::HandleCommand(const char *command_line,
log, "HandleCommand, command line after removing command name(s): '%s'",
remainder.c_str());

ElapsedTime elapsed(execute_time);
cmd_obj->Execute(remainder.c_str(), result);
}

LLDB_LOGF(log, "HandleCommand, command %s",
(result.Succeeded() ? "succeeded" : "did not succeed"));

m_transcript_stream << result.GetOutputData();
m_transcript_stream << result.GetErrorData();
// 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) {
m_transcript_stream << result.GetOutputData();
m_transcript_stream << result.GetErrorData();

transcript_item->AddStringItem("output", result.GetOutputData());
transcript_item->AddStringItem("error", result.GetErrorData());
transcript_item->AddFloatItem("seconds", execute_time.get().count());
}

return result.Succeeded();
}
Expand Down Expand Up @@ -3554,3 +3585,7 @@ llvm::json::Value CommandInterpreter::GetStatistics() {
stats.try_emplace(command_usage.getKey(), command_usage.getValue());
return stats;
}

const StructuredData::Array &CommandInterpreter::GetTranscript() const {
return m_transcript;
}
4 changes: 4 additions & 0 deletions lldb/source/Interpreter/InterpreterProperties.td
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ let Definition = "interpreter" in {
Global,
DefaultTrue,
Desc<"If true, LLDB will prompt you before quitting if there are any live processes being debugged. If false, LLDB will quit without asking in any case.">;
def SaveTranscript: Property<"save-transcript", "Boolean">,
Global,
DefaultFalse,
Desc<"If true, commands will be saved into a transcript buffer for user access.">;
def SaveSessionOnQuit: Property<"save-session-on-quit", "Boolean">,
Global,
DefaultFalse,
Expand Down
12 changes: 12 additions & 0 deletions lldb/test/API/commands/session/save/TestSessionSave.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ def test_session_save(self):
raw = ""
interpreter = self.dbg.GetCommandInterpreter()

# Make sure "save-transcript" is on, so that all the following setings
# and commands are saved into the trasncript. Note that this cannot be
# a part of the `settings`, because this command itself won't be saved
# into the transcript.
self.runCmd("settings set interpreter.save-transcript true")

settings = [
"settings set interpreter.echo-commands true",
"settings set interpreter.echo-comment-commands true",
Expand Down Expand Up @@ -95,6 +101,12 @@ def test_session_save_on_quit(self):
raw = ""
interpreter = self.dbg.GetCommandInterpreter()

# Make sure "save-transcript" is on, so that all the following setings
# and commands are saved into the trasncript. Note that this cannot be
# a part of the `settings`, because this command itself won't be saved
# into the transcript.
self.runCmd("settings set interpreter.save-transcript true")

td = tempfile.TemporaryDirectory()

settings = [
Expand Down
Loading
Loading