1 /*
2  * Copyright (C) 2020 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_slice_generator.h"
18 
19 #include <memory>
20 #include <set>
21 
22 #include "src/trace_processor/types/trace_processor_context.h"
23 
24 namespace perfetto {
25 namespace trace_processor {
26 
DescendantSliceGenerator(TraceProcessorContext * context)27 DescendantSliceGenerator::DescendantSliceGenerator(
28     TraceProcessorContext* context)
29     : context_(context) {}
30 
31 DescendantSliceGenerator::~DescendantSliceGenerator() = default;
32 
ValidateConstraints(const QueryConstraints & qc)33 util::Status DescendantSliceGenerator::ValidateConstraints(
34     const QueryConstraints& qc) {
35   const auto& cs = qc.constraints();
36 
37   auto slice_id_fn = [this](const QueryConstraints::Constraint& c) {
38     return c.column == static_cast<int>(
39                            context_->storage->slice_table().GetColumnCount()) &&
40            c.op == SQLITE_INDEX_CONSTRAINT_EQ;
41   };
42   bool has_slice_id_cs =
43       std::find_if(cs.begin(), cs.end(), slice_id_fn) != cs.end();
44 
45   return has_slice_id_cs
46              ? util::OkStatus()
47              : util::ErrStatus("Failed to find required constraints");
48 }
49 
ComputeTable(const std::vector<Constraint> & cs,const std::vector<Order> &)50 std::unique_ptr<Table> DescendantSliceGenerator::ComputeTable(
51     const std::vector<Constraint>& cs,
52     const std::vector<Order>&) {
53   const auto& slice = context_->storage->slice_table();
54 
55   auto it = std::find_if(cs.begin(), cs.end(), [&slice](const Constraint& c) {
56     return c.col_idx == slice.GetColumnCount() && c.op == FilterOp::kEq;
57   });
58   PERFETTO_DCHECK(it != cs.end());
59 
60   uint32_t start_id = static_cast<uint32_t>(it->value.AsLong());
61   auto descendants = GetDescendantSlices(slice, SliceId(start_id));
62   if (!descendants)
63     return nullptr;
64   Table reduced_slice = slice.Apply(std::move(*descendants));
65 
66   // For every row extend it to match the schema, and return it.
67   std::unique_ptr<NullableVector<uint32_t>> start_ids(
68       new NullableVector<uint32_t>());
69   for (size_t i = 0; i < reduced_slice.row_count(); ++i) {
70     start_ids->Append(start_id);
71   }
72   return std::unique_ptr<Table>(
73       new Table(std::move(reduced_slice)
74                     .ExtendWithColumn("start_id", std::move(start_ids),
75                                       TypedColumn<uint32_t>::default_flags() |
76                                           TypedColumn<uint32_t>::kHidden)));
77 }
78 
CreateSchema()79 Table::Schema DescendantSliceGenerator::CreateSchema() {
80   auto schema = tables::SliceTable::Schema();
81   schema.columns.push_back(Table::Schema::Column{
82       "start_id", SqlValue::Type::kLong, /* is_id = */ false,
83       /* is_sorted = */ false, /* is_hidden = */ true});
84   return schema;
85 }
86 
TableName()87 std::string DescendantSliceGenerator::TableName() {
88   return "descendant_slice";
89 }
90 
EstimateRowCount()91 uint32_t DescendantSliceGenerator::EstimateRowCount() {
92   return 1;
93 }
94 
95 // static
GetDescendantSlices(const tables::SliceTable & slice,SliceId start_id)96 base::Optional<RowMap> DescendantSliceGenerator::GetDescendantSlices(
97     const tables::SliceTable& slice,
98     SliceId start_id) {
99   auto start_row = slice.id().IndexOf(start_id);
100   // The query gave an invalid ID that doesn't exist in the slice table.
101   if (!start_row) {
102     // TODO(lalitm): Ideally this should result in an error, or be filtered out
103     // during ValidateConstraints so we can just dereference |start_row|
104     // directly. However ValidateConstraints doesn't know the value we're
105     // filtering for so can't ensure it exists. For now we return a nullptr
106     // which will cause the query to surface an error with the message "SQL
107     // error: constraint failed".
108     return base::nullopt;
109   }
110 
111   // All nested descendents must be on the same track, with a ts between
112   // |start_id.ts| and |start_id.ts| + |start_id.dur|, and who's depth is larger
113   // then |start_row|'s. So we just use Filter to select all relevant slices.
114   return slice.FilterToRowMap(
115       {slice.ts().ge(slice.ts()[*start_row]),
116        slice.ts().le(slice.ts()[*start_row] + slice.dur()[*start_row]),
117        slice.track_id().eq(slice.track_id()[*start_row].value),
118        slice.depth().gt(slice.depth()[*start_row])});
119 }
120 
121 }  // namespace trace_processor
122 }  // namespace perfetto
123