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
17using namespace lldb_private;
18using namespace lldb;
19using namespace llvm;
20
21static constexpr size_t g_num_log_messages = 100;
22
23void Diagnostics::Initialize() {
24 lldbassert(!InstanceImpl() && "Already initialized.");
25 InstanceImpl().emplace();
26}
27
28void Diagnostics::Terminate() {
29 lldbassert(InstanceImpl() && "Already terminated.");
30 InstanceImpl().reset();
31}
32
33bool Diagnostics::Enabled() { return InstanceImpl().operator bool(); }
34
35std::optional<Diagnostics> &Diagnostics::InstanceImpl() {
36 static std::optional<Diagnostics> g_diagnostics;
37 return g_diagnostics;
38}
39
40Diagnostics &Diagnostics::Instance() { return *InstanceImpl(); }
41
42Diagnostics::Diagnostics() : m_log_handler(g_num_log_messages) {}
43
44Diagnostics::~Diagnostics() {}
45
46Diagnostics::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(Args&: id, Args&: callback);
50 return id;
51}
52
53void Diagnostics::RemoveCallback(CallbackID id) {
54 std::lock_guard<std::mutex> guard(m_callbacks_mutex);
55 llvm::erase_if(C&: m_callbacks,
56 P: [id](const CallbackEntry &e) { return e.id == id; });
57}
58
59bool Diagnostics::Dump(raw_ostream &stream) {
60 Expected<FileSpec> diagnostics_dir = CreateUniqueDirectory();
61 if (!diagnostics_dir) {
62 stream << "unable to create diagnostic dir: "
63 << toString(E: diagnostics_dir.takeError()) << '\n';
64 return false;
65 }
66
67 return Dump(stream, dir: *diagnostics_dir);
68}
69
70bool 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(E: std::move(error)) << '\n';
76 return false;
77 }
78
79 return true;
80}
81
82llvm::Expected<FileSpec> Diagnostics::CreateUniqueDirectory() {
83 SmallString<128> diagnostics_dir;
84 std::error_code ec =
85 sys::fs::createUniqueDirectory(Prefix: "diagnostics", ResultPath&: diagnostics_dir);
86 if (ec)
87 return errorCodeToError(EC: ec);
88 return FileSpec(diagnostics_dir.str());
89}
90
91Error 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
103llvm::Error Diagnostics::DumpDiangosticsLog(const FileSpec &dir) const {
104 FileSpec log_file = dir.CopyByAppendingPathComponent(component: "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: ec);
109 m_log_handler.Dump(stream);
110 return Error::success();
111}
112
113void Diagnostics::Report(llvm::StringRef message) {
114 m_log_handler.Emit(message);
115}
116

source code of lldb/source/Utility/Diagnostics.cpp