1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef INCLUDE_PERFETTO_TRACING_CORE_STARTUP_TRACE_WRITER_H_ 18 #define INCLUDE_PERFETTO_TRACING_CORE_STARTUP_TRACE_WRITER_H_ 19 20 #include <memory> 21 #include <mutex> 22 #include <set> 23 #include <vector> 24 25 #include "perfetto/base/export.h" 26 #include "perfetto/base/optional.h" 27 #include "perfetto/base/thread_checker.h" 28 #include "perfetto/protozero/message_handle.h" 29 #include "perfetto/protozero/scattered_heap_buffer.h" 30 #include "perfetto/protozero/scattered_stream_writer.h" 31 #include "perfetto/tracing/core/basic_types.h" 32 #include "perfetto/tracing/core/trace_writer.h" 33 34 namespace perfetto { 35 36 class SharedMemoryArbiterImpl; 37 class StartupTraceWriterRegistryHandle; 38 39 namespace protos { 40 namespace pbzero { 41 class TracePacket; 42 } // namespace pbzero 43 } // namespace protos 44 45 // Facilitates writing trace events in early phases of an application's startup 46 // when the perfetto service is not available yet. 47 // 48 // Until the service is available, producer threads instantiate an unbound 49 // StartupTraceWriter instance (via a StartupTraceWriterRegistry) and use it to 50 // emit trace events. Each writer will record the serialized trace events into a 51 // temporary local memory buffer. 52 // 53 // Once the service is available, the producer binds each StartupTraceWriter to 54 // the SMB by calling SharedMemoryArbiter::BindStartupTraceWriter(). The data in 55 // the writer's local buffer will then be copied into the SMB and the any future 56 // writes will proxy directly to a new SMB-backed TraceWriter. 57 // 58 // Writing to the temporary local trace buffer is guarded by a lock and flag to 59 // allow binding the writer from a different thread. When the writer starts 60 // writing data by calling NewTracePacket(), the writer thread acquires the lock 61 // to set a flag indicating that a write is in progress. Once the packet is 62 // finalized, the flag is reset. To bind the writer, the lock is acquired while 63 // the flag is unset and released only once binding completed, thereby blocking 64 // the writer thread from starting a write concurrently. 65 // 66 // While unbound, the writer thread should finalize each TracePacket as soon as 67 // possible to ensure that it doesn't block binding the writer. 68 class PERFETTO_EXPORT StartupTraceWriter 69 : public TraceWriter, 70 public protozero::MessageHandleBase::FinalizationListener { 71 public: 72 // Create a StartupTraceWriter bound to |trace_writer|. Should only be called 73 // on the writer thread. 74 explicit StartupTraceWriter(std::unique_ptr<TraceWriter> trace_writer); 75 76 ~StartupTraceWriter() override; 77 78 // TraceWriter implementation. These methods should only be called on the 79 // writer thread. 80 TracePacketHandle NewTracePacket() override; 81 void Flush(std::function<void()> callback = {}) override; 82 83 // Note that this will return 0 until the first TracePacket was started after 84 // binding. 85 WriterID writer_id() const override; 86 87 uint64_t written() const override; 88 89 // Returns |true| if the writer thread has observed that the writer was bound 90 // to an SMB. Should only be called on the writer thread. 91 // 92 // The writer thread can use the return value to determine whether it needs to 93 // finalize the current TracePacket as soon as possible. It is only safe for 94 // the writer to batch data into a single TracePacket over a longer time 95 // period when this returns |true|. was_bound()96 bool was_bound() const { 97 PERFETTO_DCHECK_THREAD(writer_thread_checker_); 98 return was_bound_; 99 } 100 101 // Should only be called on the writer thread. 102 size_t used_buffer_size(); 103 104 private: 105 friend class StartupTraceWriterRegistry; 106 friend class StartupTraceWriterTest; 107 108 // Create an unbound StartupTraceWriter associated with the registry pointed 109 // to by the handle. The writer can later be bound by calling 110 // BindToTraceWriter(). The registry handle may be nullptr in tests. 111 StartupTraceWriter(std::shared_ptr<StartupTraceWriterRegistryHandle>); 112 113 StartupTraceWriter(const StartupTraceWriter&) = delete; 114 StartupTraceWriter& operator=(const StartupTraceWriter&) = delete; 115 116 // Bind this StartupTraceWriter to the provided SharedMemoryArbiterImpl. 117 // Called by StartupTraceWriterRegistry::BindToArbiter(). 118 // 119 // This method can be called on any thread. If any data was written locally 120 // before the writer was bound, BindToArbiter() will copy this data into 121 // chunks in the provided target buffer via the SMB. Any future packets will 122 // be directly written into the SMB via a newly obtained TraceWriter from the 123 // arbiter. 124 // 125 // Will fail and return |false| if a concurrent write is in progress. Returns 126 // |true| if successfully bound and should then not be called again. 127 bool BindToArbiter(SharedMemoryArbiterImpl*, 128 BufferID target_buffer) PERFETTO_WARN_UNUSED_RESULT; 129 130 // protozero::MessageHandleBase::FinalizationListener implementation. 131 void OnMessageFinalized(protozero::Message* message) override; 132 133 void OnTracePacketCompleted(); 134 ChunkID CommitLocalBufferChunks(SharedMemoryArbiterImpl*, WriterID, BufferID); 135 136 PERFETTO_THREAD_CHECKER(writer_thread_checker_) 137 138 std::shared_ptr<StartupTraceWriterRegistryHandle> registry_handle_; 139 140 // Only set and accessed from the writer thread. The writer thread flips this 141 // bit when it sees that trace_writer_ is set (while holding the lock). 142 // Caching this fact in this variable avoids the need to acquire the lock to 143 // check on later calls to NewTracePacket(). 144 bool was_bound_ = false; 145 146 // All variables below this point are protected by |lock_|. 147 std::mutex lock_; 148 149 // Never reset once it is changed from |nullptr|. 150 std::unique_ptr<TraceWriter> trace_writer_ = nullptr; 151 152 // Local memory buffer for trace packets written before the writer is bound. 153 std::unique_ptr<protozero::ScatteredHeapBuffer> memory_buffer_; 154 std::unique_ptr<protozero::ScatteredStreamWriter> memory_stream_writer_; 155 156 std::vector<uint32_t> packet_sizes_; 157 158 // Whether the writer thread is currently writing a TracePacket. 159 bool write_in_progress_ = false; 160 161 // The packet returned via NewTracePacket() while the writer is unbound. Reset 162 // to |nullptr| once bound. Owned by this class, TracePacketHandle has just a 163 // pointer to it. 164 std::unique_ptr<protos::pbzero::TracePacket> cur_packet_; 165 }; 166 167 } // namespace perfetto 168 169 #endif // INCLUDE_PERFETTO_TRACING_CORE_STARTUP_TRACE_WRITER_H_ 170