trace_processor: Add slices from ftrace traces - Refactor slice tracking code from json_trace_parser into slice_tracker. - Introduce Print parsing code in proto_trace_parser. - Add tests for slice tracking code. Change-Id: Iec14860f45e1eb36a884ef1ddd91ba35d2e91132
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn index 910b601..811443f 100644 --- a/src/trace_processor/BUILD.gn +++ b/src/trace_processor/BUILD.gn
@@ -63,6 +63,8 @@ "scoped_db.h", "slice_table.cc", "slice_table.h", + "slice_tracker.cc", + "slice_tracker.h", "sqlite_utils.h", "string_table.cc", "string_table.h", @@ -136,6 +138,7 @@ "query_constraints_unittest.cc", "sched_slice_table_unittest.cc", "sched_tracker_unittest.cc", + "slice_tracker_unittest.cc", "thread_table_unittest.cc", "trace_sorter_unittest.cc", ]
diff --git a/src/trace_processor/json_trace_parser.cc b/src/trace_processor/json_trace_parser.cc index 81e419c..e7a8002 100644 --- a/src/trace_processor/json_trace_parser.cc +++ b/src/trace_processor/json_trace_parser.cc
@@ -26,6 +26,7 @@ #include "perfetto/base/logging.h" #include "perfetto/base/utils.h" #include "src/trace_processor/process_tracker.h" +#include "src/trace_processor/slice_tracker.h" #include "src/trace_processor/trace_processor_context.h" #if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD) || \ @@ -106,7 +107,7 @@ ProcessTracker* procs = context_->process_tracker.get(); TraceStorage* storage = context_->storage.get(); - TraceStorage::NestableSlices* slices = storage->mutable_nestable_slices(); + SliceTracker* slice_tracker = context_->slice_tracker.get(); while (next < end) { Json::Value value; @@ -127,42 +128,19 @@ StringId cat_id = storage->InternString(cat); StringId name_id = storage->InternString(name); UniqueTid utid = procs->UpdateThread(tid, pid); - SlicesStack& stack = threads_[utid]; - - auto add_slice = [slices, &stack, utid, cat_id, - name_id](const Slice& slice) { - if (stack.size() >= std::numeric_limits<uint8_t>::max()) - return; - const uint8_t depth = static_cast<uint8_t>(stack.size()) - 1; - uint64_t parent_stack_id, stack_id; - std::tie(parent_stack_id, stack_id) = GetStackHashes(stack); - slices->AddSlice(slice.start_ts, slice.end_ts - slice.start_ts, utid, - cat_id, name_id, depth, stack_id, parent_stack_id); - }; switch (phase) { case 'B': { // TRACE_EVENT_BEGIN. - MaybeCloseStack(ts, stack); - stack.emplace_back(Slice{cat_id, name_id, ts, 0}); + slice_tracker->Begin(ts, utid, cat_id, name_id); break; } case 'E': { // TRACE_EVENT_END. - PERFETTO_CHECK(!stack.empty()); - MaybeCloseStack(ts, stack); - PERFETTO_CHECK(stack.back().cat_id == cat_id); - PERFETTO_CHECK(stack.back().name_id == name_id); - Slice& slice = stack.back(); - slice.end_ts = slice.start_ts; - add_slice(slice); - stack.pop_back(); + slice_tracker->End(ts, utid, cat_id, name_id); break; } case 'X': { // TRACE_EVENT (scoped event). - MaybeCloseStack(ts, stack); - uint64_t end_ts = ts + value["dur"].asUInt() * 1000; - stack.emplace_back(Slice{cat_id, name_id, ts, end_ts}); - Slice& slice = stack.back(); - add_slice(slice); + uint64_t duration = value["dur"].asUInt() * 1000; + slice_tracker->Scoped(ts, utid, cat_id, name_id, duration); break; } case 'M': { // Metadata events (process and thread names). @@ -178,54 +156,11 @@ } } } - // TODO(primiano): auto-close B slices left open at the end. } offset_ += static_cast<uint64_t>(next - buf); buffer_.erase(buffer_.begin(), buffer_.begin() + (next - buf)); return true; } -void JsonTraceParser::MaybeCloseStack(uint64_t ts, SlicesStack& stack) { - bool check_only = false; - for (int i = static_cast<int>(stack.size()) - 1; i >= 0; i--) { - const Slice& slice = stack[size_t(i)]; - if (slice.end_ts == 0) { - check_only = true; - } - - if (check_only) { - PERFETTO_DCHECK(ts >= slice.start_ts); - PERFETTO_DCHECK(slice.end_ts == 0 || ts <= slice.end_ts); - continue; - } - - if (slice.end_ts <= ts) { - stack.pop_back(); - } - } -} - -// Returns <parent_stack_id, stack_id>, where -// |parent_stack_id| == hash(stack_id - last slice). -std::tuple<uint64_t, uint64_t> JsonTraceParser::GetStackHashes( - const SlicesStack& stack) { - PERFETTO_DCHECK(!stack.empty()); - std::string s; - s.reserve(stack.size() * sizeof(uint64_t) * 2); - constexpr uint64_t kMask = uint64_t(-1) >> 1; - uint64_t parent_stack_id = 0; - for (size_t i = 0; i < stack.size(); i++) { - if (i == stack.size() - 1) - parent_stack_id = i > 0 ? (std::hash<std::string>{}(s)) & kMask : 0; - const Slice& slice = stack[i]; - s.append(reinterpret_cast<const char*>(&slice.cat_id), - sizeof(slice.cat_id)); - s.append(reinterpret_cast<const char*>(&slice.name_id), - sizeof(slice.name_id)); - } - uint64_t stack_id = (std::hash<std::string>{}(s)) & kMask; - return std::make_tuple(parent_stack_id, stack_id); -} - } // namespace trace_processor } // namespace perfetto
diff --git a/src/trace_processor/json_trace_parser.h b/src/trace_processor/json_trace_parser.h index c51fbba..b0e39ec 100644 --- a/src/trace_processor/json_trace_parser.h +++ b/src/trace_processor/json_trace_parser.h
@@ -44,22 +44,9 @@ bool Parse(std::unique_ptr<uint8_t[]>, size_t) override; private: - struct Slice { - StringId cat_id; - StringId name_id; - uint64_t start_ts; - uint64_t end_ts; // Only for complete events (scoped TRACE_EVENT macros). - }; - using SlicesStack = std::vector<Slice>; - - static inline void MaybeCloseStack(uint64_t end_ts, SlicesStack&); - static inline std::tuple<uint64_t, uint64_t> GetStackHashes( - const SlicesStack&); - TraceProcessorContext* const context_; uint64_t offset_ = 0; std::vector<char> buffer_; - std::unordered_map<UniqueTid, SlicesStack> threads_; }; } // namespace trace_processor
diff --git a/src/trace_processor/process_tracker.cc b/src/trace_processor/process_tracker.cc index fc10f26..731e15c 100644 --- a/src/trace_processor/process_tracker.cc +++ b/src/trace_processor/process_tracker.cc
@@ -112,6 +112,14 @@ return upid; } +UniquePid ProcessTracker::UpdateProcess(uint32_t pid) { + UniquePid upid; + TraceStorage::Process* process; + std::tie(upid, process) = GetOrCreateProcess(pid, 0 /* start_ns */); + UpdateThread(/*tid=*/pid, pid); // Create an entry for the main thread. + return upid; +} + std::tuple<UniquePid, TraceStorage::Process*> ProcessTracker::GetOrCreateProcess(uint32_t pid, uint64_t start_ns) { auto pids_pair = pids_.equal_range(pid);
diff --git a/src/trace_processor/process_tracker.h b/src/trace_processor/process_tracker.h index 5bd4d2b..87d67cf 100644 --- a/src/trace_processor/process_tracker.h +++ b/src/trace_processor/process_tracker.h
@@ -67,6 +67,11 @@ // Virtual for testing. virtual UniquePid UpdateProcess(uint32_t pid, base::StringView name); + // Called when a process is seen in a process tree. Retrieves the UniquePid + // for that pid or assigns a new one. + // Virtual for testing. + virtual UniquePid UpdateProcess(uint32_t pid); + // Returns the bounds of a range that includes all UniquePids that have the // requested pid. UniqueProcessBounds UpidsForPid(uint32_t pid) {
diff --git a/src/trace_processor/proto_trace_parser.cc b/src/trace_processor/proto_trace_parser.cc index b3d3dda..8cf1101 100644 --- a/src/trace_processor/proto_trace_parser.cc +++ b/src/trace_processor/proto_trace_parser.cc
@@ -24,6 +24,7 @@ #include "perfetto/protozero/proto_decoder.h" #include "src/trace_processor/process_tracker.h" #include "src/trace_processor/sched_tracker.h" +#include "src/trace_processor/slice_tracker.h" #include "src/trace_processor/trace_processor_context.h" #include "perfetto/trace/trace.pb.h" @@ -32,6 +33,53 @@ namespace perfetto { namespace trace_processor { +// We have to handle trace_marker events of a few different types: +// 1. some random text +// 2. B|1636|pokeUserActivity +// 3. E|1636 +// 4. C|1636|wq:monitor|0 +bool ParseSystraceTracePoint(base::StringView str, SystraceTracePoint* out) { + // THIS char* IS NOT NULL TERMINATED. + const char* s = str.data(); + size_t len = str.size(); + + // If str matches '[BEC]\|[0-9]+[\|\n]' set pid_length to the length of + // the number. Otherwise return false. + if (len < 3 || s[1] != '|') + return false; + if (s[0] != 'B' && s[0] != 'E' && s[0] != 'C') + return false; + size_t pid_length; + for (size_t i = 2;; i++) { + if (i >= len) + return false; + if (s[i] == '|' || s[i] == '\n') { + pid_length = i - 2; + break; + } + if (s[i] < '0' || s[i] > '9') + return false; + } + + std::string pid_str(s + 2, pid_length); + out->pid = static_cast<uint32_t>(std::stoi(pid_str.c_str())); + + out->phase = s[0]; + switch (s[0]) { + case 'B': { + size_t name_index = 2 + pid_length + 1; + out->name = base::StringView(s + name_index, len - name_index); + return true; + } + case 'E': + return true; + case 'C': + return true; + default: + return false; + } +} + using protozero::ProtoDecoder; using protozero::proto_utils::kFieldTypeLengthDelimited; @@ -140,6 +188,12 @@ ParseCpuFreq(timestamp, ftrace.slice(fld_off, fld.size())); break; } + case protos::FtraceEvent::kPrintFieldNumber: { + PERFETTO_DCHECK(timestamp > 0); + const size_t fld_off = ftrace.offset_of(fld.data()); + ParsePrint(cpu, timestamp, ftrace.slice(fld_off, fld.size())); + break; + } default: break; } @@ -200,5 +254,39 @@ PERFETTO_DCHECK(decoder.IsEndOfBuffer()); } +void ProtoTraceParser::ParsePrint(uint32_t, + uint64_t timestamp, + TraceBlobView print) { + ProtoDecoder decoder(print.data(), print.length()); + + base::StringView buf{}; + for (auto fld = decoder.ReadField(); fld.id != 0; fld = decoder.ReadField()) { + if (fld.id == protos::PrintFtraceEvent::kBufFieldNumber) { + buf = fld.as_string(); + break; + } + } + + SystraceTracePoint point{}; + if (!ParseSystraceTracePoint(buf, &point)) + return; + + UniquePid upid = context_->process_tracker->UpdateProcess(point.pid); + + switch (point.phase) { + case 'B': { + StringId name_id = context_->storage->InternString(point.name); + context_->slice_tracker->Begin(timestamp, upid, 0 /*cat_id*/, name_id); + break; + } + + case 'E': { + context_->slice_tracker->End(timestamp, upid); + break; + } + } + PERFETTO_DCHECK(decoder.IsEndOfBuffer()); +} + } // namespace trace_processor } // namespace perfetto
diff --git a/src/trace_processor/proto_trace_parser.h b/src/trace_processor/proto_trace_parser.h index 2130beb..be3bc40 100644 --- a/src/trace_processor/proto_trace_parser.h +++ b/src/trace_processor/proto_trace_parser.h
@@ -20,13 +20,34 @@ #include <stdint.h> #include <memory> +#include "perfetto/base/string_view.h" #include "src/trace_processor/trace_blob_view.h" +#include "src/trace_processor/trace_storage.h" namespace perfetto { namespace trace_processor { class TraceProcessorContext; +struct SystraceTracePoint { + char phase; + uint32_t pid; + + // For phase = 'B' and phase = 'C' only. + base::StringView name; + + // For phase = 'C' only. + int64_t value; +}; + +inline bool operator==(const SystraceTracePoint& x, + const SystraceTracePoint& y) { + return std::tie(x.phase, x.pid, x.name, x.value) == + std::tie(y.phase, y.pid, y.name, y.value); +} + +bool ParseSystraceTracePoint(base::StringView, SystraceTracePoint* out); + class ProtoTraceParser { public: explicit ProtoTraceParser(TraceProcessorContext*); @@ -40,6 +61,7 @@ void ParseProcessTree(TraceBlobView); void ParseSchedSwitch(uint32_t cpu, uint64_t timestamp, TraceBlobView); void ParseCpuFreq(uint64_t timestamp, TraceBlobView); + void ParsePrint(uint32_t cpu, uint64_t timestamp, TraceBlobView); void ParseThread(TraceBlobView); void ParseProcess(TraceBlobView);
diff --git a/src/trace_processor/proto_trace_parser_unittest.cc b/src/trace_processor/proto_trace_parser_unittest.cc index cb1c254..965fc5b 100644 --- a/src/trace_processor/proto_trace_parser_unittest.cc +++ b/src/trace_processor/proto_trace_parser_unittest.cc
@@ -18,6 +18,7 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "perfetto/base/string_view.h" #include "src/trace_processor/process_tracker.h" #include "src/trace_processor/proto_trace_parser.h" #include "src/trace_processor/sched_tracker.h" @@ -283,6 +284,15 @@ Tokenize(trace); } +TEST(SystraceParserTest, SystraceEvent) { + SystraceTracePoint result{}; + ASSERT_TRUE(ParseSystraceTracePoint(base::StringView("B|1|foo"), &result)); + EXPECT_EQ(result, (SystraceTracePoint{'B', 1, base::StringView("foo"), 0})); + + ASSERT_TRUE(ParseSystraceTracePoint(base::StringView("B|42|Bar"), &result)); + EXPECT_EQ(result, (SystraceTracePoint{'B', 42, base::StringView("Bar"), 0})); +} + } // namespace } // namespace trace_processor } // namespace perfetto
diff --git a/src/trace_processor/slice_tracker.cc b/src/trace_processor/slice_tracker.cc new file mode 100644 index 0000000..db2a17d --- /dev/null +++ b/src/trace_processor/slice_tracker.cc
@@ -0,0 +1,131 @@ +/* + * Copyright (C) 2018 The Android Open Source 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 <stdint.h> + +#include "src/trace_processor/slice_tracker.h" +#include "src/trace_processor/trace_processor_context.h" +#include "src/trace_processor/trace_storage.h" + +namespace perfetto { +namespace trace_processor { + +SliceTracker::SliceTracker(TraceProcessorContext* context) + : context_(context) {} + +SliceTracker::~SliceTracker() = default; + +void SliceTracker::Begin(uint64_t timestamp, + UniqueTid utid, + StringId cat, + StringId name) { + auto& stack = threads_[utid]; + MaybeCloseStack(timestamp, stack); + stack.emplace_back(Slice{cat, name, timestamp, 0}); +} + +void SliceTracker::Scoped(uint64_t timestamp, + UniqueTid utid, + StringId cat, + StringId name, + uint64_t duration) { + auto& stack = threads_[utid]; + MaybeCloseStack(timestamp, stack); + stack.emplace_back(Slice{cat, name, timestamp, timestamp + duration}); + CompleteSlice(utid); +} + +void SliceTracker::End(uint64_t timestamp, + UniqueTid utid, + StringId cat, + StringId name) { + auto& stack = threads_[utid]; + MaybeCloseStack(timestamp, stack); + PERFETTO_CHECK(!stack.empty()); + + PERFETTO_CHECK(cat == 0 || stack.back().cat_id == cat); + PERFETTO_CHECK(name == 0 || stack.back().name_id == name); + + Slice& slice = stack.back(); + slice.end_ts = timestamp; + + CompleteSlice(utid); + // TODO(primiano): auto-close B slices left open at the end. +} + +void SliceTracker::CompleteSlice(UniqueTid utid) { + auto& stack = threads_[utid]; + if (stack.size() >= std::numeric_limits<uint8_t>::max()) { + stack.pop_back(); + return; + } + const uint8_t depth = static_cast<uint8_t>(stack.size()) - 1; + + uint64_t parent_stack_id, stack_id; + std::tie(parent_stack_id, stack_id) = GetStackHashes(stack); + + Slice& slice = stack.back(); + auto* slices = context_->storage->mutable_nestable_slices(); + slices->AddSlice(slice.start_ts, slice.end_ts - slice.start_ts, utid, 0, + slice.name_id, depth, stack_id, parent_stack_id); + + stack.pop_back(); +} + +void SliceTracker::MaybeCloseStack(uint64_t ts, SlicesStack& stack) { + bool check_only = false; + for (int i = static_cast<int>(stack.size()) - 1; i >= 0; i--) { + const Slice& slice = stack[size_t(i)]; + if (slice.end_ts == 0) { + check_only = true; + } + + if (check_only) { + PERFETTO_DCHECK(ts >= slice.start_ts); + PERFETTO_DCHECK(slice.end_ts == 0 || ts <= slice.end_ts); + continue; + } + + if (slice.end_ts <= ts) { + stack.pop_back(); + } + } +} + +// Returns <parent_stack_id, stack_id>, where +// |parent_stack_id| == hash(stack_id - last slice). +std::tuple<uint64_t, uint64_t> SliceTracker::GetStackHashes( + const SlicesStack& stack) { + PERFETTO_DCHECK(!stack.empty()); + std::string s; + s.reserve(stack.size() * sizeof(uint64_t) * 2); + constexpr uint64_t kMask = uint64_t(-1) >> 1; + uint64_t parent_stack_id = 0; + for (size_t i = 0; i < stack.size(); i++) { + if (i == stack.size() - 1) + parent_stack_id = i > 0 ? (std::hash<std::string>{}(s)) & kMask : 0; + const Slice& slice = stack[i]; + s.append(reinterpret_cast<const char*>(&slice.cat_id), + sizeof(slice.cat_id)); + s.append(reinterpret_cast<const char*>(&slice.name_id), + sizeof(slice.name_id)); + } + uint64_t stack_id = (std::hash<std::string>{}(s)) & kMask; + return std::make_tuple(parent_stack_id, stack_id); +} + +} // namespace trace_processor +} // namespace perfetto
diff --git a/src/trace_processor/slice_tracker.h b/src/trace_processor/slice_tracker.h new file mode 100644 index 0000000..5197527 --- /dev/null +++ b/src/trace_processor/slice_tracker.h
@@ -0,0 +1,68 @@ +/* + * Copyright (C) 2018 The Android Open Source 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. + */ + +#ifndef SRC_TRACE_PROCESSOR_SLICE_TRACKER_H_ +#define SRC_TRACE_PROCESSOR_SLICE_TRACKER_H_ + +#include <stdint.h> + +#include "src/trace_processor/trace_storage.h" + +namespace perfetto { +namespace trace_processor { + +class TraceProcessorContext; + +class SliceTracker { + public: + explicit SliceTracker(TraceProcessorContext*); + ~SliceTracker(); + + void Begin(uint64_t timestamp, UniqueTid utid, StringId cat, StringId name); + + void Scoped(uint64_t timestamp, + UniqueTid utid, + StringId cat, + StringId name, + uint64_t duration); + + void End(uint64_t timestamp, + UniqueTid utid, + StringId opt_cat = {}, + StringId opt_name = {}); + + private: + struct Slice { + StringId cat_id; + StringId name_id; + uint64_t start_ts; + uint64_t end_ts; // Only for complete events (scoped TRACE_EVENT macros). + }; + using SlicesStack = std::vector<Slice>; + + static inline void MaybeCloseStack(uint64_t end_ts, SlicesStack&); + static inline std::tuple<uint64_t, uint64_t> GetStackHashes( + const SlicesStack&); + void CompleteSlice(UniqueTid tid); + + TraceProcessorContext* const context_; + std::unordered_map<UniqueTid, SlicesStack> threads_; +}; + +} // namespace trace_processor +} // namespace perfetto + +#endif // SRC_TRACE_PROCESSOR_SLICE_TRACKER_H_
diff --git a/src/trace_processor/slice_tracker_unittest.cc b/src/trace_processor/slice_tracker_unittest.cc new file mode 100644 index 0000000..8eb72b9 --- /dev/null +++ b/src/trace_processor/slice_tracker_unittest.cc
@@ -0,0 +1,123 @@ +/* + * Copyright (C) 2018 The Android Open Source 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 <vector> + +#include "src/trace_processor/slice_tracker.h" +#include "src/trace_processor/trace_processor_context.h" +#include "src/trace_processor/trace_storage.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace perfetto { +namespace trace_processor { +namespace { + +using ::testing::ElementsAre; +using ::testing::Eq; + +struct SliceInfo { + uint64_t start; + uint64_t duration; + + bool operator==(const SliceInfo& other) const { + return std::tie(start, duration) == std::tie(other.start, other.duration); + } +}; + +inline void PrintTo(const SliceInfo& info, ::std::ostream* os) { + *os << "SliceInfo{" << info.start << ", " << info.duration << "}"; +} + +std::vector<SliceInfo> ToSliceInfo(const TraceStorage::NestableSlices& slices) { + std::vector<SliceInfo> infos; + for (size_t i = 0; i < slices.slice_count(); i++) { + infos.emplace_back(SliceInfo{slices.start_ns()[i], slices.durations()[i]}); + } + return infos; +} + +TEST(SliceTrackerTest, OneSliceDetailed) { + TraceProcessorContext context; + context.storage.reset(new TraceStorage()); + SliceTracker tracker(&context); + + tracker.Begin(2 /*ts*/, 42 /*tid*/, 0 /*cat*/, 1 /*name*/); + tracker.End(10 /*ts*/, 42 /*tid*/, 0 /*cat*/, 1 /*name*/); + + auto slices = context.storage->nestable_slices(); + EXPECT_EQ(slices.slice_count(), 1); + EXPECT_EQ(slices.start_ns()[0], 2); + EXPECT_EQ(slices.durations()[0], 8); + EXPECT_EQ(slices.cats()[0], 0); + EXPECT_EQ(slices.names()[0], 1); + EXPECT_EQ(slices.utids()[0], 42); + EXPECT_EQ(slices.depths()[0], 0); +} + +TEST(SliceTrackerTest, TwoSliceDetailed) { + TraceProcessorContext context; + context.storage.reset(new TraceStorage()); + SliceTracker tracker(&context); + + tracker.Begin(2 /*ts*/, 42 /*tid*/, 0 /*cat*/, 1 /*name*/); + tracker.Begin(3 /*ts*/, 42 /*tid*/, 0 /*cat*/, 2 /*name*/); + tracker.End(5 /*ts*/, 42 /*tid*/); + tracker.End(10 /*ts*/, 42 /*tid*/); + + auto slices = context.storage->nestable_slices(); + + EXPECT_EQ(slices.slice_count(), 2); + + EXPECT_EQ(slices.start_ns()[0], 3); + EXPECT_EQ(slices.durations()[0], 2); + EXPECT_EQ(slices.cats()[0], 0); + EXPECT_EQ(slices.names()[0], 2); + EXPECT_EQ(slices.utids()[0], 42); + EXPECT_EQ(slices.depths()[0], 1); + + EXPECT_EQ(slices.start_ns()[1], 2); + EXPECT_EQ(slices.durations()[1], 8); + EXPECT_EQ(slices.cats()[1], 0); + EXPECT_EQ(slices.names()[1], 1); + EXPECT_EQ(slices.utids()[1], 42); + EXPECT_EQ(slices.depths()[1], 0); + + EXPECT_EQ(slices.parent_stack_ids()[1], 0); + EXPECT_EQ(slices.stack_ids()[1], slices.parent_stack_ids()[0]); + EXPECT_NE(slices.stack_ids()[0], 0); +} + +TEST(SliceTrackerTest, Scoped) { + TraceProcessorContext context; + context.storage.reset(new TraceStorage()); + SliceTracker tracker(&context); + + tracker.Begin(0 /*ts*/, 42 /*tid*/, 0, 0); + tracker.Begin(1 /*ts*/, 42 /*tid*/, 0, 0); + tracker.Scoped(2 /*ts*/, 42 /*tid*/, 0, 0, 6); + tracker.End(9 /*ts*/, 42 /*tid*/); + tracker.End(10 /*ts*/, 42 /*tid*/); + + auto slices = ToSliceInfo(context.storage->nestable_slices()); + EXPECT_THAT(slices, + ElementsAre(SliceInfo{2, 6}, SliceInfo{1, 8}, SliceInfo{0, 10})); +} + +} // namespace +} // namespace trace_processor +} // namespace perfetto
diff --git a/src/trace_processor/trace_processor.cc b/src/trace_processor/trace_processor.cc index 62b58a1..ddda225 100644 --- a/src/trace_processor/trace_processor.cc +++ b/src/trace_processor/trace_processor.cc
@@ -28,6 +28,7 @@ #include "src/trace_processor/sched_slice_table.h" #include "src/trace_processor/sched_tracker.h" #include "src/trace_processor/slice_table.h" +#include "src/trace_processor/slice_tracker.h" #include "src/trace_processor/string_table.h" #include "src/trace_processor/table.h" #include "src/trace_processor/thread_table.h" @@ -43,6 +44,7 @@ PERFETTO_CHECK(sqlite3_open(":memory:", &db) == SQLITE_OK); db_.reset(std::move(db)); + context_.slice_tracker.reset(new SliceTracker(&context_)); context_.sched_tracker.reset(new SchedTracker(&context_)); context_.proto_parser.reset(new ProtoTraceParser(&context_)); context_.process_tracker.reset(new ProcessTracker(&context_));
diff --git a/src/trace_processor/trace_processor_context.cc b/src/trace_processor/trace_processor_context.cc index a26e30c..477ec09 100644 --- a/src/trace_processor/trace_processor_context.cc +++ b/src/trace_processor/trace_processor_context.cc
@@ -20,6 +20,7 @@ #include "src/trace_processor/process_tracker.h" #include "src/trace_processor/proto_trace_parser.h" #include "src/trace_processor/sched_tracker.h" +#include "src/trace_processor/slice_tracker.h" #include "src/trace_processor/trace_sorter.h" namespace perfetto {
diff --git a/src/trace_processor/trace_processor_context.h b/src/trace_processor/trace_processor_context.h index 493c39a..f8432c6 100644 --- a/src/trace_processor/trace_processor_context.h +++ b/src/trace_processor/trace_processor_context.h
@@ -22,6 +22,7 @@ namespace perfetto { namespace trace_processor { +class SliceTracker; class ProcessTracker; class TraceStorage; class SchedTracker; @@ -35,6 +36,7 @@ TraceProcessorContext(); ~TraceProcessorContext(); + std::unique_ptr<SliceTracker> slice_tracker; std::unique_ptr<ProcessTracker> process_tracker; std::unique_ptr<SchedTracker> sched_tracker; std::unique_ptr<TraceStorage> storage;
diff --git a/src/trace_processor/trace_storage.h b/src/trace_processor/trace_storage.h index 925f307..2fe305d 100644 --- a/src/trace_processor/trace_storage.h +++ b/src/trace_processor/trace_storage.h
@@ -103,7 +103,7 @@ const std::deque<uint64_t>& cycles() const { return cycles_; } private: - // Each vector below has the same number of entries (the number of slices + // Each deque below has the same number of entries (the number of slices // in the trace for the CPU). std::deque<uint64_t> start_ns_; std::deque<uint64_t> durations_;