1 //===-- Diagnostics.cpp ---------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "lldb/Utility/Diagnostics.h"
10 #include "lldb/Utility/LLDBAssert.h"
11 
12 #include "llvm/Support/Error.h"
13 #include "llvm/Support/FileSystem.h"
14 #include "llvm/Support/raw_ostream.h"
15 #include <optional>
16 
17 using namespace lldb_private;
18 using namespace lldb;
19 using namespace llvm;
20 
21 static constexpr size_t g_num_log_messages = 100;
22 
23 void Diagnostics::Initialize() {
24   lldbassert(!InstanceImpl() && "Already initialized.");
25   InstanceImpl().emplace();
26 }
27 
28 void Diagnostics::Terminate() {
29   lldbassert(InstanceImpl() && "Already terminated.");
30   InstanceImpl().reset();
31 }
32 
33 bool Diagnostics::Enabled() { return InstanceImpl().operator bool(); }
34 
35 std::optional<Diagnostics> &Diagnostics::InstanceImpl() {
36   static std::optional<Diagnostics> g_diagnostics;
37   return g_diagnostics;
38 }
39 
40 Diagnostics &Diagnostics::Instance() { return *InstanceImpl(); }
41 
42 Diagnostics::Diagnostics() : m_log_handler(g_num_log_messages) {}
43 
44 Diagnostics::~Diagnostics() {}
45 
46 void Diagnostics::AddCallback(Callback callback) {
47   std::lock_guard<std::mutex> guard(m_callbacks_mutex);
48   m_callbacks.push_back(callback);
49 }
50 
51 bool Diagnostics::Dump(raw_ostream &stream) {
52   Expected<FileSpec> diagnostics_dir = CreateUniqueDirectory();
53   if (!diagnostics_dir) {
54     stream << "unable to create diagnostic dir: "
55            << toString(diagnostics_dir.takeError()) << '\n';
56     return false;
57   }
58 
59   return Dump(stream, *diagnostics_dir);
60 }
61 
62 bool Diagnostics::Dump(raw_ostream &stream, const FileSpec &dir) {
63   stream << "LLDB diagnostics will be written to " << dir.GetPath() << "\n";
64   stream << "Please include the directory content when filing a bug report\n";
65 
66   if (Error error = Create(dir)) {
67     stream << toString(std::move(error)) << '\n';
68     return false;
69   }
70 
71   return true;
72 }
73 
74 llvm::Expected<FileSpec> Diagnostics::CreateUniqueDirectory() {
75   SmallString<128> diagnostics_dir;
76   std::error_code ec =
77       sys::fs::createUniqueDirectory("diagnostics", diagnostics_dir);
78   if (ec)
79     return errorCodeToError(ec);
80   return FileSpec(diagnostics_dir.str());
81 }
82 
83 Error Diagnostics::Create(const FileSpec &dir) {
84   if (Error err = DumpDiangosticsLog(dir))
85     return err;
86 
87   for (Callback c : m_callbacks) {
88     if (Error err = c(dir))
89       return err;
90   }
91 
92   return Error::success();
93 }
94 
95 llvm::Error Diagnostics::DumpDiangosticsLog(const FileSpec &dir) const {
96   FileSpec log_file = dir.CopyByAppendingPathComponent("diagnostics.log");
97   std::error_code ec;
98   llvm::raw_fd_ostream stream(log_file.GetPath(), ec, llvm::sys::fs::OF_None);
99   if (ec)
100     return errorCodeToError(ec);
101   m_log_handler.Dump(stream);
102   return Error::success();
103 }
104 
105 void Diagnostics::Report(llvm::StringRef message) {
106   m_log_handler.Emit(message);
107 }
108