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::Print(const char *str) { 64 LLDB_RECORD_METHOD(void, SBStream, Print, (const char *), str); 65 66 Printf("%s", str); 67 } 68 69 void SBStream::Printf(const char *format, ...) { 70 if (!format) 71 return; 72 va_list args; 73 va_start(args, format); 74 ref().PrintfVarArg(format, args); 75 va_end(args); 76 } 77 78 void SBStream::RedirectToFile(const char *path, bool append) { 79 LLDB_RECORD_METHOD(void, SBStream, RedirectToFile, (const char *, bool), path, 80 append); 81 82 if (path == nullptr) 83 return; 84 85 std::string local_data; 86 if (m_opaque_up) { 87 // See if we have any locally backed data. If so, copy it so we can then 88 // redirect it to the file so we don't lose the data 89 if (!m_is_file) 90 local_data = std::string( 91 static_cast<StreamString *>(m_opaque_up.get())->GetString()); 92 } 93 auto open_options = File::eOpenOptionWrite | File::eOpenOptionCanCreate; 94 if (append) 95 open_options |= File::eOpenOptionAppend; 96 else 97 open_options |= File::eOpenOptionTruncate; 98 99 llvm::Expected<FileUP> file = 100 FileSystem::Instance().Open(FileSpec(path), open_options); 101 if (!file) { 102 LLDB_LOG_ERROR(GetLogIfAllCategoriesSet(LIBLLDB_LOG_API), file.takeError(), 103 "Cannot open {1}: {0}", path); 104 return; 105 } 106 107 m_opaque_up = std::make_unique<StreamFile>(std::move(file.get())); 108 m_is_file = true; 109 110 // If we had any data locally in our StreamString, then pass that along to 111 // the to new file we are redirecting to. 112 if (!local_data.empty()) 113 m_opaque_up->Write(&local_data[0], local_data.size()); 114 } 115 116 void SBStream::RedirectToFileHandle(FILE *fh, bool transfer_fh_ownership) { 117 LLDB_RECORD_METHOD(void, SBStream, RedirectToFileHandle, (FILE *, bool), fh, 118 transfer_fh_ownership); 119 FileSP file = std::make_unique<NativeFile>(fh, transfer_fh_ownership); 120 return RedirectToFile(file); 121 } 122 123 void SBStream::RedirectToFile(SBFile file) { 124 LLDB_RECORD_METHOD(void, SBStream, RedirectToFile, (SBFile), file) 125 RedirectToFile(file.GetFile()); 126 } 127 128 void SBStream::RedirectToFile(FileSP file_sp) { 129 LLDB_RECORD_METHOD(void, SBStream, RedirectToFile, (FileSP), file_sp); 130 131 if (!file_sp || !file_sp->IsValid()) 132 return; 133 134 std::string local_data; 135 if (m_opaque_up) { 136 // See if we have any locally backed data. If so, copy it so we can then 137 // redirect it to the file so we don't lose the data 138 if (!m_is_file) 139 local_data = std::string( 140 static_cast<StreamString *>(m_opaque_up.get())->GetString()); 141 } 142 143 m_opaque_up = std::make_unique<StreamFile>(file_sp); 144 m_is_file = true; 145 146 // If we had any data locally in our StreamString, then pass that along to 147 // the to new file we are redirecting to. 148 if (!local_data.empty()) 149 m_opaque_up->Write(&local_data[0], local_data.size()); 150 } 151 152 void SBStream::RedirectToFileDescriptor(int fd, bool transfer_fh_ownership) { 153 LLDB_RECORD_METHOD(void, SBStream, RedirectToFileDescriptor, (int, bool), fd, 154 transfer_fh_ownership); 155 156 std::string local_data; 157 if (m_opaque_up) { 158 // See if we have any locally backed data. If so, copy it so we can then 159 // redirect it to the file so we don't lose the data 160 if (!m_is_file) 161 local_data = std::string( 162 static_cast<StreamString *>(m_opaque_up.get())->GetString()); 163 } 164 165 m_opaque_up = std::make_unique<StreamFile>(fd, transfer_fh_ownership); 166 m_is_file = true; 167 168 // If we had any data locally in our StreamString, then pass that along to 169 // the to new file we are redirecting to. 170 if (!local_data.empty()) 171 m_opaque_up->Write(&local_data[0], local_data.size()); 172 } 173 174 lldb_private::Stream *SBStream::operator->() { return m_opaque_up.get(); } 175 176 lldb_private::Stream *SBStream::get() { return m_opaque_up.get(); } 177 178 lldb_private::Stream &SBStream::ref() { 179 if (m_opaque_up == nullptr) 180 m_opaque_up = std::make_unique<StreamString>(); 181 return *m_opaque_up; 182 } 183 184 void SBStream::Clear() { 185 LLDB_RECORD_METHOD_NO_ARGS(void, SBStream, Clear); 186 187 if (m_opaque_up) { 188 // See if we have any locally backed data. If so, copy it so we can then 189 // redirect it to the file so we don't lose the data 190 if (m_is_file) 191 m_opaque_up.reset(); 192 else 193 static_cast<StreamString *>(m_opaque_up.get())->Clear(); 194 } 195 } 196 197 namespace lldb_private { 198 namespace repro { 199 200 template <> 201 void RegisterMethods<SBStream>(Registry &R) { 202 LLDB_REGISTER_CONSTRUCTOR(SBStream, ()); 203 LLDB_REGISTER_METHOD_CONST(bool, SBStream, IsValid, ()); 204 LLDB_REGISTER_METHOD_CONST(bool, SBStream, operator bool, ()); 205 LLDB_REGISTER_METHOD(const char *, SBStream, GetData, ()); 206 LLDB_REGISTER_METHOD(size_t, SBStream, GetSize, ()); 207 LLDB_REGISTER_METHOD(void, SBStream, RedirectToFile, (const char *, bool)); 208 LLDB_REGISTER_METHOD(void, SBStream, RedirectToFile, (FileSP)); 209 LLDB_REGISTER_METHOD(void, SBStream, RedirectToFile, (SBFile)); 210 LLDB_REGISTER_METHOD(void, SBStream, RedirectToFileHandle, (FILE *, bool)); 211 LLDB_REGISTER_METHOD(void, SBStream, RedirectToFileDescriptor, (int, bool)); 212 LLDB_REGISTER_METHOD(void, SBStream, Clear, ()); 213 LLDB_REGISTER_METHOD(void, SBStream, Print, (const char *)); 214 } 215 216 } 217 } 218