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

Unified Diff: components/tracing/core/trace_buffer_reader_unittest.cc

Issue 2308583003: tracing v2: Introduce TraceBufferReader to read-back the trace buffer (Closed)
Patch Set: some fixes + moar tests Created 4 years, 3 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 side-by-side diff with in-line comments
Download patch
Index: components/tracing/core/trace_buffer_reader_unittest.cc
diff --git a/components/tracing/core/trace_buffer_reader_unittest.cc b/components/tracing/core/trace_buffer_reader_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..8a4210b99104ac5185ed142412a20d1919be0a78
--- /dev/null
+++ b/components/tracing/core/trace_buffer_reader_unittest.cc
@@ -0,0 +1,227 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/tracing/core/trace_buffer_reader.h"
+
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "components/tracing/core/string_interning.h"
+#include "components/tracing/test/golden_protos/event.pb.h"
+#include "components/tracing/test/golden_protos/events_chunk.pb.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace tracing {
+namespace v2 {
+namespace {
+
+namespace golden_protos = ::tracing::proto;
+
+::testing::AssertionResult ValueEquals(const char* m_expr,
+ const char* n_expr,
+ const base::Value& m,
+ const base::Value& n) {
+ if (m.Equals(&n))
+ return ::testing::AssertionSuccess();
+
+ return ::testing::AssertionFailure() << "\nExpected:\n"
+ << m << "\nActual:\n"
+ << n;
+}
+
+void ParseChunk(TraceBufferReader* reader, const std::string& chunk) {
+ reader->ParseChunk(reinterpret_cast<const uint8_t*>(chunk.data()),
+ chunk.size());
+}
+
+// Parse a zero length chunk and a chunk with no events. In both cases parsing
+// should succeed but no event should be decoded.
+TEST(TraceBufferReaderTest, EmptyChunk) {
+ golden_protos::EventsChunk chunk;
+ chunk.set_writer_id(1);
+ chunk.set_seq_id(0);
+
+ TraceBufferReader reader(TraceBufferReader::kBuildLegacyJSONEvents);
+ std::string zero_len_chunk;
+ ParseChunk(&reader, zero_len_chunk);
+ ParseChunk(&reader, chunk.SerializeAsString());
+ ASSERT_EQ(0u, reader.legacy_events()->GetSize());
+}
+
+TEST(TraceBufferReaderTest, OneChunkOneEvent) {
+ golden_protos::Event evt;
+ evt.set_type(golden_protos::Event::COMPLETE);
+ evt.set_name_str("copied_str_name");
+ auto interned_cat_name = InternString("interned_category_name");
+ evt.set_category_id(interned_cat_name);
+ evt.set_timestamp(42);
+ evt.set_thread_timestamp(0xf1f2f3f4);
+
+ golden_protos::EventsChunk chunk;
+ chunk.set_writer_id(1);
+ chunk.set_seq_id(0);
+ chunk.add_events(evt.SerializeAsString());
+
+ TraceBufferReader reader(TraceBufferReader::kBuildLegacyJSONEvents |
+ TraceBufferReader::kBuildInternedStringsTable);
+ ParseChunk(&reader, chunk.SerializeAsString());
+ ASSERT_EQ(1u, reader.legacy_events()->GetSize());
+ base::DictionaryValue* decoded_evt = nullptr;
+ ASSERT_TRUE(reader.legacy_events()->GetDictionary(0, &decoded_evt));
+
+ base::DictionaryValue expected_value;
+ expected_value.SetString("ph", "X");
+ expected_value.SetString("name", "copied_str_name");
+ expected_value.SetString("cat", "interned_category_name");
+ expected_value.SetInteger("ts", 42);
+ expected_value.SetInteger("tts", static_cast<int>(0xf1f2f3f4));
+ EXPECT_PRED_FORMAT2(ValueEquals, expected_value, *decoded_evt);
+
+ ASSERT_EQ(1u, reader.interned_strings().count(interned_cat_name));
+ EXPECT_EQ("interned_category_name",
+ reader.interned_strings().at(interned_cat_name));
+}
+
+TEST(TraceBufferReaderTest, OneChunkManyEvents) {
+ golden_protos::EventsChunk chunk;
+ chunk.set_writer_id(1);
+ chunk.set_seq_id(42);
+ const char* kCatNames[] = {"cat1", "cat2", "cat3", "cat4"};
+ const size_t kNumEvents = 100;
+
+ for (size_t i = 0; i < kNumEvents; ++i) {
+ golden_protos::Event evt;
+ evt.set_type(golden_protos::Event::COMPLETE);
+ evt.set_name_str("copied_str_name");
+ evt.set_category_id(InternString(kCatNames[i % arraysize(kCatNames)]));
+ evt.set_timestamp(0xff000000 + i);
+ chunk.add_events(evt.SerializeAsString());
+ }
+
+ TraceBufferReader reader(TraceBufferReader::kBuildLegacyJSONEvents |
+ TraceBufferReader::kBuildInternedStringsTable);
+ ParseChunk(&reader, chunk.SerializeAsString());
+ ASSERT_EQ(kNumEvents, reader.legacy_events()->GetSize());
+ for (size_t i = 0; i < kNumEvents; ++i) {
+ base::DictionaryValue* decoded_evt = nullptr;
+ ASSERT_TRUE(reader.legacy_events()->GetDictionary(i, &decoded_evt));
+ base::DictionaryValue expected_value;
+ expected_value.SetString("ph", "X");
+ expected_value.SetString("name", "copied_str_name");
+ expected_value.SetString("cat", kCatNames[i % arraysize(kCatNames)]);
+ expected_value.SetInteger("ts", static_cast<int>(0xff000000 + i));
+ ASSERT_PRED_FORMAT2(ValueEquals, expected_value, *decoded_evt);
+ }
+}
+
+// Test the decoding events that are fragmented across multiple chunks.
+TEST(TraceBufferReaderTest, FragmentedEvents) {
+ golden_protos::Event evt[3];
+ for (size_t i = 0; i < arraysize(evt); ++i) {
+ evt[i].set_type(golden_protos::Event::COMPLETE);
+ evt[i].set_name_str("copied_str_name");
+ evt[i].set_category_id(InternString("interned_category_name"));
+ evt[i].set_timestamp(i);
+ }
+
+ // The 2nd event (evt[1]) will be split into three fragments and distributed
+ // over all three chunks.
+ const size_t evt1_size = evt[1].ByteSize();
+ DCHECK_GT(evt1_size, 8u);
+ std::unique_ptr<uint8_t[]> serialized_evt1(new uint8_t[evt1_size]);
+ evt[1].SerializeToArray(serialized_evt1.get(), evt1_size);
+
+ // Create three chunks and split the three events as follows:
+ // chunk[0]: evt[0] (full), evt[1] (fragment 1/3)
+ // chunk[1]: evt[1] (fragment 2/3)
+ // chunk[2]: evt[1] (fragment 3/3), evt[2] (full)
+
+ golden_protos::EventsChunk chunk[3];
+ for (size_t i = 0; i < arraysize(chunk); ++i) {
+ chunk[i].set_writer_id(42);
+ chunk[i].set_seq_id(i);
+ }
+
+ chunk[0].set_first_event_continues_from_prev_chunk(false);
+ chunk[0].set_last_event_continues_on_next_chunk(true);
+ chunk[0].add_events(evt[0].SerializeAsString());
+ chunk[0].add_events(&serialized_evt1[0], 4);
+
+ chunk[1].set_first_event_continues_from_prev_chunk(true);
+ chunk[1].set_last_event_continues_on_next_chunk(true);
+ chunk[1].add_events(&serialized_evt1[4], 4);
+
+ chunk[2].set_first_event_continues_from_prev_chunk(true);
+ chunk[2].add_events(&serialized_evt1[8], evt1_size - 8);
+ chunk[2].add_events(evt[2].SerializeAsString());
+
+ TraceBufferReader reader(TraceBufferReader::kBuildLegacyJSONEvents |
+ TraceBufferReader::kBuildInternedStringsTable);
+ for (size_t i = 0; i < arraysize(chunk); ++i)
+ ParseChunk(&reader, chunk[i].SerializeAsString());
+
+ ASSERT_EQ(arraysize(evt), reader.legacy_events()->GetSize());
+ for (size_t i = 0; i < arraysize(evt); ++i) {
+ base::DictionaryValue* decoded_evt = nullptr;
+ ASSERT_TRUE(reader.legacy_events()->GetDictionary(i, &decoded_evt));
+
+ base::DictionaryValue expected_value;
+ expected_value.SetString("ph", "X");
+ expected_value.SetString("name", "copied_str_name");
+ expected_value.SetString("cat", "interned_category_name");
+ expected_value.SetInteger("ts", static_cast<int>(i));
+ ASSERT_PRED_FORMAT2(ValueEquals, expected_value, *decoded_evt);
+ }
+}
+
+// Test that the fragment reconstruction logic discards fragments if one of
+// the chunks is missing.
+TEST(TraceBufferReaderTest, FragmentedEventsWithMissingChunks) {
+ golden_protos::Event evt[2];
+ for (size_t i = 0; i < arraysize(evt); ++i) {
+ evt[i].set_type(golden_protos::Event::METADATA);
+ evt[i].set_timestamp(i);
+ }
+
+ golden_protos::EventsChunk chunk[2];
+ for (size_t i = 0; i < arraysize(chunk); ++i) {
+ chunk[i].set_writer_id(42);
+ chunk[i].set_seq_id(1 + i);
+ }
+
+ // The 2nd event (evt[1]) will be split into two fragments.
+ const size_t evt1_size = evt[1].ByteSize();
+ DCHECK_GT(evt1_size, 2u);
+ std::unique_ptr<uint8_t[]> serialized_evt1(new uint8_t[evt1_size]);
+ evt[1].SerializeToArray(serialized_evt1.get(), evt1_size);
+
+ chunk[0].set_first_event_continues_from_prev_chunk(true);
+ chunk[0].set_last_event_continues_on_next_chunk(true);
+ chunk[0].add_events("should be discarded because seq id 0 is not seen.");
+ chunk[0].add_events(evt[0].SerializeAsString());
+ chunk[0].add_events(&serialized_evt1[0], 2);
+
+ chunk[1].set_first_event_continues_from_prev_chunk(true);
+ chunk[1].set_last_event_continues_on_next_chunk(true);
+ chunk[1].add_events(&serialized_evt1[0] + 2, evt1_size - 2);
+ chunk[1].add_events("should be discarded because seq id 2 is not seen.");
+
+ TraceBufferReader reader(TraceBufferReader::kBuildLegacyJSONEvents);
+ ParseChunk(&reader, chunk[0].SerializeAsString());
+ ParseChunk(&reader, chunk[1].SerializeAsString());
+
+ ASSERT_EQ(arraysize(evt), reader.legacy_events()->GetSize());
+ for (size_t i = 0; i < arraysize(evt); ++i) {
+ base::DictionaryValue* decoded_evt = nullptr;
+ ASSERT_TRUE(reader.legacy_events()->GetDictionary(i, &decoded_evt));
+
+ base::DictionaryValue expected_value;
+ expected_value.SetString("ph", "M");
+ expected_value.SetInteger("ts", static_cast<int>(i));
+ ASSERT_PRED_FORMAT2(ValueEquals, expected_value, *decoded_evt);
+ }
+}
+
+} // namespace
+} // namespace v2
+} // namespace tracing

Powered by Google App Engine
This is Rietveld 408576698