SBStream.cpp revision 360784
1//===-- SBStream.cpp ----------------------------------------*- C++ -*-===//
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
19using namespace lldb;
20using namespace lldb_private;
21
22SBStream::SBStream() : m_opaque_up(new StreamString()), m_is_file(false) {
23  LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBStream);
24}
25
26SBStream::SBStream(SBStream &&rhs)
27    : m_opaque_up(std::move(rhs.m_opaque_up)), m_is_file(rhs.m_is_file) {}
28
29SBStream::~SBStream() {}
30
31bool SBStream::IsValid() const {
32  LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBStream, IsValid);
33  return this->operator bool();
34}
35SBStream::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.
43const 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.
54size_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
63void 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
72void 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 = static_cast<StreamString *>(m_opaque_up.get())->GetString();
85  }
86  auto open_options = File::eOpenOptionWrite | File::eOpenOptionCanCreate;
87  if (append)
88    open_options |= File::eOpenOptionAppend;
89  else
90    open_options |= File::eOpenOptionTruncate;
91
92  llvm::Expected<FileUP> file =
93      FileSystem::Instance().Open(FileSpec(path), open_options);
94  if (!file) {
95    LLDB_LOG_ERROR(GetLogIfAllCategoriesSet(LIBLLDB_LOG_API), file.takeError(),
96                   "Cannot open {1}: {0}", path);
97    return;
98  }
99
100  m_opaque_up = std::make_unique<StreamFile>(std::move(file.get()));
101  m_is_file = true;
102
103  // If we had any data locally in our StreamString, then pass that along to
104  // the to new file we are redirecting to.
105  if (!local_data.empty())
106    m_opaque_up->Write(&local_data[0], local_data.size());
107}
108
109void SBStream::RedirectToFileHandle(FILE *fh, bool transfer_fh_ownership) {
110  LLDB_RECORD_METHOD(void, SBStream, RedirectToFileHandle, (FILE *, bool), fh,
111                     transfer_fh_ownership);
112  FileSP file = std::make_unique<NativeFile>(fh, transfer_fh_ownership);
113  return RedirectToFile(file);
114}
115
116void SBStream::RedirectToFile(SBFile file) {
117  LLDB_RECORD_METHOD(void, SBStream, RedirectToFile, (SBFile), file)
118  RedirectToFile(file.GetFile());
119}
120
121void SBStream::RedirectToFile(FileSP file_sp) {
122  LLDB_RECORD_METHOD(void, SBStream, RedirectToFile, (FileSP), file_sp);
123
124  if (!file_sp || !file_sp->IsValid())
125    return;
126
127  std::string local_data;
128  if (m_opaque_up) {
129    // See if we have any locally backed data. If so, copy it so we can then
130    // redirect it to the file so we don't lose the data
131    if (!m_is_file)
132      local_data = static_cast<StreamString *>(m_opaque_up.get())->GetString();
133  }
134
135  m_opaque_up = std::make_unique<StreamFile>(file_sp);
136  m_is_file = true;
137
138  // If we had any data locally in our StreamString, then pass that along to
139  // the to new file we are redirecting to.
140  if (!local_data.empty())
141    m_opaque_up->Write(&local_data[0], local_data.size());
142}
143
144void SBStream::RedirectToFileDescriptor(int fd, bool transfer_fh_ownership) {
145  LLDB_RECORD_METHOD(void, SBStream, RedirectToFileDescriptor, (int, bool), fd,
146                     transfer_fh_ownership);
147
148  std::string local_data;
149  if (m_opaque_up) {
150    // See if we have any locally backed data. If so, copy it so we can then
151    // redirect it to the file so we don't lose the data
152    if (!m_is_file)
153      local_data = static_cast<StreamString *>(m_opaque_up.get())->GetString();
154  }
155
156  m_opaque_up = std::make_unique<StreamFile>(fd, transfer_fh_ownership);
157  m_is_file = true;
158
159  // If we had any data locally in our StreamString, then pass that along to
160  // the to new file we are redirecting to.
161  if (!local_data.empty())
162    m_opaque_up->Write(&local_data[0], local_data.size());
163}
164
165lldb_private::Stream *SBStream::operator->() { return m_opaque_up.get(); }
166
167lldb_private::Stream *SBStream::get() { return m_opaque_up.get(); }
168
169lldb_private::Stream &SBStream::ref() {
170  if (m_opaque_up == nullptr)
171    m_opaque_up.reset(new StreamString());
172  return *m_opaque_up;
173}
174
175void SBStream::Clear() {
176  LLDB_RECORD_METHOD_NO_ARGS(void, SBStream, Clear);
177
178  if (m_opaque_up) {
179    // See if we have any locally backed data. If so, copy it so we can then
180    // redirect it to the file so we don't lose the data
181    if (m_is_file)
182      m_opaque_up.reset();
183    else
184      static_cast<StreamString *>(m_opaque_up.get())->Clear();
185  }
186}
187
188namespace lldb_private {
189namespace repro {
190
191template <>
192void RegisterMethods<SBStream>(Registry &R) {
193  LLDB_REGISTER_CONSTRUCTOR(SBStream, ());
194  LLDB_REGISTER_METHOD_CONST(bool, SBStream, IsValid, ());
195  LLDB_REGISTER_METHOD_CONST(bool, SBStream, operator bool, ());
196  LLDB_REGISTER_METHOD(const char *, SBStream, GetData, ());
197  LLDB_REGISTER_METHOD(size_t, SBStream, GetSize, ());
198  LLDB_REGISTER_METHOD(void, SBStream, RedirectToFile, (const char *, bool));
199  LLDB_REGISTER_METHOD(void, SBStream, RedirectToFile, (FileSP));
200  LLDB_REGISTER_METHOD(void, SBStream, RedirectToFile, (SBFile));
201  LLDB_REGISTER_METHOD(void, SBStream, RedirectToFileHandle, (FILE *, bool));
202  LLDB_REGISTER_METHOD(void, SBStream, RedirectToFileDescriptor, (int, bool));
203  LLDB_REGISTER_METHOD(void, SBStream, Clear, ());
204}
205
206}
207}
208