1 //===-- SBStream.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/API/SBStream.h" 10 11 #include "SBReproducerPrivate.h" 12 #include "lldb/API/SBFile.h" 13 #include "lldb/Core/StreamFile.h" 14 #include "lldb/Host/FileSystem.h" 15 #include "lldb/Utility/Status.h" 16 #include "lldb/Utility/Stream.h" 17 #include "lldb/Utility/StreamString.h" 18 19 using namespace lldb; 20 using namespace lldb_private; 21 22 SBStream::SBStream() : m_opaque_up(new StreamString()), m_is_file(false) { 23 LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBStream); 24 } 25 26 SBStream::SBStream(SBStream &&rhs) 27 : m_opaque_up(std::move(rhs.m_opaque_up)), m_is_file(rhs.m_is_file) {} 28 29 SBStream::~SBStream() = default; 30 31 bool SBStream::IsValid() const { 32 LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBStream, IsValid); 33 return this->operator bool(); 34 } 35 SBStream::operator bool() const { 36 LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBStream, operator bool); 37 38 return (m_opaque_up != nullptr); 39 } 40 41 // If this stream is not redirected to a file, it will maintain a local cache 42 // for the stream data which can be accessed using this accessor. 43 const char *SBStream::GetData() { 44 LLDB_RECORD_METHOD_NO_ARGS(const char *, SBStream, GetData); 45 46 if (m_is_file || m_opaque_up == nullptr) 47 return nullptr; 48 49 return static_cast<StreamString *>(m_opaque_up.get())->GetData(); 50 } 51 52 // If this stream is not redirected to a file, it will maintain a local cache 53 // for the stream output whose length can be accessed using this accessor. 54 size_t SBStream::GetSize() { 55 LLDB_RECORD_METHOD_NO_ARGS(size_t, SBStream, GetSize); 56 57 if (m_is_file || m_opaque_up == nullptr) 58 return 0; 59 60 return static_cast<StreamString *>(m_opaque_up.get())->GetSize(); 61 } 62 63 void SBStream::Printf(const char *format, ...) { 64 if (!format) 65 return; 66 va_list args; 67 va_start(args, format); 68 ref().PrintfVarArg(format, args); 69 va_end(args); 70 } 71 72 void SBStream::RedirectToFile(const char *path, bool append) { 73 LLDB_RECORD_METHOD(void, SBStream, RedirectToFile, (const char *, bool), path, 74 append); 75 76 if (path == nullptr) 77 return; 78 79 std::string local_data; 80 if (m_opaque_up) { 81 // See if we have any locally backed data. If so, copy it so we can then 82 // redirect it to the file so we don't lose the data 83 if (!m_is_file) 84 local_data = std::string( 85 static_cast<StreamString *>(m_opaque_up.get())->GetString()); 86 } 87 auto open_options = File::eOpenOptionWrite | File::eOpenOptionCanCreate; 88 if (append) 89 open_options |= File::eOpenOptionAppend; 90 else 91 open_options |= File::eOpenOptionTruncate; 92 93 llvm::Expected<FileUP> file = 94 FileSystem::Instance().Open(FileSpec(path), open_options); 95 if (!file) { 96 LLDB_LOG_ERROR(GetLogIfAllCategoriesSet(LIBLLDB_LOG_API), file.takeError(), 97 "Cannot open {1}: {0}", path); 98 return; 99 } 100 101 m_opaque_up = std::make_unique<StreamFile>(std::move(file.get())); 102 m_is_file = true; 103 104 // If we had any data locally in our StreamString, then pass that along to 105 // the to new file we are redirecting to. 106 if (!local_data.empty()) 107 m_opaque_up->Write(&local_data[0], local_data.size()); 108 } 109 110 void SBStream::RedirectToFileHandle(FILE *fh, bool transfer_fh_ownership) { 111 LLDB_RECORD_METHOD(void, SBStream, RedirectToFileHandle, (FILE *, bool), fh, 112 transfer_fh_ownership); 113 FileSP file = std::make_unique<NativeFile>(fh, transfer_fh_ownership); 114 return RedirectToFile(file); 115 } 116 117 void SBStream::RedirectToFile(SBFile file) { 118 LLDB_RECORD_METHOD(void, SBStream, RedirectToFile, (SBFile), file) 119 RedirectToFile(file.GetFile()); 120 } 121 122 void SBStream::RedirectToFile(FileSP file_sp) { 123 LLDB_RECORD_METHOD(void, SBStream, RedirectToFile, (FileSP), file_sp); 124 125 if (!file_sp || !file_sp->IsValid()) 126 return; 127 128 std::string local_data; 129 if (m_opaque_up) { 130 // See if we have any locally backed data. If so, copy it so we can then 131 // redirect it to the file so we don't lose the data 132 if (!m_is_file) 133 local_data = std::string( 134 static_cast<StreamString *>(m_opaque_up.get())->GetString()); 135 } 136 137 m_opaque_up = std::make_unique<StreamFile>(file_sp); 138 m_is_file = true; 139 140 // If we had any data locally in our StreamString, then pass that along to 141 // the to new file we are redirecting to. 142 if (!local_data.empty()) 143 m_opaque_up->Write(&local_data[0], local_data.size()); 144 } 145 146 void SBStream::RedirectToFileDescriptor(int fd, bool transfer_fh_ownership) { 147 LLDB_RECORD_METHOD(void, SBStream, RedirectToFileDescriptor, (int, bool), fd, 148 transfer_fh_ownership); 149 150 std::string local_data; 151 if (m_opaque_up) { 152 // See if we have any locally backed data. If so, copy it so we can then 153 // redirect it to the file so we don't lose the data 154 if (!m_is_file) 155 local_data = std::string( 156 static_cast<StreamString *>(m_opaque_up.get())->GetString()); 157 } 158 159 m_opaque_up = std::make_unique<StreamFile>(fd, transfer_fh_ownership); 160 m_is_file = true; 161 162 // If we had any data locally in our StreamString, then pass that along to 163 // the to new file we are redirecting to. 164 if (!local_data.empty()) 165 m_opaque_up->Write(&local_data[0], local_data.size()); 166 } 167 168 lldb_private::Stream *SBStream::operator->() { return m_opaque_up.get(); } 169 170 lldb_private::Stream *SBStream::get() { return m_opaque_up.get(); } 171 172 lldb_private::Stream &SBStream::ref() { 173 if (m_opaque_up == nullptr) 174 m_opaque_up.reset(new StreamString()); 175 return *m_opaque_up; 176 } 177 178 void SBStream::Clear() { 179 LLDB_RECORD_METHOD_NO_ARGS(void, SBStream, Clear); 180 181 if (m_opaque_up) { 182 // See if we have any locally backed data. If so, copy it so we can then 183 // redirect it to the file so we don't lose the data 184 if (m_is_file) 185 m_opaque_up.reset(); 186 else 187 static_cast<StreamString *>(m_opaque_up.get())->Clear(); 188 } 189 } 190 191 namespace lldb_private { 192 namespace repro { 193 194 template <> 195 void RegisterMethods<SBStream>(Registry &R) { 196 LLDB_REGISTER_CONSTRUCTOR(SBStream, ()); 197 LLDB_REGISTER_METHOD_CONST(bool, SBStream, IsValid, ()); 198 LLDB_REGISTER_METHOD_CONST(bool, SBStream, operator bool, ()); 199 LLDB_REGISTER_METHOD(const char *, SBStream, GetData, ()); 200 LLDB_REGISTER_METHOD(size_t, SBStream, GetSize, ()); 201 LLDB_REGISTER_METHOD(void, SBStream, RedirectToFile, (const char *, bool)); 202 LLDB_REGISTER_METHOD(void, SBStream, RedirectToFile, (FileSP)); 203 LLDB_REGISTER_METHOD(void, SBStream, RedirectToFile, (SBFile)); 204 LLDB_REGISTER_METHOD(void, SBStream, RedirectToFileHandle, (FILE *, bool)); 205 LLDB_REGISTER_METHOD(void, SBStream, RedirectToFileDescriptor, (int, bool)); 206 LLDB_REGISTER_METHOD(void, SBStream, Clear, ()); 207 } 208 209 } 210 } 211