• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 SRC_TRACING_CORE_TRACE_WRITER_IMPL_H_
18 #define SRC_TRACING_CORE_TRACE_WRITER_IMPL_H_
19 
20 #include <cstdint>
21 #include <functional>
22 #include <memory>
23 
24 #include "perfetto/base/proc_utils.h"
25 #include "perfetto/ext/tracing/core/basic_types.h"
26 #include "perfetto/ext/tracing/core/shared_memory_abi.h"
27 #include "perfetto/ext/tracing/core/shared_memory_arbiter.h"
28 #include "perfetto/ext/tracing/core/trace_writer.h"
29 #include "perfetto/protozero/contiguous_memory_range.h"
30 #include "perfetto/protozero/message_handle.h"
31 #include "perfetto/protozero/root_message.h"
32 #include "perfetto/protozero/scattered_stream_writer.h"
33 #include "perfetto/tracing/buffer_exhausted_policy.h"
34 #include "src/tracing/core/patch_list.h"
35 
36 namespace perfetto {
37 
38 class SharedMemoryArbiterImpl;
39 
40 // See //include/perfetto/ext/tracing/core/trace_writer.h for docs.
41 //
42 // Locking will happen only when a chunk is exhausted and a new one is
43 // acquired from the arbiter.
44 //
45 // TODO: TraceWriter needs to keep the shared memory buffer alive (refcount?).
46 // Otherwise if the shared memory buffer goes away (e.g. the Service crashes)
47 // the TraceWriter will keep writing into unmapped memory.
48 //
49 class TraceWriterImpl : public TraceWriter,
50                         public protozero::MessageFinalizationListener,
51                         public protozero::ScatteredStreamWriter::Delegate {
52  public:
53   // TracePacketHandle is defined in trace_writer.h
54   TraceWriterImpl(SharedMemoryArbiterImpl*,
55                   WriterID,
56                   MaybeUnboundBufferID buffer_id,
57                   BufferExhaustedPolicy);
58   ~TraceWriterImpl() override;
59 
60   // TraceWriter implementation. See documentation in trace_writer.h.
61   TracePacketHandle NewTracePacket() override;
62   void FinishTracePacket() override;
63   // Commits the data pending for the current chunk into the shared memory
64   // buffer and sends a CommitDataRequest() to the service.
65   // TODO(primiano): right now the |callback| will be called on the IPC thread.
66   // This is fine in the current single-thread scenario, but long-term
67   // trace_writer_impl.cc should be smarter and post it on the right thread.
68   void Flush(std::function<void()> callback = {}) override;
69   WriterID writer_id() const override;
written()70   uint64_t written() const override {
71     return protobuf_stream_writer_.written();
72   }
drop_count()73   uint64_t drop_count() const override { return drop_count_; }
74 
drop_packets_for_testing()75   bool drop_packets_for_testing() const { return drop_packets_; }
76 
77  private:
78   TraceWriterImpl(const TraceWriterImpl&) = delete;
79   TraceWriterImpl& operator=(const TraceWriterImpl&) = delete;
80 
81   // ScatteredStreamWriter::Delegate implementation.
82   protozero::ContiguousMemoryRange GetNewBuffer() override;
83   uint8_t* AnnotatePatch(uint8_t*) override;
84 
85   // MessageFinalizationListener implementation.
86   void OnMessageFinalized(protozero::Message*) override;
87 
88   // Writes the size of the current fragment into the chunk.
89   //
90   // The size of nested messages inside TracePacket is written by
91   // by the user, but the size of the TracePacket fragments is written by
92   // TraceWriterImpl.
93   void FinalizeFragmentIfRequired();
94 
95   // Returns |cur_chunk_| (for which is_valid() must be true) to the
96   // |shmem_arbiter|.
97   void ReturnCompletedChunk();
98 
99   // The per-producer arbiter that coordinates access to the shared memory
100   // buffer from several threads.
101   SharedMemoryArbiterImpl* const shmem_arbiter_;
102 
103   // ID of the current writer.
104   const WriterID id_;
105 
106   // This is copied into the commit request by SharedMemoryArbiter. See comments
107   // in data_source_config.proto for |target_buffer|. If this is a reservation
108   // for a buffer ID in case of a startup trace writer, SharedMemoryArbiterImpl
109   // will also translate the reservation ID to the actual buffer ID.
110   const MaybeUnboundBufferID target_buffer_;
111 
112   // Whether GetNewChunk() should stall or return an invalid chunk if the SMB is
113   // exhausted.
114   const BufferExhaustedPolicy buffer_exhausted_policy_;
115 
116   // Monotonic (% wrapping) sequence id of the chunk. Together with the WriterID
117   // this allows the Service to reconstruct the linear sequence of packets.
118   ChunkID next_chunk_id_ = 0;
119 
120   // The chunk we are holding onto (if any).
121   SharedMemoryABI::Chunk cur_chunk_;
122 
123   // Passed to protozero message to write directly into |cur_chunk_|. It
124   // keeps track of the write pointer. It calls us back (GetNewBuffer()) when
125   // |cur_chunk_| is filled.
126   protozero::ScatteredStreamWriter protobuf_stream_writer_;
127 
128   // The packet returned via NewTracePacket(). Its owned by this class,
129   // TracePacketHandle has just a pointer to it.
130   //
131   // The caller of NewTracePacket can use TakeStreamWriter() and use the stream
132   // writer directly: in that case:
133   // * cur_packet_->size() is not up to date. Only the stream writer has the
134   //   correct information.
135   // * cur_packet_->nested_message() is always nullptr.
136   // * cur_packet_->size_field() is still used to track the start of the current
137   //   fragment.
138   std::unique_ptr<protozero::RootMessage<protos::pbzero::TracePacket>>
139       cur_packet_;
140 
141   // The start address of |cur_packet_| within |cur_chunk_|. Used to figure out
142   // fragments sizes when a TracePacket write is interrupted by GetNewBuffer().
143   uint8_t* cur_fragment_start_ = nullptr;
144 
145   // true if we received a call to GetNewBuffer() after NewTracePacket(),
146   // false if GetNewBuffer() happened during NewTracePacket() prologue, while
147   // starting the TracePacket header.
148   bool fragmenting_packet_ = false;
149 
150   // Set to |true| when the current chunk contains the maximum number of packets
151   // a chunk can contain. When this is |true|, the next packet requires starting
152   // a new chunk.
153   bool reached_max_packets_per_chunk_ = false;
154 
155   // If we fail to acquire a new chunk when the arbiter operates in
156   // SharedMemory::BufferExhaustedPolicy::kDrop mode, the trace writer enters a
157   // mode in which data is written to a local garbage chunk and dropped.
158   bool drop_packets_ = false;
159 
160   // Whether the trace writer should try to acquire a new chunk from the SMB
161   // when the next TracePacket is started because it filled the garbage chunk at
162   // least once since the last attempt.
163   bool retry_new_chunk_after_packet_ = false;
164 
165   // Set to true if `cur_chunk_` has a packet counter that's inflated by one.
166   // The count may be inflated to convince the tracing service scraping logic
167   // that the last packet has been completed. When this is true, cur_chunk_
168   // should have at least `kExtraRoomForInflatedPacket` bytes free.
169   bool cur_chunk_packet_count_inflated_ = false;
170 
171   // Points to the size field of the still open fragment we're writing to the
172   // current chunk. If the chunk was already returned, this is reset to
173   // |nullptr|. If the fragment is finalized, this is reset to |nullptr|.
174   //
175   // Note: for nested messages the field is tracked somewhere else
176   // (protozero::Message::size_field_ or PerfettoPbMsg::size_field). For the
177   // root message, protozero::Message::size_field_ is nullptr and this is used
178   // instead. This is because at the root level we deal with fragments, not
179   // logical messages.
180   uint8_t* cur_fragment_size_field_ = nullptr;
181 
182   // When a packet is fragmented across different chunks, the |size_field| of
183   // the outstanding nested protobuf messages is redirected onto Patch entries
184   // in this list at the time the Chunk is returned (because at that point we
185   // have to release the ownership of the current Chunk). This list will be
186   // later sent out-of-band to the tracing service, who will patch the required
187   // chunks, if they are still around.
188   PatchList patch_list_;
189 
190   // PID of the process that created the trace writer. Used for a DCHECK that
191   // aims to detect unsupported process forks while tracing.
192   const base::PlatformProcessId process_id_;
193 
194   // True for the first packet on sequence. See the comment for
195   // TracePacket.first_packet_on_sequence for more details.
196   bool first_packet_on_sequence_ = true;
197 
198   // Number of times the trace writter entered a
199   // SharedMemory::BufferExhaustedPolicy::kDrop mode (i.e. as indicated by the
200   // `drop_packets_` variable). Note that this does *not* necessarily equal the
201   // number of trace packets dropped as multiple packets could have been dropped
202   // in one entry into kDrop mode (i.e. this variable will be a *lower bound*
203   // but *not* an upper bound).
204   uint64_t drop_count_ = 0;
205 };
206 
207 }  // namespace perfetto
208 
209 #endif  // SRC_TRACING_CORE_TRACE_WRITER_IMPL_H_
210