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