JDevlieghere created this revision.
JDevlieghere added reviewers: labath, DavidSpickett, clayborg, jingham.
Herald added a project: All.
JDevlieghere requested review of this revision.

Add a "diagnostics dump" command to, as the name implies, dump the diagnostics 
to disk. The goal of this command is to let the user generate the diagnostics 
in case of an issue that doesn't cause the debugger to crash. This is also 
useful for testing.


https://reviews.llvm.org/D135622

Files:
  lldb/include/lldb/Utility/Diagnostics.h
  lldb/source/Commands/CMakeLists.txt
  lldb/source/Commands/CommandObjectDiagnostics.cpp
  lldb/source/Commands/CommandObjectDiagnostics.h
  lldb/source/Commands/Options.td
  lldb/source/Interpreter/CommandInterpreter.cpp
  lldb/source/Utility/Diagnostics.cpp
  lldb/test/Shell/Diagnostics/TestAlwaysOnLog.test
  lldb/test/Shell/Diagnostics/TestDump.test
  lldb/test/Shell/Diagnostics/TestStats.test

Index: lldb/test/Shell/Diagnostics/TestStats.test
===================================================================
--- /dev/null
+++ lldb/test/Shell/Diagnostics/TestStats.test
@@ -0,0 +1,6 @@
+# RUN: rm -rf %t
+# RUN: mkdir -p %t
+# RUN: %lldb -o 'diagnostics dump -d %t'
+# RUN: cat %t/debugger-0-stats.json | FileCheck %s
+
+# CHECK: "totalModuleCount":0
Index: lldb/test/Shell/Diagnostics/TestDump.test
===================================================================
--- /dev/null
+++ lldb/test/Shell/Diagnostics/TestDump.test
@@ -0,0 +1,12 @@
+# Check that the diagnostics dump command uses the correct directory.
+
+# Dump to an existing directory.
+# RUN: rm -rf %t.existing
+# RUN: mkdir -p %t.existing
+# RUN: %lldb -o 'diagnostics dump -d %t.existing'
+# RUN: cat %t.existing/always-on.log
+
+# Dump to a non-existing directory.
+# RUN: rm -rf %t.nonexisting
+# RUN: %lldb -o 'diagnostics dump -d %t.nonexisting'
+# RUN: cat %t.nonexisting/always-on.log
Index: lldb/test/Shell/Diagnostics/TestAlwaysOnLog.test
===================================================================
--- /dev/null
+++ lldb/test/Shell/Diagnostics/TestAlwaysOnLog.test
@@ -0,0 +1,6 @@
+# RUN: rm -rf %t
+# RUN: mkdir -p %t
+# RUN: %lldb -o 'diagnostics dump -d %t'
+# RUN: cat %t/always-on.log | FileCheck %s
+
+# CHECK: Dumping {{.*}} diagnostics to '{{.*}}'
Index: lldb/source/Utility/Diagnostics.cpp
===================================================================
--- lldb/source/Utility/Diagnostics.cpp
+++ lldb/source/Utility/Diagnostics.cpp
@@ -56,19 +56,18 @@
 }
 
 bool Diagnostics::Dump(raw_ostream &stream) {
-  SmallString<128> diagnostics_dir;
-  std::error_code ec =
-      sys::fs::createUniqueDirectory("diagnostics", diagnostics_dir);
-  if (ec) {
+  Expected<FileSpec> diagnostics_dir = CreateUniqueDirectory();
+  if (!diagnostics_dir) {
     stream << "unable to create diagnostic dir: "
-           << toString(errorCodeToError(ec)) << '\n';
+           << toString(diagnostics_dir.takeError()) << '\n';
     return false;
   }
 
-  stream << "LLDB diagnostics written to " << diagnostics_dir << "\n";
+  stream << "LLDB diagnostics written to " << diagnostics_dir->GetPath()
+         << "\n";
   stream << "Please include the directory content when filing a bug report\n";
 
-  Error error = Create(FileSpec(diagnostics_dir.str()));
+  Error error = Create(*diagnostics_dir);
   if (error) {
     stream << toString(std::move(error)) << '\n';
     return false;
@@ -77,6 +76,15 @@
   return true;
 }
 
+llvm::Expected<FileSpec> Diagnostics::CreateUniqueDirectory() {
+  SmallString<128> diagnostics_dir;
+  std::error_code ec =
+      sys::fs::createUniqueDirectory("diagnostics", diagnostics_dir);
+  if (ec)
+    return errorCodeToError(ec);
+  return FileSpec(diagnostics_dir.str());
+}
+
 Error Diagnostics::Create(const FileSpec &dir) {
   LLDB_LOG(GetLog(LLDBLog::AlwaysOn), "Dumping {0} diagnostics to '{1}'",
            m_callbacks.size(), dir.GetPath());
Index: lldb/source/Interpreter/CommandInterpreter.cpp
===================================================================
--- lldb/source/Interpreter/CommandInterpreter.cpp
+++ lldb/source/Interpreter/CommandInterpreter.cpp
@@ -15,6 +15,7 @@
 #include "Commands/CommandObjectApropos.h"
 #include "Commands/CommandObjectBreakpoint.h"
 #include "Commands/CommandObjectCommands.h"
+#include "Commands/CommandObjectDiagnostics.h"
 #include "Commands/CommandObjectDisassemble.h"
 #include "Commands/CommandObjectExpression.h"
 #include "Commands/CommandObjectFrame.h"
@@ -518,6 +519,7 @@
   REGISTER_COMMAND_OBJECT("apropos", CommandObjectApropos);
   REGISTER_COMMAND_OBJECT("breakpoint", CommandObjectMultiwordBreakpoint);
   REGISTER_COMMAND_OBJECT("command", CommandObjectMultiwordCommands);
+  REGISTER_COMMAND_OBJECT("diagnostics", CommandObjectDiagnostics);
   REGISTER_COMMAND_OBJECT("disassemble", CommandObjectDisassemble);
   REGISTER_COMMAND_OBJECT("expression", CommandObjectExpression);
   REGISTER_COMMAND_OBJECT("frame", CommandObjectMultiwordFrame);
Index: lldb/source/Commands/Options.td
===================================================================
--- lldb/source/Commands/Options.td
+++ lldb/source/Commands/Options.td
@@ -343,6 +343,11 @@
     Desc<"Force disassembly of large functions.">;
 }
 
+let Command = "diagnostics dump" in {
+  def diagnostics_dump_directory : Option<"directory", "d">, Group<1>,
+    Arg<"Path">, Desc<"Dump the diagnostics to the given directory.">;
+}
+
 let Command = "expression" in {
   def expression_options_all_threads : Option<"all-threads", "a">,
     Groups<[1,2]>, Arg<"Boolean">, Desc<"Should we run all threads if the "
Index: lldb/source/Commands/CommandObjectDiagnostics.h
===================================================================
--- /dev/null
+++ lldb/source/Commands/CommandObjectDiagnostics.h
@@ -0,0 +1,29 @@
+//===-- CommandObjectDiagnostics.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_COMMANDOBJECTDIAGNOSTICS_H
+#define LLDB_SOURCE_COMMANDS_COMMANDOBJECTDIAGNOSTICS_H
+
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+
+namespace lldb_private {
+
+class CommandObjectDiagnostics : public CommandObjectMultiword {
+public:
+  CommandObjectDiagnostics(CommandInterpreter &interpreter);
+  ~CommandObjectDiagnostics() override;
+
+private:
+  CommandObjectDiagnostics(const CommandObjectDiagnostics &) = delete;
+  const CommandObjectDiagnostics &
+  operator=(const CommandObjectDiagnostics &) = delete;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_COMMANDS_COMMANDOBJECTDIAGNOSTICS_H
Index: lldb/source/Commands/CommandObjectDiagnostics.cpp
===================================================================
--- /dev/null
+++ lldb/source/Commands/CommandObjectDiagnostics.cpp
@@ -0,0 +1,114 @@
+//===-- CommandObjectDiagnostics.cpp --------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectDiagnostics.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandOptionArgumentTable.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Interpreter/OptionValueEnumeration.h"
+#include "lldb/Interpreter/OptionValueUInt64.h"
+#include "lldb/Interpreter/Options.h"
+#include "lldb/Utility/Diagnostics.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+#define LLDB_OPTIONS_diagnostics_dump
+#include "CommandOptions.inc"
+
+class CommandObjectDiagnosticsDump : public CommandObjectParsed {
+public:
+  // Constructors and Destructors
+  CommandObjectDiagnosticsDump(CommandInterpreter &interpreter)
+      : CommandObjectParsed(interpreter, "diagnostics dump",
+                            "Dump diagnostics to disk", nullptr) {}
+
+  ~CommandObjectDiagnosticsDump() override = default;
+
+  class CommandOptions : public Options {
+  public:
+    CommandOptions() = default;
+
+    ~CommandOptions() override = default;
+
+    Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+                          ExecutionContext *execution_context) override {
+      Status error;
+      const int short_option = m_getopt_table[option_idx].val;
+
+      switch (short_option) {
+      case 'd':
+        directory.SetDirectory(option_arg);
+        break;
+      default:
+        llvm_unreachable("Unimplemented option");
+      }
+      return error;
+    }
+
+    void OptionParsingStarting(ExecutionContext *execution_context) override {
+      directory.Clear();
+    }
+
+    llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+      return llvm::makeArrayRef(g_diagnostics_dump_options);
+    }
+
+    FileSpec directory;
+  };
+
+  Options *GetOptions() override { return &m_options; }
+
+protected:
+  llvm::Expected<FileSpec> GetDirectory() {
+    if (m_options.directory) {
+      auto ec =
+          llvm::sys::fs::create_directories(m_options.directory.GetPath());
+      if (ec)
+        return llvm::errorCodeToError(ec);
+      return m_options.directory;
+    }
+    return Diagnostics::CreateUniqueDirectory();
+  }
+
+  bool DoExecute(Args &args, CommandReturnObject &result) override {
+    llvm::Expected<FileSpec> directory = GetDirectory();
+
+    if (!directory) {
+      result.AppendError(llvm::toString(directory.takeError()));
+      result.SetStatus(eReturnStatusFailed);
+      return result.Succeeded();
+    }
+
+    llvm::Error error = Diagnostics::Instance().Create(*directory);
+    if (error) {
+      result.AppendError(llvm::toString(std::move(error)));
+      result.SetStatus(eReturnStatusFailed);
+      return result.Succeeded();
+    }
+
+    result.GetOutputStream() << "diagnostics written to " << *directory << '\n';
+
+    result.SetStatus(eReturnStatusSuccessFinishResult);
+    return result.Succeeded();
+  }
+
+  CommandOptions m_options;
+};
+
+CommandObjectDiagnostics::CommandObjectDiagnostics(
+    CommandInterpreter &interpreter)
+    : CommandObjectMultiword(interpreter, "log",
+                             "Commands controlling LLDB diagnostics.",
+                             "diagnostics <subcommand> [<command-options>]") {
+  LoadSubCommand(
+      "dump", CommandObjectSP(new CommandObjectDiagnosticsDump(interpreter)));
+}
+
+CommandObjectDiagnostics::~CommandObjectDiagnostics() = default;
Index: lldb/source/Commands/CMakeLists.txt
===================================================================
--- lldb/source/Commands/CMakeLists.txt
+++ lldb/source/Commands/CMakeLists.txt
@@ -8,6 +8,7 @@
   CommandObjectBreakpoint.cpp
   CommandObjectBreakpointCommand.cpp
   CommandObjectCommands.cpp
+  CommandObjectDiagnostics.cpp
   CommandObjectDisassemble.cpp
   CommandObjectExpression.cpp
   CommandObjectFrame.cpp
Index: lldb/include/lldb/Utility/Diagnostics.h
===================================================================
--- lldb/include/lldb/Utility/Diagnostics.h
+++ lldb/include/lldb/Utility/Diagnostics.h
@@ -44,6 +44,9 @@
   static void Initialize();
   static void Terminate();
 
+  /// Create a unique diagnostic directory.
+  static llvm::Expected<FileSpec> CreateUniqueDirectory();
+
 private:
   static llvm::Optional<Diagnostics> &InstanceImpl();
   static const size_t g_log_messages;
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to