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 "base/macros.h" |
| 8 #include "base/memory/ptr_util.h" |
| 9 #include "components/tracing/core/string_interning.h" |
| 10 #include "components/tracing/test/golden_protos/event.pb.h" |
| 11 #include "components/tracing/test/golden_protos/events_chunk.pb.h" |
| 12 #include "testing/gtest/include/gtest/gtest.h" |
| 13 |
| 14 namespace tracing { |
| 15 namespace v2 { |
| 16 namespace { |
| 17 |
| 18 namespace golden_protos = ::tracing::proto; |
| 19 |
| 20 ::testing::AssertionResult ValueEquals(const char* m_expr, |
| 21 const char* n_expr, |
| 22 const base::Value& m, |
| 23 const base::Value& n) { |
| 24 if (m.Equals(&n)) |
| 25 return ::testing::AssertionSuccess(); |
| 26 |
| 27 return ::testing::AssertionFailure() << "\nExpected:\n" |
| 28 << m << "\nActual:\n" |
| 29 << n; |
| 30 } |
| 31 |
| 32 void ParseChunk(TraceBufferReader* reader, const std::string& chunk) { |
| 33 reader->ParseChunk(reinterpret_cast<const uint8_t*>(chunk.data()), |
| 34 chunk.size()); |
| 35 } |
| 36 |
| 37 // Parse a zero length chunk and a chunk with no events. In both cases parsing |
| 38 // should succeed but no event should be decoded. |
| 39 TEST(TraceBufferReaderTest, EmptyChunk) { |
| 40 golden_protos::EventsChunk chunk; |
| 41 chunk.set_writer_id(1); |
| 42 chunk.set_seq_id(0); |
| 43 |
| 44 TraceBufferReader reader(TraceBufferReader::kBuildLegacyJSONEvents); |
| 45 std::string zero_len_chunk; |
| 46 ParseChunk(&reader, zero_len_chunk); |
| 47 ParseChunk(&reader, chunk.SerializeAsString()); |
| 48 ASSERT_EQ(0u, reader.legacy_events()->GetSize()); |
| 49 } |
| 50 |
| 51 TEST(TraceBufferReaderTest, OneChunkOneEvent) { |
| 52 golden_protos::Event evt; |
| 53 evt.set_type(golden_protos::Event::COMPLETE); |
| 54 evt.set_name_str("copied_str_name"); |
| 55 auto interned_cat_name = InternString("interned_category_name"); |
| 56 evt.set_category_id(interned_cat_name); |
| 57 evt.set_timestamp(42); |
| 58 evt.set_thread_timestamp(0xf1f2f3f4); |
| 59 |
| 60 golden_protos::EventsChunk chunk; |
| 61 chunk.set_writer_id(1); |
| 62 chunk.set_seq_id(0); |
| 63 chunk.add_events(evt.SerializeAsString()); |
| 64 |
| 65 TraceBufferReader reader(TraceBufferReader::kBuildLegacyJSONEvents | |
| 66 TraceBufferReader::kBuildInternedStringsTable); |
| 67 ParseChunk(&reader, chunk.SerializeAsString()); |
| 68 ASSERT_EQ(1u, reader.legacy_events()->GetSize()); |
| 69 base::DictionaryValue* decoded_evt = nullptr; |
| 70 ASSERT_TRUE(reader.legacy_events()->GetDictionary(0, &decoded_evt)); |
| 71 |
| 72 base::DictionaryValue expected_value; |
| 73 expected_value.SetString("ph", "X"); |
| 74 expected_value.SetString("name", "copied_str_name"); |
| 75 expected_value.SetString("cat", "interned_category_name"); |
| 76 expected_value.SetInteger("ts", 42); |
| 77 expected_value.SetInteger("tts", static_cast<int>(0xf1f2f3f4)); |
| 78 EXPECT_PRED_FORMAT2(ValueEquals, expected_value, *decoded_evt); |
| 79 |
| 80 ASSERT_EQ(1u, reader.interned_strings().count(interned_cat_name)); |
| 81 EXPECT_EQ("interned_category_name", |
| 82 reader.interned_strings().at(interned_cat_name)); |
| 83 } |
| 84 |
| 85 TEST(TraceBufferReaderTest, OneChunkManyEvents) { |
| 86 golden_protos::EventsChunk chunk; |
| 87 chunk.set_writer_id(1); |
| 88 chunk.set_seq_id(42); |
| 89 const char* kCatNames[] = {"cat1", "cat2", "cat3", "cat4"}; |
| 90 const size_t kNumEvents = 100; |
| 91 |
| 92 for (size_t i = 0; i < kNumEvents; ++i) { |
| 93 golden_protos::Event evt; |
| 94 evt.set_type(golden_protos::Event::COMPLETE); |
| 95 evt.set_name_str("copied_str_name"); |
| 96 evt.set_category_id(InternString(kCatNames[i % arraysize(kCatNames)])); |
| 97 evt.set_timestamp(0xff000000 + i); |
| 98 chunk.add_events(evt.SerializeAsString()); |
| 99 } |
| 100 |
| 101 TraceBufferReader reader(TraceBufferReader::kBuildLegacyJSONEvents | |
| 102 TraceBufferReader::kBuildInternedStringsTable); |
| 103 ParseChunk(&reader, chunk.SerializeAsString()); |
| 104 ASSERT_EQ(kNumEvents, reader.legacy_events()->GetSize()); |
| 105 for (size_t i = 0; i < kNumEvents; ++i) { |
| 106 base::DictionaryValue* decoded_evt = nullptr; |
| 107 ASSERT_TRUE(reader.legacy_events()->GetDictionary(i, &decoded_evt)); |
| 108 base::DictionaryValue expected_value; |
| 109 expected_value.SetString("ph", "X"); |
| 110 expected_value.SetString("name", "copied_str_name"); |
| 111 expected_value.SetString("cat", kCatNames[i % arraysize(kCatNames)]); |
| 112 expected_value.SetInteger("ts", static_cast<int>(0xff000000 + i)); |
| 113 ASSERT_PRED_FORMAT2(ValueEquals, expected_value, *decoded_evt); |
| 114 } |
| 115 } |
| 116 |
| 117 // Test the decoding events that are fragmented across multiple chunks. |
| 118 TEST(TraceBufferReaderTest, FragmentedEvents) { |
| 119 golden_protos::Event evt[3]; |
| 120 for (size_t i = 0; i < arraysize(evt); ++i) { |
| 121 evt[i].set_type(golden_protos::Event::COMPLETE); |
| 122 evt[i].set_name_str("copied_str_name"); |
| 123 evt[i].set_category_id(InternString("interned_category_name")); |
| 124 evt[i].set_timestamp(i); |
| 125 } |
| 126 |
| 127 // The 2nd event (evt[1]) will be split into three fragments and distributed |
| 128 // over all three chunks. |
| 129 const size_t evt1_size = evt[1].ByteSize(); |
| 130 DCHECK_GT(evt1_size, 8u); |
| 131 std::unique_ptr<uint8_t[]> serialized_evt1(new uint8_t[evt1_size]); |
| 132 evt[1].SerializeToArray(serialized_evt1.get(), evt1_size); |
| 133 |
| 134 // Create three chunks and split the three events as follows: |
| 135 // chunk[0]: evt[0] (full), evt[1] (fragment 1/3) |
| 136 // chunk[1]: evt[1] (fragment 2/3) |
| 137 // chunk[2]: evt[1] (fragment 3/3), evt[2] (full) |
| 138 |
| 139 golden_protos::EventsChunk chunk[3]; |
| 140 for (size_t i = 0; i < arraysize(chunk); ++i) { |
| 141 chunk[i].set_writer_id(42); |
| 142 chunk[i].set_seq_id(i); |
| 143 } |
| 144 |
| 145 chunk[0].set_first_event_continues_from_prev_chunk(false); |
| 146 chunk[0].set_last_event_continues_on_next_chunk(true); |
| 147 chunk[0].add_events(evt[0].SerializeAsString()); |
| 148 chunk[0].add_events(&serialized_evt1[0], 4); |
| 149 |
| 150 chunk[1].set_first_event_continues_from_prev_chunk(true); |
| 151 chunk[1].set_last_event_continues_on_next_chunk(true); |
| 152 chunk[1].add_events(&serialized_evt1[4], 4); |
| 153 |
| 154 chunk[2].set_first_event_continues_from_prev_chunk(true); |
| 155 chunk[2].add_events(&serialized_evt1[8], evt1_size - 8); |
| 156 chunk[2].add_events(evt[2].SerializeAsString()); |
| 157 |
| 158 TraceBufferReader reader(TraceBufferReader::kBuildLegacyJSONEvents | |
| 159 TraceBufferReader::kBuildInternedStringsTable); |
| 160 for (size_t i = 0; i < arraysize(chunk); ++i) |
| 161 ParseChunk(&reader, chunk[i].SerializeAsString()); |
| 162 |
| 163 ASSERT_EQ(arraysize(evt), reader.legacy_events()->GetSize()); |
| 164 for (size_t i = 0; i < arraysize(evt); ++i) { |
| 165 base::DictionaryValue* decoded_evt = nullptr; |
| 166 ASSERT_TRUE(reader.legacy_events()->GetDictionary(i, &decoded_evt)); |
| 167 |
| 168 base::DictionaryValue expected_value; |
| 169 expected_value.SetString("ph", "X"); |
| 170 expected_value.SetString("name", "copied_str_name"); |
| 171 expected_value.SetString("cat", "interned_category_name"); |
| 172 expected_value.SetInteger("ts", static_cast<int>(i)); |
| 173 ASSERT_PRED_FORMAT2(ValueEquals, expected_value, *decoded_evt); |
| 174 } |
| 175 } |
| 176 |
| 177 // Test that the fragment reconstruction logic discards fragments if one of |
| 178 // the chunks is missing. |
| 179 TEST(TraceBufferReaderTest, FragmentedEventsWithMissingChunks) { |
| 180 golden_protos::Event evt[2]; |
| 181 for (size_t i = 0; i < arraysize(evt); ++i) { |
| 182 evt[i].set_type(golden_protos::Event::METADATA); |
| 183 evt[i].set_timestamp(i); |
| 184 } |
| 185 |
| 186 golden_protos::EventsChunk chunk[2]; |
| 187 for (size_t i = 0; i < arraysize(chunk); ++i) { |
| 188 chunk[i].set_writer_id(42); |
| 189 chunk[i].set_seq_id(1 + i); |
| 190 } |
| 191 |
| 192 // The 2nd event (evt[1]) will be split into two fragments. |
| 193 const size_t evt1_size = evt[1].ByteSize(); |
| 194 DCHECK_GT(evt1_size, 2u); |
| 195 std::unique_ptr<uint8_t[]> serialized_evt1(new uint8_t[evt1_size]); |
| 196 evt[1].SerializeToArray(serialized_evt1.get(), evt1_size); |
| 197 |
| 198 chunk[0].set_first_event_continues_from_prev_chunk(true); |
| 199 chunk[0].set_last_event_continues_on_next_chunk(true); |
| 200 chunk[0].add_events("should be discarded because seq id 0 is not seen."); |
| 201 chunk[0].add_events(evt[0].SerializeAsString()); |
| 202 chunk[0].add_events(&serialized_evt1[0], 2); |
| 203 |
| 204 chunk[1].set_first_event_continues_from_prev_chunk(true); |
| 205 chunk[1].set_last_event_continues_on_next_chunk(true); |
| 206 chunk[1].add_events(&serialized_evt1[0] + 2, evt1_size - 2); |
| 207 chunk[1].add_events("should be discarded because seq id 2 is not seen."); |
| 208 |
| 209 TraceBufferReader reader(TraceBufferReader::kBuildLegacyJSONEvents); |
| 210 ParseChunk(&reader, chunk[0].SerializeAsString()); |
| 211 ParseChunk(&reader, chunk[1].SerializeAsString()); |
| 212 |
| 213 ASSERT_EQ(arraysize(evt), reader.legacy_events()->GetSize()); |
| 214 for (size_t i = 0; i < arraysize(evt); ++i) { |
| 215 base::DictionaryValue* decoded_evt = nullptr; |
| 216 ASSERT_TRUE(reader.legacy_events()->GetDictionary(i, &decoded_evt)); |
| 217 |
| 218 base::DictionaryValue expected_value; |
| 219 expected_value.SetString("ph", "M"); |
| 220 expected_value.SetInteger("ts", static_cast<int>(i)); |
| 221 ASSERT_PRED_FORMAT2(ValueEquals, expected_value, *decoded_evt); |
| 222 } |
| 223 } |
| 224 |
| 225 } // namespace |
| 226 } // namespace v2 |
| 227 } // namespace tracing |
OLD | NEW |