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