trace_processor: add code for parsing a trace
This CL utilises the previously introduced ProtoDecoder class to parse a
Trace into its important components. It doesn't do anything with the
parsed data yet but it lays the skeleton for writing to TraceStorage in
a subsequent CL.
Bug: 80416541
Change-Id: Id0422968adea16a98ec46b583057e440cd5274cf
diff --git a/include/perfetto/protozero/proto_decoder.h b/include/perfetto/protozero/proto_decoder.h
index 24d36f6..ce57b91 100644
--- a/include/perfetto/protozero/proto_decoder.h
+++ b/include/perfetto/protozero/proto_decoder.h
@@ -20,6 +20,7 @@
#include <stdint.h>
#include <memory>
+#include "perfetto/base/logging.h"
#include "perfetto/protozero/proto_utils.h"
namespace protozero {
@@ -41,8 +42,14 @@
protozero::proto_utils::FieldType type;
union {
uint64_t int_value;
- LengthDelimited length_value;
+ LengthDelimited length_limited;
};
+
+ inline uint32_t as_uint32() const {
+ PERFETTO_DCHECK(type == proto_utils::FieldType::kFieldTypeVarInt ||
+ type == proto_utils::FieldType::kFieldTypeFixed32);
+ return static_cast<uint32_t>(int_value);
+ }
};
// Creates a ProtoDecoder using the given |buffer| with size |length| bytes.
@@ -56,6 +63,9 @@
// otherwise.
bool IsEndOfBuffer();
+ // Resets the current position to the start of the buffer.
+ void Reset();
+
private:
const uint8_t* const buffer_;
const uint64_t length_;
diff --git a/src/protozero/proto_decoder.cc b/src/protozero/proto_decoder.cc
index 64eda6f..3f38a3c 100644
--- a/src/protozero/proto_decoder.cc
+++ b/src/protozero/proto_decoder.cc
@@ -49,6 +49,11 @@
PERFETTO_DCHECK(pos >= buffer_);
PERFETTO_DCHECK(pos <= end);
+ // If we've already hit the end, just return an invalid field.
+ if (pos >= end) {
+ return field;
+ }
+
uint64_t raw_field_id = 0;
pos = ParseVarInt(pos, end, &raw_field_id);
@@ -102,8 +107,8 @@
// We need to explicity check for zero to ensure that ParseVarInt doesn't
// return zero because of running out of space in the buffer.
if (*pos == 0) {
- field.length_value.data = ++pos;
- field.length_value.length = 0;
+ field.length_limited.data = ++pos;
+ field.length_limited.length = 0;
} else {
pos = ParseVarInt(pos, end, &field_intvalue);
@@ -115,8 +120,8 @@
if (field_intvalue == 0 || pos + field_intvalue > end) {
return field;
}
- field.length_value.data = pos;
- field.length_value.length = field_intvalue;
+ field.length_limited.data = pos;
+ field.length_limited.length = field_intvalue;
pos += field_intvalue;
}
break;
@@ -134,4 +139,8 @@
return length_ == static_cast<uint64_t>(current_position_ - buffer_);
}
+void ProtoDecoder::Reset() {
+ current_position_ = buffer_;
+}
+
} // namespace protozero
diff --git a/src/protozero/proto_decoder_unittest.cc b/src/protozero/proto_decoder_unittest.cc
index fff9dba..a3b5b69 100644
--- a/src/protozero/proto_decoder_unittest.cc
+++ b/src/protozero/proto_decoder_unittest.cc
@@ -49,9 +49,9 @@
ASSERT_EQ(field.id, 1);
ASSERT_EQ(field.type, proto_utils::FieldType::kFieldTypeLengthDelimited);
- ASSERT_EQ(field.length_value.length, sizeof(kTestString) - 1);
+ ASSERT_EQ(field.length_limited.length, sizeof(kTestString) - 1);
for (size_t i = 0; i < sizeof(kTestString) - 1; i++) {
- ASSERT_EQ(field.length_value.data[i], kTestString[i]);
+ ASSERT_EQ(field.length_limited.data[i], kTestString[i]);
}
}
@@ -93,7 +93,7 @@
ASSERT_EQ(exp.type, field.type);
if (field.type == kFieldTypeLengthDelimited) {
- ASSERT_EQ(exp.int_value, field.length_value.length);
+ ASSERT_EQ(exp.int_value, field.length_limited.length);
} else {
ASSERT_EQ(exp.int_value, field.int_value);
}
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index 6419157..c5605f6 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -35,10 +35,13 @@
"trace_parser.h",
"trace_storage.cc",
"trace_storage.h",
+ "virtual_destructors.cc",
]
deps = [
"../../gn:default_deps",
+ "../../protos/perfetto/trace:lite",
"../base",
+ "../protozero",
]
}
@@ -52,5 +55,7 @@
":lib",
"../../gn:default_deps",
"../../gn:gtest_deps",
+ "../../protos/perfetto/trace:lite",
+ "../base",
]
}
diff --git a/src/trace_processor/trace_parser.cc b/src/trace_processor/trace_parser.cc
index 355c6f1..be45cee 100644
--- a/src/trace_processor/trace_parser.cc
+++ b/src/trace_processor/trace_parser.cc
@@ -16,17 +16,39 @@
#include "src/trace_processor/trace_parser.h"
+#include <string>
+
+#include "perfetto/base/logging.h"
#include "perfetto/base/utils.h"
+#include "perfetto/trace/trace.pb.h"
+#include "perfetto/trace/trace_packet.pb.h"
namespace perfetto {
namespace trace_processor {
+using protozero::ProtoDecoder;
+using protozero::proto_utils::kFieldTypeLengthDelimited;
+
+namespace {
+bool FindIntField(ProtoDecoder* decoder,
+ uint32_t field_id,
+ uint64_t* field_value) {
+ for (auto f = decoder->ReadField(); f.id != 0; f = decoder->ReadField()) {
+ if (f.id == field_id) {
+ *field_value = f.int_value;
+ return true;
+ }
+ }
+ return false;
+}
+} // namespace
+
TraceParser::TraceParser(BlobReader* reader,
TraceStorage* trace,
uint32_t chunk_size_b)
: reader_(reader), trace_(trace), chunk_size_b_(chunk_size_b) {}
-void TraceParser::LoadNextChunk() {
+void TraceParser::ParseNextChunk() {
if (!buffer_)
buffer_.reset(new uint8_t[chunk_size_b_]);
@@ -34,11 +56,121 @@
if (read == 0)
return;
- // TODO(lalitm): actually parse the data read here.
- base::ignore_result(trace_);
+ ProtoDecoder decoder(buffer_.get(), read);
+ for (auto fld = decoder.ReadField(); fld.id != 0; fld = decoder.ReadField()) {
+ if (fld.id != protos::Trace::kPacketFieldNumber) {
+ PERFETTO_ELOG("Non-trace packet field found in root Trace proto");
+ continue;
+ }
+ ParsePacket(fld.length_limited.data,
+ static_cast<uint32_t>(fld.length_limited.length));
+ }
offset_ += read;
}
+void TraceParser::ParsePacket(const uint8_t* data, uint32_t length) {
+ ProtoDecoder decoder(data, length);
+ for (auto fld = decoder.ReadField(); fld.id != 0; fld = decoder.ReadField()) {
+ switch (fld.id) {
+ case protos::TracePacket::kFtraceEventsFieldNumber:
+ ParseFtraceEventBundle(
+ fld.length_limited.data,
+ static_cast<uint32_t>(fld.length_limited.length));
+ break;
+ default:
+ break;
+ }
+ }
+ PERFETTO_DCHECK(decoder.IsEndOfBuffer());
+}
+
+void TraceParser::ParseFtraceEventBundle(const uint8_t* data, uint32_t length) {
+ ProtoDecoder decoder(data, length);
+ uint64_t cpu = 0;
+ if (!FindIntField(&decoder, protos::FtraceEventBundle::kCpuFieldNumber,
+ &cpu)) {
+ PERFETTO_ELOG("CPU field not found in FtraceEventBundle");
+ return;
+ }
+ decoder.Reset();
+
+ for (auto fld = decoder.ReadField(); fld.id != 0; fld = decoder.ReadField()) {
+ switch (fld.id) {
+ case protos::FtraceEventBundle::kEventFieldNumber:
+ ParseFtraceEvent(static_cast<uint32_t>(cpu), fld.length_limited.data,
+ static_cast<uint32_t>(fld.length_limited.length));
+ break;
+ default:
+ break;
+ }
+ }
+ PERFETTO_DCHECK(decoder.IsEndOfBuffer());
+}
+
+void TraceParser::ParseFtraceEvent(uint32_t cpu,
+ const uint8_t* data,
+ uint32_t length) {
+ ProtoDecoder decoder(data, length);
+ uint64_t timestamp = 0;
+ if (!FindIntField(&decoder, protos::FtraceEvent::kTimestampFieldNumber,
+ ×tamp)) {
+ PERFETTO_ELOG("Timestamp field not found in FtraceEvent");
+ return;
+ }
+ decoder.Reset();
+
+ for (auto fld = decoder.ReadField(); fld.id != 0; fld = decoder.ReadField()) {
+ switch (fld.id) {
+ case protos::FtraceEvent::kSchedSwitchFieldNumber:
+ PERFETTO_DCHECK(timestamp > 0);
+ ParseSchedSwitch(cpu, timestamp, fld.length_limited.data,
+ static_cast<uint32_t>(fld.length_limited.length));
+ break;
+ default:
+ break;
+ }
+ }
+ PERFETTO_DCHECK(decoder.IsEndOfBuffer());
+}
+
+void TraceParser::ParseSchedSwitch(uint32_t cpu,
+ uint64_t timestamp,
+ const uint8_t* data,
+ uint32_t length) {
+ ProtoDecoder decoder(data, length);
+
+ uint32_t prev_pid = 0;
+ uint32_t next_pid = 0;
+ std::string next_comm;
+ for (auto fld = decoder.ReadField(); fld.id != 0; fld = decoder.ReadField()) {
+ switch (fld.id) {
+ case protos::SchedSwitchFtraceEvent::kPrevPidFieldNumber:
+ prev_pid = fld.as_uint32();
+ break;
+ case protos::SchedSwitchFtraceEvent::kNextPidFieldNumber:
+ next_pid = fld.as_uint32();
+ break;
+ case protos::SchedSwitchFtraceEvent::kNextCommFieldNumber:
+ next_comm =
+ std::string(reinterpret_cast<const char*>(fld.length_limited.data),
+ static_cast<size_t>(fld.length_limited.length));
+ break;
+ default:
+ break;
+ }
+ }
+
+ // TODO(lalitm): store these fields inside the TraceStorage class.
+ base::ignore_result(cpu);
+ base::ignore_result(timestamp);
+ base::ignore_result(trace_);
+ base::ignore_result(prev_pid);
+ base::ignore_result(next_pid);
+ base::ignore_result(next_comm);
+
+ PERFETTO_DCHECK(decoder.IsEndOfBuffer());
+}
+
} // namespace trace_processor
} // namespace perfetto
diff --git a/src/trace_processor/trace_parser.h b/src/trace_processor/trace_parser.h
index 309c892..e4cb818 100644
--- a/src/trace_processor/trace_parser.h
+++ b/src/trace_processor/trace_parser.h
@@ -20,6 +20,7 @@
#include <stdint.h>
#include <memory>
+#include "perfetto/protozero/proto_decoder.h"
#include "src/trace_processor/blob_reader.h"
#include "src/trace_processor/trace_storage.h"
@@ -34,9 +35,17 @@
// from a trace file with these chunks parsed into |trace|.
TraceParser(BlobReader* reader, TraceStorage* trace, uint32_t chunk_size_b);
- void LoadNextChunk();
+ void ParseNextChunk();
private:
+ void ParsePacket(const uint8_t* data, uint32_t length);
+ void ParseFtraceEventBundle(const uint8_t* data, uint32_t length);
+ void ParseFtraceEvent(uint32_t cpu, const uint8_t* data, uint32_t length);
+ void ParseSchedSwitch(uint32_t cpu,
+ uint64_t timestamp,
+ const uint8_t* data,
+ uint32_t length);
+
BlobReader* const reader_;
TraceStorage* const trace_;
const uint32_t chunk_size_b_;
diff --git a/src/trace_processor/trace_parser_unittest.cc b/src/trace_processor/trace_parser_unittest.cc
index 3f454a2..5e4d2a7 100644
--- a/src/trace_processor/trace_parser_unittest.cc
+++ b/src/trace_processor/trace_parser_unittest.cc
@@ -18,6 +18,9 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
+#include "perfetto/base/logging.h"
+#include "perfetto/trace/trace.pb.h"
+#include "perfetto/trace/trace_packet.pb.h"
namespace perfetto {
namespace trace_processor {
@@ -27,8 +30,40 @@
using ::testing::InSequence;
using ::testing::Invoke;
+class FakeStringBlobReader : public BlobReader {
+ public:
+ FakeStringBlobReader(const std::string& data) : data_(data) {}
+ ~FakeStringBlobReader() override {}
+
+ uint32_t Read(uint64_t offset, uint32_t len, uint8_t* dst) override {
+ PERFETTO_CHECK(offset <= data_.size());
+ uint32_t rsize =
+ std::min(static_cast<uint32_t>(data_.size() - offset), len);
+ memcpy(dst, data_.c_str() + offset, rsize);
+ return rsize;
+ }
+
+ private:
+ std::string data_;
+};
+
TEST(TraceParser, LoadSinglePacket) {
- // TODO(lalitm): write this test.
+ protos::Trace trace;
+
+ auto* bundle = trace.add_packet()->mutable_ftrace_events();
+ bundle->set_cpu(10);
+
+ auto* event = bundle->add_event();
+ event->set_timestamp(1000);
+
+ auto* sched_switch = event->mutable_sched_switch();
+ sched_switch->set_prev_pid(10);
+ sched_switch->set_next_prio(100);
+
+ FakeStringBlobReader reader(trace.SerializeAsString());
+ TraceStorage storage;
+ TraceParser parser(&reader, &storage, 1024);
+ parser.ParseNextChunk();
}
TEST(TraceParser, LoadMultiplePacket) {
diff --git a/src/trace_processor/virtual_destructors.cc b/src/trace_processor/virtual_destructors.cc
new file mode 100644
index 0000000..26c6a0c
--- /dev/null
+++ b/src/trace_processor/virtual_destructors.cc
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2017 The Android Open foo Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/trace_processor/blob_reader.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+BlobReader::~BlobReader() {}
+
+} // namespace trace_processor
+} // namespace perfetto