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