1 //===-- StreamTee.h ------------------------------------------*- 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 #ifndef LLDB_UTILITY_STREAMTEE_H 10 #define LLDB_UTILITY_STREAMTEE_H 11 12 #include <climits> 13 14 #include <mutex> 15 16 #include "lldb/Utility/Stream.h" 17 18 namespace lldb_private { 19 20 class StreamTee : public Stream { 21 public: Stream(colors)22 StreamTee(bool colors = false) : Stream(colors) {} 23 StreamTee(lldb::StreamSP & stream_sp)24 StreamTee(lldb::StreamSP &stream_sp) { 25 // No need to lock mutex during construction 26 if (stream_sp) 27 m_streams.push_back(stream_sp); 28 } 29 StreamTee(lldb::StreamSP & stream_sp,lldb::StreamSP & stream_2_sp)30 StreamTee(lldb::StreamSP &stream_sp, lldb::StreamSP &stream_2_sp) { 31 // No need to lock mutex during construction 32 if (stream_sp) 33 m_streams.push_back(stream_sp); 34 if (stream_2_sp) 35 m_streams.push_back(stream_2_sp); 36 } 37 StreamTee(const StreamTee & rhs)38 StreamTee(const StreamTee &rhs) : Stream(rhs) { 39 // Don't copy until we lock down "rhs" 40 std::lock_guard<std::recursive_mutex> guard(rhs.m_streams_mutex); 41 m_streams = rhs.m_streams; 42 } 43 44 ~StreamTee() override = default; 45 46 StreamTee &operator=(const StreamTee &rhs) { 47 if (this != &rhs) { 48 Stream::operator=(rhs); 49 std::lock(m_streams_mutex, rhs.m_streams_mutex); 50 std::lock_guard<std::recursive_mutex> lhs_locker(m_streams_mutex, 51 std::adopt_lock); 52 std::lock_guard<std::recursive_mutex> rhs_locker(rhs.m_streams_mutex, 53 std::adopt_lock); 54 m_streams = rhs.m_streams; 55 } 56 return *this; 57 } 58 Flush()59 void Flush() override { 60 std::lock_guard<std::recursive_mutex> guard(m_streams_mutex); 61 collection::iterator pos, end; 62 for (pos = m_streams.begin(), end = m_streams.end(); pos != end; ++pos) { 63 // Allow for our collection to contain NULL streams. This allows the 64 // StreamTee to be used with hard coded indexes for clients that might 65 // want N total streams with only a few that are set to valid values. 66 Stream *strm = pos->get(); 67 if (strm) 68 strm->Flush(); 69 } 70 } 71 AppendStream(const lldb::StreamSP & stream_sp)72 size_t AppendStream(const lldb::StreamSP &stream_sp) { 73 size_t new_idx = m_streams.size(); 74 std::lock_guard<std::recursive_mutex> guard(m_streams_mutex); 75 m_streams.push_back(stream_sp); 76 return new_idx; 77 } 78 GetNumStreams()79 size_t GetNumStreams() const { 80 size_t result = 0; 81 { 82 std::lock_guard<std::recursive_mutex> guard(m_streams_mutex); 83 result = m_streams.size(); 84 } 85 return result; 86 } 87 GetStreamAtIndex(uint32_t idx)88 lldb::StreamSP GetStreamAtIndex(uint32_t idx) { 89 lldb::StreamSP stream_sp; 90 std::lock_guard<std::recursive_mutex> guard(m_streams_mutex); 91 if (idx < m_streams.size()) 92 stream_sp = m_streams[idx]; 93 return stream_sp; 94 } 95 SetStreamAtIndex(uint32_t idx,const lldb::StreamSP & stream_sp)96 void SetStreamAtIndex(uint32_t idx, const lldb::StreamSP &stream_sp) { 97 std::lock_guard<std::recursive_mutex> guard(m_streams_mutex); 98 // Resize our stream vector as necessary to fit as many streams as needed. 99 // This also allows this class to be used with hard coded indexes that can 100 // be used contain many streams, not all of which are valid. 101 if (idx >= m_streams.size()) 102 m_streams.resize(idx + 1); 103 m_streams[idx] = stream_sp; 104 } 105 106 protected: 107 typedef std::vector<lldb::StreamSP> collection; 108 mutable std::recursive_mutex m_streams_mutex; 109 collection m_streams; 110 WriteImpl(const void * s,size_t length)111 size_t WriteImpl(const void *s, size_t length) override { 112 std::lock_guard<std::recursive_mutex> guard(m_streams_mutex); 113 if (m_streams.empty()) 114 return 0; 115 116 size_t min_bytes_written = SIZE_MAX; 117 collection::iterator pos, end; 118 for (pos = m_streams.begin(), end = m_streams.end(); pos != end; ++pos) { 119 // Allow for our collection to contain NULL streams. This allows the 120 // StreamTee to be used with hard coded indexes for clients that might 121 // want N total streams with only a few that are set to valid values. 122 Stream *strm = pos->get(); 123 if (strm) { 124 const size_t bytes_written = strm->Write(s, length); 125 if (min_bytes_written > bytes_written) 126 min_bytes_written = bytes_written; 127 } 128 } 129 if (min_bytes_written == SIZE_MAX) 130 return 0; 131 return min_bytes_written; 132 } 133 }; 134 135 } // namespace lldb_private 136 137 #endif // LLDB_UTILITY_STREAMTEE_H 138