• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/experimental_flat_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 
ExperimentalFlatSliceGenerator(TraceProcessorContext * context)27 ExperimentalFlatSliceGenerator::ExperimentalFlatSliceGenerator(
28     TraceProcessorContext* context)
29     : context_(context) {}
30 
ValidateConstraints(const QueryConstraints & qc)31 base::Status ExperimentalFlatSliceGenerator::ValidateConstraints(
32     const QueryConstraints& qc) {
33   using CI = tables::ExperimentalFlatSliceTable::ColumnIndex;
34   bool has_start_bound = false;
35   bool has_end_bound = false;
36   for (const auto& c : qc.constraints()) {
37     has_start_bound |= c.column == static_cast<int>(CI::start_bound) &&
38                        c.op == SQLITE_INDEX_CONSTRAINT_EQ;
39     has_end_bound |= c.column == static_cast<int>(CI::end_bound) &&
40                      c.op == SQLITE_INDEX_CONSTRAINT_EQ;
41   }
42   return has_start_bound && has_end_bound
43              ? base::OkStatus()
44              : base::ErrStatus("Failed to find required constraints");
45 }
46 
ComputeTable(const std::vector<Constraint> & cs,const std::vector<Order> &,const BitVector &,std::unique_ptr<Table> & table_return)47 base::Status ExperimentalFlatSliceGenerator::ComputeTable(
48     const std::vector<Constraint>& cs,
49     const std::vector<Order>&,
50     const BitVector&,
51     std::unique_ptr<Table>& table_return) {
52   using CI = tables::ExperimentalFlatSliceTable::ColumnIndex;
53   auto start_it = std::find_if(cs.begin(), cs.end(), [](const Constraint& c) {
54     return c.col_idx == static_cast<uint32_t>(CI::start_bound) &&
55            c.op == FilterOp::kEq;
56   });
57   auto end_it = std::find_if(cs.begin(), cs.end(), [](const Constraint& c) {
58     return c.col_idx == static_cast<uint32_t>(CI::end_bound) &&
59            c.op == FilterOp::kEq;
60   });
61   // TODO(rsavitski): consider checking the values' types (in case of erroneous
62   // queries passing e.g. null).
63   int64_t start_bound = start_it->value.AsLong();
64   int64_t end_bound = end_it->value.AsLong();
65   table_return = ComputeFlatSliceTable(context_->storage->slice_table(),
66                                        context_->storage->mutable_string_pool(),
67                                        start_bound, end_bound);
68   return base::OkStatus();
69 }
70 
71 std::unique_ptr<tables::ExperimentalFlatSliceTable>
ComputeFlatSliceTable(const tables::SliceTable & slice,StringPool * pool,int64_t start_bound,int64_t end_bound)72 ExperimentalFlatSliceGenerator::ComputeFlatSliceTable(
73     const tables::SliceTable& slice,
74     StringPool* pool,
75     int64_t start_bound,
76     int64_t end_bound) {
77   std::unique_ptr<tables::ExperimentalFlatSliceTable> out(
78       new tables::ExperimentalFlatSliceTable(pool, nullptr));
79 
80   auto insert_slice = [&](uint32_t i, int64_t ts,
81                           tables::TrackTable::Id track_id) {
82     tables::ExperimentalFlatSliceTable::Row row;
83     row.ts = ts;
84     row.dur = -1;
85     row.track_id = track_id;
86     row.category = slice.category()[i];
87     row.name = slice.name()[i];
88     row.arg_set_id = slice.arg_set_id()[i];
89     row.source_id = slice.id()[i];
90     row.start_bound = start_bound;
91     row.end_bound = end_bound;
92     return out->Insert(row).row;
93   };
94   auto insert_sentinel = [&](int64_t ts, TrackId track_id) {
95     tables::ExperimentalFlatSliceTable::Row row;
96     row.ts = ts;
97     row.dur = -1;
98     row.track_id = track_id;
99     row.category = kNullStringId;
100     row.name = kNullStringId;
101     row.arg_set_id = kInvalidArgSetId;
102     row.source_id = base::nullopt;
103     row.start_bound = start_bound;
104     row.end_bound = end_bound;
105     return out->Insert(row).row;
106   };
107 
108   auto terminate_slice = [&](uint32_t out_row, int64_t end_ts) {
109     PERFETTO_DCHECK(out->dur()[out_row] == -1);
110     int64_t out_ts = out->ts()[out_row];
111     out->mutable_dur()->Set(out_row, end_ts - out_ts);
112   };
113 
114   struct ActiveSlice {
115     base::Optional<uint32_t> source_row;
116     uint32_t out_row = std::numeric_limits<uint32_t>::max();
117 
118     bool is_sentinel() const { return !source_row; }
119   };
120   struct Track {
121     std::vector<uint32_t> parents;
122     ActiveSlice active;
123     bool initialized = false;
124   };
125   std::unordered_map<TrackId, Track> tracks;
126 
127   auto maybe_terminate_active_slice = [&](const Track& t, int64_t fin_ts) {
128     int64_t ts = slice.ts()[t.active.source_row.value()];
129     int64_t dur = slice.dur()[t.active.source_row.value()];
130     if (dur == -1 || ts + dur > fin_ts)
131       return false;
132 
133     terminate_slice(t.active.out_row, ts + dur);
134     return true;
135   };
136 
137   // Post-condition: |tracks[track_id].active| will always point to
138   // a slice which finishes after |fin_ts| and has a |dur| == -1 in
139   // |out|.
140   auto output_slices_before = [&](TrackId track_id, int64_t fin_ts) {
141     auto& t = tracks[track_id];
142 
143     // A sentinel slice cannot have parents.
144     PERFETTO_DCHECK(!t.active.is_sentinel() || t.parents.empty());
145 
146     // If we have a sentinel slice active, we have nothing to output.
147     if (t.active.is_sentinel())
148       return;
149 
150     // Try and terminate the current slice (if it ends before |fin_ts|)
151     // If we cannot terminate it, then we leave it as pending for the caller
152     // to terminate.
153     if (!maybe_terminate_active_slice(t, fin_ts))
154       return;
155 
156     // Next, add any parents as appropriate.
157     for (int64_t i = static_cast<int64_t>(t.parents.size()) - 1; i >= 0; --i) {
158       uint32_t source_row = t.parents[static_cast<size_t>(i)];
159       t.parents.pop_back();
160 
161       int64_t active_ts = out->ts()[t.active.out_row];
162       int64_t active_dur = out->dur()[t.active.out_row];
163       PERFETTO_DCHECK(active_dur != -1);
164 
165       t.active.source_row = source_row;
166       t.active.out_row =
167           insert_slice(source_row, active_ts + active_dur, track_id);
168 
169       if (!maybe_terminate_active_slice(t, fin_ts))
170         break;
171     }
172 
173     if (!t.parents.empty())
174       return;
175 
176     // If the active slice is a sentinel, the check at the top of this function
177     // should have caught it; all code only adds slices from source.
178     PERFETTO_DCHECK(!t.active.is_sentinel());
179 
180     int64_t ts = out->ts()[t.active.out_row];
181     int64_t dur = out->dur()[t.active.out_row];
182 
183     // If the active slice is unfinshed, we return that for the caller to
184     // terminate.
185     if (dur == -1)
186       return;
187 
188     // Otherwise, Add a sentinel slice after the end of the active slice.
189     t.active.source_row = base::nullopt;
190     t.active.out_row = insert_sentinel(ts + dur, track_id);
191   };
192 
193   for (uint32_t i = 0; i < slice.row_count(); ++i) {
194     // TODO(lalitm): this can be optimized using a O(logn) lower bound/filter.
195     // Not adding for now as a premature optimization but may be needed down the
196     // line.
197     int64_t ts = slice.ts()[i];
198     if (ts < start_bound)
199       continue;
200 
201     if (ts >= end_bound)
202       break;
203 
204     // Ignore instants as they don't factor into flat slice at all.
205     if (slice.dur()[i] == 0)
206       continue;
207 
208     TrackId track_id = slice.track_id()[i];
209     Track& track = tracks[track_id];
210 
211     // Initalize the track (if needed) by adding a sentinel slice starting at
212     // start_bound.
213     bool is_root = slice.depth()[i] == 0;
214     if (!track.initialized) {
215       // If we are unintialized and our start box picks up slices mid way
216       // through startup, wait until we reach a root slice.
217       if (!is_root)
218         continue;
219 
220       track.active.out_row = insert_sentinel(start_bound, track_id);
221       track.initialized = true;
222     }
223     output_slices_before(track_id, ts);
224     terminate_slice(track.active.out_row, ts);
225 
226     // We should have sentinel slices iff the slice is a root.
227     PERFETTO_DCHECK(track.active.is_sentinel() == is_root);
228 
229     // If our current slice has a parent, that must be the current active slice.
230     if (!is_root) {
231       track.parents.push_back(*track.active.source_row);
232     }
233 
234     // The depth of our slice should also match the depth of the parent stack
235     // (after adding the previous slice).
236     PERFETTO_DCHECK(track.parents.size() == slice.depth()[i]);
237 
238     track.active.source_row = i;
239     track.active.out_row = insert_slice(i, ts, track_id);
240   }
241 
242   for (const auto& track : tracks) {
243     // If the track is not initialized, don't add anything.
244     if (!track.second.initialized)
245       continue;
246 
247     // First, terminate any hanging slices.
248     output_slices_before(track.first, end_bound);
249 
250     // Second, force terminate the final slice to the end bound.
251     terminate_slice(track.second.active.out_row, end_bound);
252   }
253 
254   return out;
255 }
256 
CreateSchema()257 Table::Schema ExperimentalFlatSliceGenerator::CreateSchema() {
258   return tables::ExperimentalFlatSliceTable::Schema();
259 }
260 
TableName()261 std::string ExperimentalFlatSliceGenerator::TableName() {
262   return "experimental_flat_slice";
263 }
264 
EstimateRowCount()265 uint32_t ExperimentalFlatSliceGenerator::EstimateRowCount() {
266   return context_->storage->slice_table().row_count();
267 }
268 
269 }  // namespace trace_processor
270 }  // namespace perfetto
271