Add new bugreport command to lldb

The new command add functionality to print out domain specific
information for reporting a bug. Currently the only supported
domain is stack unwinding (with "bugreport unwind") but adding
new domains is fairly easy.

Differential revision: http://reviews.llvm.org/D10868

llvm-svn: 241252
diff --git a/lldb/source/Commands/CommandObjectBugreport.cpp b/lldb/source/Commands/CommandObjectBugreport.cpp
new file mode 100644
index 0000000..f171d2f
--- /dev/null
+++ b/lldb/source/Commands/CommandObjectBugreport.cpp
@@ -0,0 +1,145 @@
+//===-- CommandObjectBugreport.cpp ------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectBugreport.h"
+
+// C Includes
+#include <cstdio>
+
+// C++ Includes
+// Other libraries and framework includes
+
+// Project includes
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionGroupOutputFile.h"
+#include "lldb/Target/Thread.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+//-------------------------------------------------------------------------
+// "bugreport unwind"
+//-------------------------------------------------------------------------
+
+class CommandObjectBugreportUnwind : public CommandObjectParsed
+{
+public:
+    CommandObjectBugreportUnwind(CommandInterpreter &interpreter) :
+        CommandObjectParsed(interpreter,
+                            "bugreport unwind",
+                            "Create a bugreport for a bug in the stack unwinding code.",
+                            nullptr),
+        m_option_group(interpreter),
+        m_outfile_options()
+    {
+        m_option_group.Append (&m_outfile_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1 | LLDB_OPT_SET_2 | LLDB_OPT_SET_3);
+        m_option_group.Finalize();
+    }
+
+    ~CommandObjectBugreportUnwind()
+    {
+    }
+
+    Options *
+    GetOptions() override
+    {
+        return &m_option_group;
+    }
+
+protected:
+    bool
+    DoExecute(Args& command, CommandReturnObject &result) override
+    {
+        StringList commands;
+        commands.AppendString("thread backtrace");
+
+        Thread *thread = m_exe_ctx.GetThreadPtr();
+        if (thread)
+        {
+            char command_buffer[256];
+
+            uint32_t frame_count = thread->GetStackFrameCount();
+            for (uint32_t i = 0; i < frame_count; ++i)
+            {
+                StackFrameSP frame = thread->GetStackFrameAtIndex(i);
+                lldb::addr_t pc = frame->GetStackID().GetPC();
+
+                snprintf(command_buffer, sizeof(command_buffer), "disassemble --bytes --address 0x%" PRIx64, pc);
+                commands.AppendString(command_buffer);
+
+                snprintf(command_buffer, sizeof(command_buffer), "image show-unwind --address 0x%" PRIx64, pc);
+                commands.AppendString(command_buffer);
+            }
+        }
+
+        const FileSpec &outfile_spec = m_outfile_options.GetFile().GetCurrentValue();
+        if (outfile_spec)
+        {
+            char path[PATH_MAX];
+            outfile_spec.GetPath (path, sizeof(path));
+
+            uint32_t open_options = File::eOpenOptionWrite |
+                                    File::eOpenOptionCanCreate |
+                                    File::eOpenOptionAppend |
+                                    File::eOpenOptionCloseOnExec;
+
+            const bool append = m_outfile_options.GetAppend().GetCurrentValue();
+            if (!append)
+                open_options |= File::eOpenOptionTruncate;
+            
+            StreamFileSP outfile_stream = std::make_shared<StreamFile>();
+            Error error = outfile_stream->GetFile().Open(path, open_options);
+            if (error.Fail())
+            {
+                result.AppendErrorWithFormat("Failed to open file '%s' for %s: %s\n",
+                                             path,
+                                             append ? "append" : "write",
+                                             error.AsCString());
+                result.SetStatus(eReturnStatusFailed);
+                return false;
+            }
+
+            result.SetImmediateOutputStream(outfile_stream);
+        }
+
+        CommandInterpreterRunOptions options;
+        options.SetStopOnError(false);
+        options.SetEchoCommands(true);
+        options.SetPrintResults(true);
+        options.SetAddToHistory(false);
+        m_interpreter.HandleCommands(commands, &m_exe_ctx, options, result);
+
+        return result.Succeeded();
+    }
+
+private:
+    OptionGroupOptions m_option_group;
+    OptionGroupOutputFile m_outfile_options;
+};
+
+#pragma mark CommandObjectMultiwordBugreport
+
+//-------------------------------------------------------------------------
+// CommandObjectMultiwordBugreport
+//-------------------------------------------------------------------------
+
+CommandObjectMultiwordBugreport::CommandObjectMultiwordBugreport(CommandInterpreter &interpreter) :
+    CommandObjectMultiword(interpreter,
+                           "bugreport",
+                           "Set of commands for creating domain specific bugreports.",
+                           "bugreport <subcommand> [<subcommand-options>]")
+{
+
+    LoadSubCommand("unwind", CommandObjectSP(new CommandObjectBugreportUnwind(interpreter)));
+}
+
+CommandObjectMultiwordBugreport::~CommandObjectMultiwordBugreport ()
+{
+}