1 /*
2 * Copyright (C) 2021 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "src/trace_processor/dynamic/descendant_generator.h"
18
19 #include <memory>
20 #include <set>
21
22 #include "src/trace_processor/types/trace_processor_context.h"
23 #include "src/trace_processor/util/status_macros.h"
24
25 namespace perfetto {
26 namespace trace_processor {
27 namespace {
GetConstraintColumnIndex(TraceProcessorContext * context)28 uint32_t GetConstraintColumnIndex(TraceProcessorContext* context) {
29 return context->storage->slice_table().GetColumnCount();
30 }
31
32 template <typename T>
ExtendTableWithStartId(const T & table,int64_t constraint_value)33 Table ExtendTableWithStartId(const T& table, int64_t constraint_value) {
34 // Add a new column that includes the constraint.
35 std::unique_ptr<NullableVector<int64_t>> child_ids(
36 new NullableVector<int64_t>());
37 for (uint32_t i = 0; i < table.row_count(); ++i)
38 child_ids->Append(constraint_value);
39 return table.ExtendWithColumn(
40 "start_id", std::move(child_ids),
41 TypedColumn<uint32_t>::default_flags() | TypedColumn<uint32_t>::kHidden);
42 }
43
BuildDescendantsRowMap(const tables::SliceTable & slices,SliceId starting_id,RowMap & rowmap_return)44 base::Status BuildDescendantsRowMap(const tables::SliceTable& slices,
45 SliceId starting_id,
46 RowMap& rowmap_return) {
47 auto start_row = slices.id().IndexOf(starting_id);
48 // The query gave an invalid ID that doesn't exist in the slice table.
49 if (!start_row) {
50 return base::ErrStatus("no row with id %" PRIu32 "",
51 static_cast<uint32_t>(starting_id.value));
52 }
53
54 // All nested descendents must be on the same track, with a ts between
55 // |start_id.ts| and |start_id.ts| + |start_id.dur|, and who's depth is larger
56 // then |start_row|'s. So we just use Filter to select all relevant slices.
57 rowmap_return = slices.FilterToRowMap(
58 {slices.ts().ge(slices.ts()[*start_row]),
59 slices.ts().le(slices.ts()[*start_row] + slices.dur()[*start_row]),
60 slices.track_id().eq(slices.track_id()[*start_row].value),
61 slices.depth().gt(slices.depth()[*start_row])});
62 return base::OkStatus();
63 }
64
BuildDescendantsTable(int64_t constraint_value,const tables::SliceTable & slices,SliceId starting_id,std::unique_ptr<Table> & table_return)65 base::Status BuildDescendantsTable(int64_t constraint_value,
66 const tables::SliceTable& slices,
67 SliceId starting_id,
68 std::unique_ptr<Table>& table_return) {
69 // Build up all the children row ids.
70 RowMap descendants;
71 RETURN_IF_ERROR(BuildDescendantsRowMap(slices, starting_id, descendants));
72
73 table_return.reset(new Table(ExtendTableWithStartId(
74 slices.Apply(std::move(descendants)), constraint_value)));
75 return base::OkStatus();
76 }
77 } // namespace
78
DescendantGenerator(Descendant type,TraceProcessorContext * context)79 DescendantGenerator::DescendantGenerator(Descendant type,
80 TraceProcessorContext* context)
81 : type_(type), context_(context) {}
82
ValidateConstraints(const QueryConstraints & qc)83 base::Status DescendantGenerator::ValidateConstraints(
84 const QueryConstraints& qc) {
85 const auto& cs = qc.constraints();
86
87 int column = static_cast<int>(GetConstraintColumnIndex(context_));
88 auto id_fn = [column](const QueryConstraints::Constraint& c) {
89 return c.column == column && c.op == SQLITE_INDEX_CONSTRAINT_EQ;
90 };
91 bool has_id_cs = std::find_if(cs.begin(), cs.end(), id_fn) != cs.end();
92 return has_id_cs ? base::OkStatus()
93 : base::ErrStatus("Failed to find required constraints");
94 }
95
ComputeTable(const std::vector<Constraint> & cs,const std::vector<Order> &,const BitVector &,std::unique_ptr<Table> & table_return)96 base::Status DescendantGenerator::ComputeTable(
97 const std::vector<Constraint>& cs,
98 const std::vector<Order>&,
99 const BitVector&,
100 std::unique_ptr<Table>& table_return) {
101 const auto& slices = context_->storage->slice_table();
102
103 uint32_t column = GetConstraintColumnIndex(context_);
104 auto constraint_it =
105 std::find_if(cs.begin(), cs.end(), [column](const Constraint& c) {
106 return c.col_idx == column && c.op == FilterOp::kEq;
107 });
108 PERFETTO_DCHECK(constraint_it != cs.end());
109 if (constraint_it == cs.end() ||
110 constraint_it->value.type != SqlValue::Type::kLong) {
111 return base::ErrStatus("invalid start_id");
112 }
113 auto start_id = constraint_it->value.AsLong();
114
115 switch (type_) {
116 case Descendant::kSlice: {
117 RETURN_IF_ERROR(BuildDescendantsTable(
118 start_id, slices, SliceId(static_cast<uint32_t>(start_id)),
119 table_return));
120 return base::OkStatus();
121 }
122
123 case Descendant::kSliceByStack: {
124 auto result = RowMap();
125 auto slice_ids = slices.FilterToRowMap({slices.stack_id().eq(start_id)});
126
127 for (auto id_it = slice_ids.IterateRows(); id_it; id_it.Next()) {
128 auto slice_id = slices.id()[id_it.index()];
129
130 auto descendants = GetDescendantSlices(slices, slice_id);
131 for (auto row_it = descendants->IterateRows(); row_it; row_it.Next()) {
132 result.Insert(row_it.index());
133 }
134 }
135
136 table_return.reset(new Table(
137 ExtendTableWithStartId(slices.Apply(std::move(result)), start_id)));
138 return base::OkStatus();
139 }
140 }
141 return base::ErrStatus("unknown DescendantGenerator type");
142 }
143
CreateSchema()144 Table::Schema DescendantGenerator::CreateSchema() {
145 auto schema = tables::SliceTable::Schema();
146 schema.columns.push_back(Table::Schema::Column{
147 "start_id", SqlValue::Type::kLong, /* is_id = */ false,
148 /* is_sorted = */ false, /* is_hidden = */ true});
149 return schema;
150 }
151
TableName()152 std::string DescendantGenerator::TableName() {
153 switch (type_) {
154 case Descendant::kSlice:
155 return "descendant_slice";
156 case Descendant::kSliceByStack:
157 return "descendant_slice_by_stack";
158 }
159 return "descendant_unknown";
160 }
161
EstimateRowCount()162 uint32_t DescendantGenerator::EstimateRowCount() {
163 return 1;
164 }
165
166 // static
GetDescendantSlices(const tables::SliceTable & slices,SliceId slice_id)167 base::Optional<RowMap> DescendantGenerator::GetDescendantSlices(
168 const tables::SliceTable& slices,
169 SliceId slice_id) {
170 RowMap ret;
171 auto status = BuildDescendantsRowMap(slices, slice_id, ret);
172 if (!status.ok())
173 return base::nullopt;
174 return std::move(ret); // -Wreturn-std-move-in-c++11
175 }
176
177 } // namespace trace_processor
178 } // namespace perfetto
179