mib updated this revision to Diff 279639.
mib added a comment.
Reformat patch.
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D82155/new/
https://reviews.llvm.org/D82155
Files:
lldb/include/lldb/Interpreter/CommandInterpreter.h
lldb/source/Commands/CMakeLists.txt
lldb/source/Commands/CommandObjectQuit.cpp
lldb/source/Commands/CommandObjectSession.cpp
lldb/source/Commands/CommandObjectSession.h
lldb/source/Interpreter/CommandInterpreter.cpp
lldb/source/Interpreter/InterpreterProperties.td
lldb/test/API/commands/session/save/TestSessionSave.py
Index: lldb/test/API/commands/session/save/TestSessionSave.py
===================================================================
--- /dev/null
+++ lldb/test/API/commands/session/save/TestSessionSave.py
@@ -0,0 +1,70 @@
+"""
+Test the session save feature
+"""
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class SessionSaveTestCase(TestBase):
+
+ mydir = TestBase.compute_mydir(__file__)
+
+ def setUp(self):
+ configuration.settings.append(("interpreter.echo-commands", "true"))
+ configuration.settings.append(("interpreter.echo-comment-commands", "true"))
+ configuration.settings.append(("interpreter.stop-command-source-on-error", "false"))
+ TestBase.setUp(self)
+
+
+ def raw_transcript_builder(self, cmd, res):
+ raw = "(lldb) " + cmd + "\n"
+ if res.GetOutputSize():
+ raw += res.GetOutput()
+ if res.GetErrorSize():
+ raw += res.GetError()
+ return raw
+
+
+ @skipIfWindows
+ @skipIfReproducer
+ @no_debug_info_test
+ def test_session_save(self):
+ raw = ""
+ inputs = [
+ '# This is a comment', # Comment
+ 'help session', # Valid command
+ 'Lorem ipsum' # Invalid command
+ ]
+
+ import tempfile
+ tf = tempfile.NamedTemporaryFile()
+
+ interpreter = self.dbg.GetCommandInterpreter()
+ for cmd in inputs:
+ res = lldb.SBCommandReturnObject()
+ interpreter.HandleCommand(cmd, res)
+ raw += self.raw_transcript_builder(cmd, res)
+
+ self.assertTrue(interpreter.HasCommands())
+ self.assertTrue(len(raw) != 0)
+
+ # Check for error
+ cmd = 'session save /root/file'
+ interpreter.HandleCommand(cmd, res)
+ self.assertFalse(res.Succeeded())
+ raw += self.raw_transcript_builder(cmd, res)
+
+ output_file = tf.name
+
+ res = lldb.SBCommandReturnObject()
+ interpreter.HandleCommand('session save ' + output_file, res)
+ self.assertTrue(res.Succeeded())
+ raw += self.raw_transcript_builder(cmd, res)
+
+ with open(output_file, "r") as file:
+ content = file.read()
+ for line in raw.splitlines():
+ self.assertIn(line, content)
Index: lldb/source/Interpreter/InterpreterProperties.td
===================================================================
--- lldb/source/Interpreter/InterpreterProperties.td
+++ lldb/source/Interpreter/InterpreterProperties.td
@@ -9,6 +9,10 @@
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 SaveSessionOnQuit: Property<"save-session-on-quit", "Boolean">,
+ Global,
+ DefaultFalse,
+ Desc<"If true, LLDB will save the session's transcripts before quitting.">;
def StopCmdSourceOnError: Property<"stop-command-source-on-error", "Boolean">,
Global,
DefaultTrue,
Index: lldb/source/Interpreter/CommandInterpreter.cpp
===================================================================
--- lldb/source/Interpreter/CommandInterpreter.cpp
+++ lldb/source/Interpreter/CommandInterpreter.cpp
@@ -6,6 +6,7 @@
//
//===----------------------------------------------------------------------===//
+#include <limits>
#include <memory>
#include <stdlib.h>
#include <string>
@@ -31,6 +32,7 @@
#include "Commands/CommandObjectQuit.h"
#include "Commands/CommandObjectRegister.h"
#include "Commands/CommandObjectReproducer.h"
+#include "Commands/CommandObjectSession.h"
#include "Commands/CommandObjectSettings.h"
#include "Commands/CommandObjectSource.h"
#include "Commands/CommandObjectStats.h"
@@ -52,6 +54,8 @@
#if LLDB_ENABLE_LIBEDIT
#include "lldb/Host/Editline.h"
#endif
+#include "lldb/Host/File.h"
+#include "lldb/Host/FileCache.h"
#include "lldb/Host/Host.h"
#include "lldb/Host/HostInfo.h"
@@ -74,6 +78,7 @@
#include "llvm/Support/FormatAdapters.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/PrettyStackTrace.h"
+#include "llvm/Support/ScopedPrinter.h"
using namespace lldb;
using namespace lldb_private;
@@ -116,7 +121,7 @@
m_skip_lldbinit_files(false), m_skip_app_init_files(false),
m_command_io_handler_sp(), m_comment_char('#'),
m_batch_command_mode(false), m_truncation_warning(eNoTruncation),
- m_command_source_depth(0), m_result() {
+ m_command_source_depth(0), m_result(), m_transcript_stream() {
SetEventName(eBroadcastBitThreadShouldExit, "thread-should-exit");
SetEventName(eBroadcastBitResetPrompt, "reset-prompt");
SetEventName(eBroadcastBitQuitCommandReceived, "quit");
@@ -142,6 +147,17 @@
m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, enable);
}
+bool CommandInterpreter::GetSaveSessionOnQuit() const {
+ const uint32_t idx = ePropertySaveSessionOnQuit;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_interpreter_properties[idx].default_uint_value != 0);
+}
+
+void CommandInterpreter::SetSaveSessionOnQuit(bool enable) {
+ const uint32_t idx = ePropertySaveSessionOnQuit;
+ m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, enable);
+}
+
bool CommandInterpreter::GetEchoCommands() const {
const uint32_t idx = ePropertyEchoCommands;
return m_collection_sp->GetPropertyAtIndexAsBoolean(
@@ -493,6 +509,7 @@
CommandObjectSP(new CommandObjectReproducer(*this));
m_command_dict["script"] =
CommandObjectSP(new CommandObjectScript(*this, script_language));
+ m_command_dict["session"] = CommandObjectSP(new CommandObjectSession(*this));
m_command_dict["settings"] =
CommandObjectSP(new CommandObjectMultiwordSettings(*this));
m_command_dict["source"] =
@@ -1667,6 +1684,8 @@
else
add_to_history = (lazy_add_to_history == eLazyBoolYes);
+ m_transcript_stream << "(lldb) " << command_line << '\n';
+
bool empty_command = false;
bool comment_command = false;
if (command_string.empty())
@@ -1799,6 +1818,9 @@
LLDB_LOGF(log, "HandleCommand, command %s",
(result.Succeeded() ? "succeeded" : "did not succeed"));
+ m_transcript_stream << result.GetOutputData();
+ m_transcript_stream << result.GetErrorData();
+
return result.Succeeded();
}
@@ -2877,6 +2899,51 @@
return false;
}
+bool CommandInterpreter::SaveTranscript(
+ CommandReturnObject &result, llvm::Optional<std::string> output_file) {
+ if (output_file == llvm::None || output_file->empty()) {
+ std::string now = llvm::to_string(std::chrono::system_clock::now());
+ std::replace(now.begin(), now.end(), ' ', '_');
+ const std::string file_name = "lldb_session_" + now + ".log";
+ FileSpec tmp = HostInfo::GetGlobalTempDir();
+ tmp.AppendPathComponent(file_name);
+ output_file = tmp.GetPath();
+ }
+
+ auto error_out = [&](llvm::StringRef error_message, std::string description) {
+ LLDB_LOG(GetLogIfAllCategoriesSet(LIBLLDB_LOG_COMMANDS), "{0} ({1}:{2})",
+ error_message, output_file, description);
+ result.AppendErrorWithFormatv(
+ "Failed to save session's transcripts to {0}!", *output_file);
+ return false;
+ };
+
+ File::OpenOptions flags = File::eOpenOptionWrite |
+ File::eOpenOptionCanCreate |
+ File::eOpenOptionTruncate;
+
+ auto opened_file = FileSystem::Instance().Open(FileSpec(*output_file), flags);
+
+ if (!opened_file)
+ return error_out("Unable to create file",
+ llvm::toString(opened_file.takeError()));
+
+ FileUP file = std::move(opened_file.get());
+
+ size_t byte_size = m_transcript_stream.GetSize();
+
+ Status error = file->Write(m_transcript_stream.GetData(), byte_size);
+
+ if (error.Fail() || byte_size != m_transcript_stream.GetSize())
+ return error_out("Unable to write to destination file",
+ "Bytes written do not match transcript size.");
+
+ GetDebugger().GetOutputStreamSP()->Printf(
+ "Session's transcripts saved to %s\n", output_file->c_str());
+
+ return true;
+}
+
void CommandInterpreter::GetLLDBCommandsFromIOHandler(
const char *prompt, IOHandlerDelegate &delegate, void *baton) {
Debugger &debugger = GetDebugger();
Index: lldb/source/Commands/CommandObjectSession.h
===================================================================
--- /dev/null
+++ lldb/source/Commands/CommandObjectSession.h
@@ -0,0 +1,23 @@
+//===-- CommandObjectSession.h ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_COMMANDS_COMMANDOBJECTSESSION_H
+#define LLDB_SOURCE_COMMANDS_COMMANDOBJECTSESSION_H
+
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+
+namespace lldb_private {
+
+class CommandObjectSession : public CommandObjectMultiword {
+public:
+ CommandObjectSession(CommandInterpreter &interpreter);
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_COMMANDS_COMMANDOBJECTSESSION_H
Index: lldb/source/Commands/CommandObjectSession.cpp
===================================================================
--- /dev/null
+++ lldb/source/Commands/CommandObjectSession.cpp
@@ -0,0 +1,53 @@
+#include "CommandObjectSession.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+class CommandObjectSessionSave : public CommandObjectParsed {
+public:
+ CommandObjectSessionSave(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "session save",
+ "Save the current session transcripts to a file.\n"
+ "If no file if specified, transcripts will be "
+ "saved to a temporary file.",
+ "session save [file]") {
+ CommandArgumentEntry arg1;
+ arg1.emplace_back(eArgTypePath, eArgRepeatOptional);
+ m_arguments.push_back(arg1);
+ }
+
+ ~CommandObjectSessionSave() override = default;
+
+ void
+ HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eDiskFileCompletion,
+ request, nullptr);
+ }
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ llvm::StringRef file_path;
+
+ if (!args.empty())
+ file_path = args[0].ref();
+
+ if (m_interpreter.SaveTranscript(result, file_path.str()))
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ else
+ result.SetStatus(eReturnStatusFailed);
+ return result.Succeeded();
+ }
+};
+
+CommandObjectSession::CommandObjectSession(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "session",
+ "Commands controlling LLDB session.",
+ "session <subcommand> [<command-options>]") {
+ LoadSubCommand("save",
+ CommandObjectSP(new CommandObjectSessionSave(interpreter)));
+ // TODO: Move 'history' subcommand from CommandObjectCommands.
+}
Index: lldb/source/Commands/CommandObjectQuit.cpp
===================================================================
--- lldb/source/Commands/CommandObjectQuit.cpp
+++ lldb/source/Commands/CommandObjectQuit.cpp
@@ -72,6 +72,13 @@
}
}
+ if (m_interpreter.GetSaveSessionOnQuit()) {
+ if (!m_interpreter.SaveTranscript(result)) {
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+
if (command.GetArgumentCount() > 1) {
result.AppendError("Too many arguments for 'quit'. Only an optional exit "
"code is allowed");
Index: lldb/source/Commands/CMakeLists.txt
===================================================================
--- lldb/source/Commands/CMakeLists.txt
+++ lldb/source/Commands/CMakeLists.txt
@@ -13,6 +13,7 @@
CommandObjectFrame.cpp
CommandObjectGUI.cpp
CommandObjectHelp.cpp
+ CommandObjectLanguage.cpp
CommandObjectLog.cpp
CommandObjectMemory.cpp
CommandObjectMultiword.cpp
@@ -22,6 +23,7 @@
CommandObjectQuit.cpp
CommandObjectRegister.cpp
CommandObjectReproducer.cpp
+ CommandObjectSession.cpp
CommandObjectSettings.cpp
CommandObjectSource.cpp
CommandObjectStats.cpp
@@ -31,7 +33,6 @@
CommandObjectVersion.cpp
CommandObjectWatchpoint.cpp
CommandObjectWatchpointCommand.cpp
- CommandObjectLanguage.cpp
LINK_LIBS
lldbBase
Index: lldb/include/lldb/Interpreter/CommandInterpreter.h
===================================================================
--- lldb/include/lldb/Interpreter/CommandInterpreter.h
+++ lldb/include/lldb/Interpreter/CommandInterpreter.h
@@ -20,6 +20,7 @@
#include "lldb/Utility/CompletionRequest.h"
#include "lldb/Utility/Event.h"
#include "lldb/Utility/Log.h"
+#include "lldb/Utility/StreamString.h"
#include "lldb/Utility/StringList.h"
#include "lldb/lldb-forward.h"
#include "lldb/lldb-private.h"
@@ -485,9 +486,11 @@
bool GetExpandRegexAliases() const;
bool GetPromptOnQuit() const;
-
void SetPromptOnQuit(bool enable);
+ bool GetSaveSessionOnQuit() const;
+ void SetSaveSessionOnQuit(bool enable);
+
bool GetEchoCommands() const;
void SetEchoCommands(bool enable);
@@ -526,6 +529,18 @@
bool GetSpaceReplPrompts() const;
+ /// Save the current debugger session transcript to a file on disk.
+ /// \param output_file
+ /// The file path to which the session transcript will be written. Since
+ /// the argument is optional, an arbitrary temporary file will be create
+ /// when no argument is passed.
+ /// \param result
+ /// This is used to pass function output and error messages.
+ /// \return \b true if the session transcript was successfully written to
+ /// disk, \b false otherwise.
+ bool SaveTranscript(CommandReturnObject &result,
+ llvm::Optional<std::string> output_file = llvm::None);
+
protected:
friend class Debugger;
@@ -621,6 +636,8 @@
llvm::Optional<int> m_quit_exit_code;
// If the driver is accepts custom exit codes for the 'quit' command.
bool m_allow_exit_code = false;
+
+ StreamString m_transcript_stream;
};
} // namespace lldb_private
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits