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 Diagnostics::CallbackID Diagnostics::AddCallback(Callback callback) {
47   std::lock_guard<std::mutex> guard(m_callbacks_mutex);
48   CallbackID id = m_callback_id++;
49   m_callbacks.emplace_back(id, callback);
50   return id;
51 }
52 
53 void Diagnostics::RemoveCallback(CallbackID id) {
54   std::lock_guard<std::mutex> guard(m_callbacks_mutex);
55   m_callbacks.erase(
56       std::remove_if(m_callbacks.begin(), m_callbacks.end(),
57                      [id](const CallbackEntry &e) { return e.id == id; }),
58       m_callbacks.end());
59 }
60 
61 bool Diagnostics::Dump(raw_ostream &stream) {
62   Expected<FileSpec> diagnostics_dir = CreateUniqueDirectory();
63   if (!diagnostics_dir) {
64     stream << "unable to create diagnostic dir: "
65            << toString(diagnostics_dir.takeError()) << '\n';
66     return false;
67   }
68 
69   return Dump(stream, *diagnostics_dir);
70 }
71 
72 bool Diagnostics::Dump(raw_ostream &stream, const FileSpec &dir) {
73   stream << "LLDB diagnostics will be written to " << dir.GetPath() << "\n";
74   stream << "Please include the directory content when filing a bug report\n";
75 
76   if (Error error = Create(dir)) {
77     stream << toString(std::move(error)) << '\n';
78     return false;
79   }
80 
81   return true;
82 }
83 
84 llvm::Expected<FileSpec> Diagnostics::CreateUniqueDirectory() {
85   SmallString<128> diagnostics_dir;
86   std::error_code ec =
87       sys::fs::createUniqueDirectory("diagnostics", diagnostics_dir);
88   if (ec)
89     return errorCodeToError(ec);
90   return FileSpec(diagnostics_dir.str());
91 }
92 
93 Error Diagnostics::Create(const FileSpec &dir) {
94   if (Error err = DumpDiangosticsLog(dir))
95     return err;
96 
97   for (CallbackEntry e : m_callbacks) {
98     if (Error err = e.callback(dir))
99       return err;
100   }
101 
102   return Error::success();
103 }
104 
105 llvm::Error Diagnostics::DumpDiangosticsLog(const FileSpec &dir) const {
106   FileSpec log_file = dir.CopyByAppendingPathComponent("diagnostics.log");
107   std::error_code ec;
108   llvm::raw_fd_ostream stream(log_file.GetPath(), ec, llvm::sys::fs::OF_None);
109   if (ec)
110     return errorCodeToError(ec);
111   m_log_handler.Dump(stream);
112   return Error::success();
113 }
114 
115 void Diagnostics::Report(llvm::StringRef message) {
116   m_log_handler.Emit(message);
117 }
118