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