• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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/db/table.h"
18 
19 #include <algorithm>
20 #include <cstdint>
21 #include <memory>
22 #include <utility>
23 #include <vector>
24 
25 #include "perfetto/base/logging.h"
26 #include "perfetto/public/compiler.h"
27 #include "perfetto/trace_processor/ref_counted.h"
28 #include "src/trace_processor/containers/row_map.h"
29 #include "src/trace_processor/containers/string_pool.h"
30 #include "src/trace_processor/db/column.h"
31 #include "src/trace_processor/db/column/arrangement_overlay.h"
32 #include "src/trace_processor/db/column/data_layer.h"
33 #include "src/trace_processor/db/column/range_overlay.h"
34 #include "src/trace_processor/db/column/selector_overlay.h"
35 #include "src/trace_processor/db/column/types.h"
36 #include "src/trace_processor/db/column_storage_overlay.h"
37 #include "src/trace_processor/db/query_executor.h"
38 
39 namespace perfetto::trace_processor {
40 
41 namespace {
42 using Indices = column::DataLayerChain::Indices;
43 }
44 
Table(StringPool * pool,uint32_t row_count,std::vector<ColumnLegacy> columns,std::vector<ColumnStorageOverlay> overlays)45 Table::Table(StringPool* pool,
46              uint32_t row_count,
47              std::vector<ColumnLegacy> columns,
48              std::vector<ColumnStorageOverlay> overlays)
49     : string_pool_(pool),
50       row_count_(row_count),
51       overlays_(std::move(overlays)),
52       columns_(std::move(columns)) {
53   PERFETTO_DCHECK(string_pool_);
54 }
55 
56 Table::~Table() = default;
57 
operator =(Table && other)58 Table& Table::operator=(Table&& other) noexcept {
59   row_count_ = other.row_count_;
60   string_pool_ = other.string_pool_;
61 
62   overlays_ = std::move(other.overlays_);
63   columns_ = std::move(other.columns_);
64 
65   storage_layers_ = std::move(other.storage_layers_);
66   null_layers_ = std::move(other.null_layers_);
67   overlay_layers_ = std::move(other.overlay_layers_);
68   chains_ = std::move(other.chains_);
69 
70   for (ColumnLegacy& col : columns_) {
71     col.table_ = this;
72   }
73   return *this;
74 }
75 
Copy() const76 Table Table::Copy() const {
77   Table table = CopyExceptOverlays();
78   for (const ColumnStorageOverlay& overlay : overlays_) {
79     table.overlays_.emplace_back(overlay.Copy());
80   }
81   table.OnConstructionCompleted(storage_layers_, null_layers_, overlay_layers_);
82   return table;
83 }
84 
CopyExceptOverlays() const85 Table Table::CopyExceptOverlays() const {
86   std::vector<ColumnLegacy> cols;
87   cols.reserve(columns_.size());
88   for (const ColumnLegacy& col : columns_) {
89     cols.emplace_back(col, col.index_in_table(), col.overlay_index());
90   }
91   return {string_pool_, row_count_, std::move(cols), {}};
92 }
93 
QueryToRowMap(const Query & q) const94 RowMap Table::QueryToRowMap(const Query& q) const {
95   // We need to delay creation of the chains to this point because of Chrome
96   // does not want the binary size overhead of including the chain
97   // implementations. As they also don't query tables (instead just iterating)
98   // over them), using a combination of dead code elimination and linker
99   // stripping all chain related code be removed.
100   //
101   // From rough benchmarking, this has a negligible impact on peformance as this
102   // branch is almost never taken.
103   if (PERFETTO_UNLIKELY(chains_.size() != columns_.size())) {
104     CreateChains();
105   }
106 
107   // Apply the query constraints.
108   RowMap rm = QueryExecutor::FilterLegacy(this, q.constraints);
109 
110   if (q.order_type != Query::OrderType::kSort) {
111     ApplyDistinct(q, &rm);
112   }
113 
114   // Fastpath for one sort, no distinct and limit 1. This type of query means we
115   // need to run Max/Min on orderby column and there is no need for sorting.
116   if (q.order_type == Query::OrderType::kSort && q.orders.size() == 1 &&
117       q.limit.has_value() && *q.limit == 1) {
118     Order o = q.orders.front();
119     std::vector<uint32_t> table_indices = std::move(rm).TakeAsIndexVector();
120     auto indices = Indices::Create(table_indices, Indices::State::kMonotonic);
121     std::optional<Token> ret_tok;
122 
123     if (o.desc) {
124       ret_tok = ChainForColumn(o.col_idx).MaxElement(indices);
125     } else {
126       ret_tok = ChainForColumn(o.col_idx).MinElement(indices);
127     }
128 
129     if (!ret_tok.has_value()) {
130       return RowMap();
131     }
132 
133     return RowMap(std::vector<uint32_t>{ret_tok->payload});
134   }
135 
136   if (q.order_type != Query::OrderType::kDistinct && !q.orders.empty()) {
137     ApplySort(q, &rm);
138   }
139 
140   if (!q.limit.has_value() && q.offset == 0) {
141     return rm;
142   }
143 
144   uint32_t end = rm.size();
145   uint32_t start = std::min(q.offset, end);
146 
147   if (q.limit) {
148     end = std::min(end, *q.limit + q.offset);
149   }
150 
151   return rm.SelectRows(RowMap(start, end));
152 }
153 
Sort(const std::vector<Order> & ob) const154 Table Table::Sort(const std::vector<Order>& ob) const {
155   if (ob.empty()) {
156     return Copy();
157   }
158 
159   // Return a copy of this table with the RowMaps using the computed ordered
160   // RowMap.
161   Table table = CopyExceptOverlays();
162   Query q;
163   q.orders = ob;
164   RowMap rm = QueryToRowMap(q);
165   for (const ColumnStorageOverlay& overlay : overlays_) {
166     table.overlays_.emplace_back(overlay.SelectRows(rm));
167     PERFETTO_DCHECK(table.overlays_.back().size() == table.row_count());
168   }
169 
170   // Remove the sorted and row set flags from all the columns.
171   for (auto& col : table.columns_) {
172     col.flags_ &= ~ColumnLegacy::Flag::kSorted;
173     col.flags_ &= ~ColumnLegacy::Flag::kSetId;
174   }
175 
176   // For the first order by, make the column flag itself as sorted but
177   // only if the sort was in ascending order.
178   if (!ob.front().desc) {
179     table.columns_[ob.front().col_idx].flags_ |= ColumnLegacy::Flag::kSorted;
180   }
181 
182   std::vector<RefPtr<column::DataLayer>> overlay_layers(table.overlays_.size());
183   for (uint32_t i = 0; i < table.overlays_.size(); ++i) {
184     if (table.overlays_[i].row_map().IsIndexVector()) {
185       overlay_layers[i].reset(new column::ArrangementOverlay(
186           table.overlays_[i].row_map().GetIfIndexVector(),
187           column::DataLayerChain::Indices::State::kNonmonotonic));
188     } else if (table.overlays_[i].row_map().IsBitVector()) {
189       overlay_layers[i].reset(new column::SelectorOverlay(
190           table.overlays_[i].row_map().GetIfBitVector()));
191     } else if (table.overlays_[i].row_map().IsRange()) {
192       overlay_layers[i].reset(
193           new column::RangeOverlay(table.overlays_[i].row_map().GetIfIRange()));
194     }
195   }
196   table.OnConstructionCompleted(storage_layers_, null_layers_,
197                                 std::move(overlay_layers));
198   return table;
199 }
200 
OnConstructionCompleted(std::vector<RefPtr<column::DataLayer>> storage_layers,std::vector<RefPtr<column::DataLayer>> null_layers,std::vector<RefPtr<column::DataLayer>> overlay_layers)201 void Table::OnConstructionCompleted(
202     std::vector<RefPtr<column::DataLayer>> storage_layers,
203     std::vector<RefPtr<column::DataLayer>> null_layers,
204     std::vector<RefPtr<column::DataLayer>> overlay_layers) {
205   for (ColumnLegacy& col : columns_) {
206     col.BindToTable(this, string_pool_);
207   }
208   PERFETTO_CHECK(storage_layers.size() == columns_.size());
209   PERFETTO_CHECK(null_layers.size() == columns_.size());
210   PERFETTO_CHECK(overlay_layers.size() == overlays_.size());
211   storage_layers_ = std::move(storage_layers);
212   null_layers_ = std::move(null_layers);
213   overlay_layers_ = std::move(overlay_layers);
214 }
215 
CreateChains() const216 void Table::CreateChains() const {
217   chains_.resize(columns_.size());
218   for (uint32_t i = 0; i < columns_.size(); ++i) {
219     chains_[i] = storage_layers_[i]->MakeChain();
220     if (const auto& null_overlay = null_layers_[i]; null_overlay.get()) {
221       chains_[i] = null_overlay->MakeChain(std::move(chains_[i]));
222     }
223     const auto& oly_idx = columns_[i].overlay_index();
224     if (const auto& overlay = overlay_layers_[oly_idx]; overlay.get()) {
225       chains_[i] = overlay->MakeChain(
226           std::move(chains_[i]),
227           column::DataLayer::ChainCreationArgs{columns_[i].IsSorted()});
228     }
229   }
230 }
231 
ApplyDistinct(const Query & q,RowMap * rm) const232 void Table::ApplyDistinct(const Query& q, RowMap* rm) const {
233   auto& ob = q.orders;
234   PERFETTO_DCHECK(!ob.empty());
235 
236   // `q.orders` should be treated here only as information on what should we
237   // run distinct on, they should not be used for subsequent sorting.
238   // TODO(mayzner): Remove the check after we implement the multi column
239   // distinct.
240   PERFETTO_DCHECK(ob.size() == 1);
241 
242   std::vector<uint32_t> table_indices = std::move(*rm).TakeAsIndexVector();
243   auto indices = Indices::Create(table_indices, Indices::State::kMonotonic);
244   ChainForColumn(ob.front().col_idx).Distinct(indices);
245   PERFETTO_DCHECK(indices.tokens.size() <= table_indices.size());
246 
247   for (uint32_t i = 0; i < indices.tokens.size(); ++i) {
248     table_indices[i] = indices.tokens[i].payload;
249   }
250   table_indices.resize(indices.tokens.size());
251 
252   // Sorting that happens later might require indices to preserve ordering.
253   // TODO(mayzner): Needs to be changed after implementing multi column
254   // distinct.
255   if (q.order_type == Query::OrderType::kDistinctAndSort) {
256     std::sort(table_indices.begin(), table_indices.end());
257   }
258 
259   *rm = RowMap(std::move(table_indices));
260 }
261 
ApplySort(const Query & q,RowMap * rm) const262 void Table::ApplySort(const Query& q, RowMap* rm) const {
263   const auto& ob = q.orders;
264   // Return the RowMap directly if there is a single constraint to sort the
265   // table by a column which is already sorted.
266   const auto& first_col = columns_[ob.front().col_idx];
267   if (ob.size() == 1 && first_col.IsSorted() && !ob.front().desc)
268     return;
269 
270   // Build an index vector with all the indices for the first |size_| rows.
271   std::vector<uint32_t> idx = std::move(*rm).TakeAsIndexVector();
272   if (ob.size() == 1 && first_col.IsSorted()) {
273     // We special case a single constraint in descending order as this
274     // happens any time the |max| function is used in SQLite. We can be
275     // more efficient as this column is already sorted so we simply need
276     // to reverse the order of this column.
277     PERFETTO_DCHECK(ob.front().desc);
278     std::reverse(idx.begin(), idx.end());
279   } else {
280     QueryExecutor::SortLegacy(this, ob, idx);
281   }
282 
283   *rm = RowMap(std::move(idx));
284 }
285 
286 }  // namespace perfetto::trace_processor
287