Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(475)

Side by Side Diff: components/tracing/core/trace_buffer_writer.cc

Issue 2164013002: [OBSOLETE] tracing v2: introduce TraceBufferWriter (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@proto_handle
Patch Set: . Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "components/tracing/core/trace_buffer_writer.h"
6
7 #include "base/compiler_specific.h"
8 #include "base/logging.h"
9 #include "components/tracing/core/proto_utils.h"
10
11 namespace tracing {
12 namespace v2 {
13
14 namespace {
15
16 // TODO(primiano) remove this in next CLs. This should just be taken from the
17 // C++ class autogenerated from events_chunk.proto (crbug.com/608721).
18 struct ChunkProto {
19 enum : uint32_t {
20 kWriterIdFieldNumber = 1,
21 kSeqIdInStreamFieldNumber = 2,
22 kEventsFieldNumber = 3,
23 kFirstEventContinuesFromPrevChunkFieldNumber = 4,
24 kLastEventContinuesOnNextChunkFieldNumber = 5
25 };
26 };
27
28 const size_t kEventPreambleSize = 1 + proto::kMessageLengthFieldSize;
29
30 } // namespace
31
32 TraceBufferWriter::TraceBufferWriter(TraceRingBuffer* trace_ring_buffer,
33 uint32_t writer_id)
34 : trace_ring_buffer_(trace_ring_buffer),
35 writer_id_(writer_id),
36 chunk_seq_id_(0),
37 chunk_(nullptr),
38 continue_on_next_chunk_ptr_(nullptr),
39 event_data_start_in_current_chunk_(nullptr),
40 stream_writer_(this) {
41 event_.Reset(&stream_writer_);
42 }
43
44 TraceBufferWriter::~TraceBufferWriter() {}
45
46 TraceEventHandle TraceBufferWriter::AddEvent() {
47 // Finalize the current event. This ensures that it and all its nested fields
48 // are committed and sealed. No further changes to the chunks's memory can be
49 // made from the |event_| after this point.
50 event_.Finalize();
51
52 // In order to start a new event at least kMessageLengthFieldSize + 1 bytes
53 // are required in the chunk to write the preamble and size of the event
54 // itself. We take a bit more room here, it doesn't make a lot of sense
55 // starting a partial event that will fragment immediately after.
56
57 // TODO(primiano): replace 16 with a more reasonable size, that is, the size
58 // of a simple trace event with no args.
59 if (stream_writer_.bytes_available() < 16) {
60 stream_writer_.Reset(AcquireNewChunk(false /* is_fragmenting_event */));
61 }
62
63 // In the unlikely event that the last event did wrap over one or more chunks,
64 // is is not time to return them back to the ring buffer.
65 TraceRingBuffer::Chunk* retained_chunk = chunk_->next_in_owner_list();
66 if (UNLIKELY(retained_chunk)) {
67 while (retained_chunk) {
68 TraceRingBuffer::Chunk* next = retained_chunk->next_in_owner_list();
69 trace_ring_buffer_->ReturnChunk(retained_chunk);
70 retained_chunk = next;
71 }
72 chunk_->set_next_in_owner_list(nullptr);
73 }
74
75 event_.Reset(&stream_writer_);
76 WriteEventPrambleForNewChunk(
77 stream_writer_.ReserveBytesUnsafe(kEventPreambleSize));
78 DCHECK_EQ(stream_writer_.write_ptr(), event_data_start_in_current_chunk_);
79 return TraceEventHandle(static_cast<::tracing::proto::Event*>(&event_));
80 }
81
82 // This is invoked by the ProtoZeroMessage write methods when reaching the
83 // end of the current chunk during a write.
84 ContiguousMemoryRange TraceBufferWriter::GetNewBuffer() {
85 return AcquireNewChunk(true /* is_fragmenting_event */);
86 }
87
88 // There are two ways we can get here:
89 // When |is_fragmenting_event| = false:
90 // AddEvent() is called and there isn't enough room in the current chunk to
91 // start a new event (or we don't have a chunk yet).
92 // When |is_fragmenting_event| = true:
93 // The client is writing an event, a ProtoZeroMessage::Append* method hits
94 // the boundary of the chunk and requests a new one via GetNewBuffer().
95 ContiguousMemoryRange TraceBufferWriter::AcquireNewChunk(
96 bool is_fragmenting_event) {
97 // Finalize the current chunk, if any.
98 if (chunk_) {
99 DCHECK_GE(stream_writer_.write_ptr(), chunk_->payload());
100 DCHECK_LE(stream_writer_.write_ptr(), chunk_->end());
101 const size_t used_size = stream_writer_.write_ptr() - chunk_->payload();
102 chunk_->set_used_size(static_cast<uint32_t>(used_size));
103 }
104
105 TraceRingBuffer::Chunk* new_chunk = trace_ring_buffer_->TakeChunk(writer_id_);
106 if (is_fragmenting_event) {
107 // continue_on_next_chunk_ptr_ points to the proto field of the previous
108 // chunk and indicates that its last event continues in |new_chunk|.
109 *continue_on_next_chunk_ptr_ = 1;
110
111 // Backfill the size field of the event with the partial size acccumulated
112 // so far in the old chunk. WriteEventPrambleForNewChunk() will take care
113 // of resetting the |size_field| of the event to the new chunk.
114 DCHECK_GE(event_data_start_in_current_chunk_, chunk_->payload());
115 DCHECK_LE(event_data_start_in_current_chunk_,
116 chunk_->end() - proto::kMessageLengthFieldSize);
117 const size_t event_partial_size = static_cast<size_t>(
118 stream_writer_.write_ptr() - event_data_start_in_current_chunk_);
119 proto::WriteRedundantVarIntU32<proto::kMessageLengthFieldSize>(
120 event_partial_size, event_.size_field().begin);
121 event_.inc_size_already_written(event_partial_size);
122
123 // If this is a continuation of an event, this writer needs to retain the
124 // old chunk. The client might still be able to write to it. This is to deal
125 // with the case of a nested message which is started in one chunk and
126 // ends in another one. The finalization needs to write-back the size field
127 // in the old chunk.
128 new_chunk->set_next_in_owner_list(chunk_);
129 } else if (chunk_) {
130 // Otherwise, if this is a new event, the previous chunk can be returned.
131 trace_ring_buffer_->ReturnChunk(chunk_);
132 DCHECK(!chunk_->next_in_owner_list());
133 }
134 chunk_ = new_chunk;
135
136 // Write the protobuf for the chunk header. The generated C++ stub for
137 // events_chunk.proto cannot be/ used here because that would re-enter this
138 // class and make this code extremely hard to reason about.
139 uint8_t* chunk_proto = new_chunk->payload();
140
141 proto::StaticAssertSingleBytePreamble<ChunkProto::kWriterIdFieldNumber>();
142 *chunk_proto++ = static_cast<uint8_t>(
143 proto::MakeTagVarInt(ChunkProto::kWriterIdFieldNumber));
144 chunk_proto = proto::WriteVarIntU32(writer_id_, chunk_proto);
145
146 proto::StaticAssertSingleBytePreamble<
147 ChunkProto::kSeqIdInStreamFieldNumber>();
148 *chunk_proto++ = static_cast<uint8_t>(
149 proto::MakeTagVarInt(ChunkProto::kSeqIdInStreamFieldNumber));
150 chunk_proto = proto::WriteVarIntU32(chunk_seq_id_, chunk_proto);
151
152 proto::StaticAssertSingleBytePreamble<
153 ChunkProto::kFirstEventContinuesFromPrevChunkFieldNumber>();
154 *chunk_proto++ = static_cast<uint8_t>(proto::MakeTagVarInt(
155 ChunkProto::kFirstEventContinuesFromPrevChunkFieldNumber));
156 *chunk_proto++ = is_fragmenting_event ? 1u : 0u;
157
158 // At this point we don't know yet whether the last event in the chunk will
159 // fragment and continue in the next chunk. For the moment we put a zero as a
160 // placeholder and remember its position in the chunk. The actual value will
161 // be written the next time we will take a new chunk (above in this function).
162 proto::StaticAssertSingleBytePreamble<
163 ChunkProto::kLastEventContinuesOnNextChunkFieldNumber>();
164 *chunk_proto++ = static_cast<uint8_t>(proto::MakeTagVarInt(
165 ChunkProto::kLastEventContinuesOnNextChunkFieldNumber));
166 continue_on_next_chunk_ptr_ = chunk_proto;
167 *chunk_proto++ = 0;
168
169 ++chunk_seq_id_;
170
171 // If the new chunk was requested while writing an event (the event spans
172 // across chunks) write a new preamble for the partial event in the new chunk.
173 if (is_fragmenting_event)
174 chunk_proto = WriteEventPrambleForNewChunk(chunk_proto);
175
176 return {chunk_proto, new_chunk->end()};
177 }
178
179 // Writes the one-byte preamble for the start of either a new or a partial
180 // event and reserves kMessageLengthFieldSize bytes for its length. Also
181 // keeps size-field the bookeeping up to date. Returns the pointer in the chunk
182 // past the event preamble, where the event proto should be written.
183 uint8_t* TraceBufferWriter::WriteEventPrambleForNewChunk(uint8_t* begin) {
184 // The caller must have ensured to have enough room in the chunk. The event
185 // preamble itself cannot be fragmented.
186 uint8_t* const end = begin + kEventPreambleSize;
187 proto::StaticAssertSingleBytePreamble<ChunkProto::kEventsFieldNumber>();
188 *begin++ = static_cast<uint8_t>(
189 proto::MakeTagLengthDelimited(ChunkProto::kEventsFieldNumber));
190 ContiguousMemoryRange range = {begin, end};
191 event_.set_size_field(range);
192 event_data_start_in_current_chunk_ = end;
193 return end;
194 }
195
196 void TraceBufferWriter::Flush() {
197 // TODO this is all duuuuuuuuuuuuuup dup dup dup.
198 event_.Finalize();
199
200 if (chunk_) {
201 DCHECK_GE(stream_writer_.write_ptr(), chunk_->payload());
202 DCHECK_LE(stream_writer_.write_ptr(), chunk_->end());
203 const size_t used_size = stream_writer_.write_ptr() - chunk_->payload();
204 chunk_->set_used_size(static_cast<uint32_t>(used_size));
205 }
206
207 TraceRingBuffer::Chunk* chunk = chunk_;
208 while (chunk) {
209 TraceRingBuffer::Chunk* next = chunk->next_in_owner_list();
210 trace_ring_buffer_->ReturnChunk(chunk);
211 chunk = next;
212 }
213 }
214
215 } // namespace v2
216 } // namespace tracing
OLDNEW
« no previous file with comments | « components/tracing/core/trace_buffer_writer.h ('k') | components/tracing/core/trace_buffer_writer_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698