OLD | NEW |
---|---|
(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_reader.h" | |
6 | |
7 #include <cctype> | |
8 #include <deque> | |
9 #include <utility> | |
10 | |
11 #include "base/memory/ptr_util.h" | |
12 #include "base/strings/string_piece.h" | |
13 #include "components/tracing/core/proto_utils.h" | |
14 #include "components/tracing/core/string_interning.h" | |
15 #include "components/tracing/proto/event.pbzero.h" | |
16 #include "components/tracing/proto/events_chunk.pbzero.h" | |
17 | |
18 namespace tracing { | |
19 namespace v2 { | |
20 | |
21 TraceBufferReader::TraceBufferReader(uint32_t options) : options_(options) { | |
22 DCHECK((options & kBuildInternedStringsTable) || | |
23 (options & kBuildLegacyJSONEvents)); | |
24 if (options & kBuildLegacyJSONEvents) | |
25 legacy_events_ = base::MakeUnique<base::ListValue>(); | |
26 } | |
27 | |
28 TraceBufferReader::~TraceBufferReader() {} | |
29 | |
30 void TraceBufferReader::ParseChunk(const uint8_t* const start, size_t length) { | |
31 using ChunkProto = pbzero::tracing::proto::EventsChunk; | |
32 uint32_t writer_id = 0; | |
33 bool has_writer_id = false; | |
34 uint32_t seq_id = 0; | |
35 bool has_seq_id = false; | |
36 bool first_event_continues_from_prev_chunk = false; | |
37 bool last_event_continues_on_next_chunk = false; | |
38 using Range = std::pair<const uint8_t*, const uint8_t*>; // (begin, end) | |
39 std::deque<Range> events; | |
40 | |
41 const uint8_t* const end = start + length; | |
42 if (end == start) | |
43 return; // Empty chunk. | |
44 | |
45 // Iterate over all the proto fields in the chunk. Each chunk proto message | |
46 // contains some global properties for the chunk and one submessage for each | |
47 // trace event. | |
48 const uint8_t *cur, *next; | |
49 for (cur = start, next = nullptr; cur < end; cur = next) { | |
50 uint32_t field_id; | |
51 proto::FieldType field_type; | |
52 uint64_t field_intvalue; | |
53 const uint8_t* field_payload = nullptr; | |
54 next = proto::ParseField(cur, end, &field_id, &field_type, &field_intvalue); | |
55 DCHECK(next && next <= end); | |
56 if (field_type == proto::kFieldTypeLengthDelimited) { | |
57 // In the case of a length-delimited field, |field_intvalue| contains | |
58 // the size of the field. |field_payload| points to the start of the | |
59 // payload (either a string or a submessage). | |
60 field_payload = next - field_intvalue; | |
61 DCHECK(cur < field_payload && field_payload <= next); | |
62 } | |
63 switch (field_id) { | |
64 case ChunkProto::kWriterIdFieldNumber: | |
65 DCHECK_EQ(proto::kFieldTypeVarInt, field_type); | |
66 writer_id = static_cast<uint32_t>(field_intvalue); | |
67 has_writer_id = true; | |
68 break; | |
69 case ChunkProto::kSeqIdFieldNumber: | |
70 DCHECK_EQ(proto::kFieldTypeVarInt, field_type); | |
71 has_seq_id = true; | |
72 seq_id = static_cast<uint32_t>(field_intvalue); | |
73 break; | |
74 case ChunkProto::kFirstEventContinuesFromPrevChunkFieldNumber: | |
75 DCHECK_EQ(proto::kFieldTypeVarInt, field_type); | |
76 first_event_continues_from_prev_chunk = field_intvalue ? true : false; | |
77 break; | |
78 case ChunkProto::kLastEventContinuesOnNextChunkFieldNumber: | |
79 DCHECK_EQ(proto::kFieldTypeVarInt, field_type); | |
80 last_event_continues_on_next_chunk = field_intvalue ? true : false; | |
81 break; | |
82 case ChunkProto::kEventsFieldNumber: | |
83 DCHECK_EQ(proto::kFieldTypeLengthDelimited, field_type); | |
84 events.push_back(std::make_pair(field_payload, next)); | |
85 break; | |
86 default: | |
87 NOTREACHED(); | |
88 } | |
89 } | |
90 DCHECK_EQ(end, cur); | |
91 DCHECK(has_seq_id); | |
92 DCHECK(has_writer_id); | |
93 | |
94 WriterState* writer_state = &writer_state_[writer_id]; | |
95 | |
96 // When |first_event_continues_from_prev_chunk| is true, the first event is | |
97 // a fragment. The fragment will be parsed only if we saw all the previous | |
98 // fragments AND there are no more fragments upcoming. | |
99 if (first_event_continues_from_prev_chunk) { | |
100 DCHECK(!events.empty()); | |
101 if (writer_state->last_fragment_is_valid && | |
102 seq_id == writer_state->last_seq_id + 1) { | |
103 // The chunk sequence is valid (no drops). | |
104 writer_state->partial_event_data.insert( | |
105 writer_state->partial_event_data.end(), events[0].first, | |
106 events[0].second); | |
107 writer_state->last_seq_id = seq_id; | |
108 | |
109 // If: | |
110 // - The current chunk contains > 1 event, in which case the current | |
111 // fragment was the last one. | |
112 // OR | |
113 // - The current chunks contains 1 event, but is not flagged to to | |
114 // continue in the next chunk. | |
115 // All the sequence of fragments that have been merged in | |
116 // |partial_event_data| is valid and can be processed now. | |
117 if (events.size() > 1 || !last_event_continues_on_next_chunk) { | |
118 ParseEvent(&*writer_state->partial_event_data.begin(), | |
119 &*writer_state->partial_event_data.end()); | |
120 writer_state->Reset(); | |
121 } | |
122 } else { | |
123 // The previous chunk has been missed, dropping a previous fragment. This | |
124 // can happen if the trace ring buffer wraps over and the previous chunk | |
125 // is recycled. | |
126 writer_state->Reset(); | |
127 } | |
128 events.pop_front(); | |
129 } | |
130 | |
131 size_t event_count = 1; | |
132 for (const Range& event : events) { | |
133 if (event_count++ == events.size() && last_event_continues_on_next_chunk) { | |
134 writer_state->last_fragment_is_valid = true; | |
135 writer_state->last_seq_id = seq_id; | |
136 writer_state->partial_event_data.insert( | |
137 writer_state->partial_event_data.end(), event.first, event.second); | |
138 break; | |
139 } | |
140 ParseEvent(event.first, event.second); | |
141 } | |
142 } | |
143 | |
144 void TraceBufferReader::ParseEvent(const uint8_t* const start, | |
145 const uint8_t* const end) { | |
146 using EventProto = pbzero::tracing::proto::Event; | |
147 std::unique_ptr<base::DictionaryValue> event_value; | |
148 if (options_ & kBuildLegacyJSONEvents) | |
149 event_value = base::MakeUnique<base::DictionaryValue>(); | |
150 | |
151 DCHECK_GT(end, start); | |
152 const uint8_t *cur, *next; | |
153 for (cur = start, next = nullptr; cur < end; cur = next) { | |
154 uint32_t field_id; | |
155 proto::FieldType field_type; | |
156 uint64_t field_intvalue; | |
157 next = proto::ParseField(cur, end, &field_id, &field_type, &field_intvalue); | |
158 base::StringPiece field_strvalue; | |
159 if (field_type == proto::kFieldTypeLengthDelimited) { | |
160 const size_t str_len = static_cast<size_t>(field_intvalue); | |
161 const uint8_t* const str_start = next - str_len; | |
162 DCHECK(cur < str_start && str_start <= next); | |
163 field_strvalue = | |
164 base::StringPiece(reinterpret_cast<const char*>(str_start), str_len); | |
165 } | |
166 const char* interned_string = nullptr; | |
167 if (field_id == EventProto::kNameIdFieldNumber || | |
168 field_id == EventProto::kCategoryIdFieldNumber) { | |
169 const int64_t str_id = static_cast<int64_t>(field_intvalue); | |
170 interned_string = GetInternedStringValue(str_id); | |
171 if (options_ & kBuildInternedStringsTable) | |
172 interned_strings_[str_id] = interned_string; | |
DmitrySkiba
2016/11/29 18:09:56
Hmm, str_id is int64_t here, but interned_strings_
| |
173 } | |
174 | |
175 if (options_ & kBuildLegacyJSONEvents) { | |
176 switch (field_id) { | |
177 case EventProto::kTypeFieldNumber: { | |
178 char event_phase[] = {static_cast<char>(field_intvalue), '\0'}; | |
179 DCHECK(std::isalnum(event_phase[0])); | |
180 event_value->SetString("ph", base::StringPiece(event_phase, 1)); | |
181 break; | |
182 } | |
183 case EventProto::kNameStrFieldNumber: | |
184 // For the rare cases of TRACE_STR_COPY. | |
185 event_value->SetString("name", field_strvalue); | |
186 break; | |
187 case EventProto::kNameIdFieldNumber: | |
188 // For the most common case of event name being a const char* string. | |
189 event_value->SetString("name", interned_string); | |
190 break; | |
191 case EventProto::kCategoryIdFieldNumber: | |
192 event_value->SetString("cat", interned_string); | |
193 break; | |
194 case EventProto::kTimestampFieldNumber: | |
195 event_value->SetInteger("ts", static_cast<int>(field_intvalue)); | |
196 break; | |
197 case EventProto::kThreadTimestampFieldNumber: | |
198 event_value->SetInteger("tts", static_cast<int>(field_intvalue)); | |
199 break; | |
200 default: | |
201 NOTREACHED(); | |
202 } | |
203 } | |
204 } | |
205 DCHECK_EQ(end, cur); | |
206 if (options_ & kBuildLegacyJSONEvents) | |
207 legacy_events_->Append(std::move(event_value)); | |
208 } | |
209 | |
210 TraceBufferReader::WriterState::WriterState() { | |
211 Reset(); | |
212 } | |
213 | |
214 TraceBufferReader::WriterState::~WriterState() {} | |
215 | |
216 void TraceBufferReader::WriterState::Reset() { | |
217 last_seq_id = 0; | |
218 last_fragment_is_valid = false; | |
219 partial_event_data.clear(); | |
220 } | |
221 | |
222 } // namespace v2 | |
223 } // namespace tracing | |
OLD | NEW |