Chromium Code Reviews| 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 |