1 /*
2 * Copyright (C) 2023 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/column/set_id_storage.h"
18
19 #include <algorithm>
20 #include <cstdint>
21 #include <functional>
22 #include <memory>
23 #include <optional>
24 #include <string>
25 #include <unordered_set>
26 #include <utility>
27 #include <vector>
28
29 #include "perfetto/base/logging.h"
30 #include "perfetto/public/compiler.h"
31 #include "perfetto/trace_processor/basic_types.h"
32 #include "src/trace_processor/containers/bit_vector.h"
33 #include "src/trace_processor/db/column/data_layer.h"
34 #include "src/trace_processor/db/column/types.h"
35 #include "src/trace_processor/db/column/utils.h"
36 #include "src/trace_processor/tp_metatrace.h"
37
38 #include "protos/perfetto/trace_processor/metatrace_categories.pbzero.h"
39
40 namespace perfetto::trace_processor::column {
41 namespace {
42
43 using SetId = SetIdStorage::SetId;
44
UpperBoundIntrinsic(const SetId * data,SetId val,Range range)45 uint32_t UpperBoundIntrinsic(const SetId* data, SetId val, Range range) {
46 for (uint32_t i = std::max(range.start, val); i < range.end; i++) {
47 if (data[i] > val) {
48 return i;
49 }
50 }
51 return range.end;
52 }
53
LowerBoundIntrinsic(const SetId * data,SetId id,Range range)54 uint32_t LowerBoundIntrinsic(const SetId* data, SetId id, Range range) {
55 if (data[range.start] == id) {
56 return range.start;
57 }
58 if (range.Contains(id) && data[id] == id) {
59 return id;
60 }
61 // If none of the above are true, than |id| is not present in data, so we need
62 // to look for the first value higher than |id|.
63 return UpperBoundIntrinsic(data, id, range);
64 }
65
66 } // namespace
67
GetStoragePtr()68 SetIdStorage::StoragePtr SetIdStorage::GetStoragePtr() {
69 return values_->data();
70 }
71
ChainImpl(const std::vector<uint32_t> * values)72 SetIdStorage::ChainImpl::ChainImpl(const std::vector<uint32_t>* values)
73 : values_(values) {}
74
SingleSearch(FilterOp op,SqlValue sql_val,uint32_t i) const75 SingleSearchResult SetIdStorage::ChainImpl::SingleSearch(FilterOp op,
76 SqlValue sql_val,
77 uint32_t i) const {
78 return utils::SingleSearchNumeric(op, (*values_)[i], sql_val);
79 }
80
ValidateSearchConstraints(FilterOp op,SqlValue val) const81 SearchValidationResult SetIdStorage::ChainImpl::ValidateSearchConstraints(
82 FilterOp op,
83 SqlValue val) const {
84 // NULL checks.
85 if (PERFETTO_UNLIKELY(val.is_null())) {
86 if (op == FilterOp::kIsNotNull) {
87 return SearchValidationResult::kAllData;
88 }
89 return SearchValidationResult::kNoData;
90 }
91
92 // FilterOp checks. Switch so that we get a warning if new FilterOp is not
93 // handled.
94 switch (op) {
95 case FilterOp::kEq:
96 case FilterOp::kNe:
97 case FilterOp::kLt:
98 case FilterOp::kLe:
99 case FilterOp::kGt:
100 case FilterOp::kGe:
101 break;
102 case FilterOp::kIsNull:
103 case FilterOp::kIsNotNull:
104 PERFETTO_FATAL("Invalid constraints.");
105 case FilterOp::kGlob:
106 case FilterOp::kRegex:
107 return SearchValidationResult::kNoData;
108 }
109
110 if (PERFETTO_UNLIKELY(values_->empty())) {
111 return SearchValidationResult::kNoData;
112 }
113
114 // Type checks.
115 switch (val.type) {
116 case SqlValue::kNull:
117 case SqlValue::kLong:
118 case SqlValue::kDouble:
119 break;
120 case SqlValue::kString:
121 // Any string is always more than any numeric.
122 if (op == FilterOp::kLt || op == FilterOp::kLe) {
123 return SearchValidationResult::kAllData;
124 }
125 return SearchValidationResult::kNoData;
126 case SqlValue::kBytes:
127 return SearchValidationResult::kNoData;
128 }
129
130 // Bounds of the value.
131 double num_val = val.type == SqlValue::kLong
132 ? static_cast<double>(val.AsLong())
133 : val.AsDouble();
134
135 // As values are sorted, we can cover special cases for when |num_val| is
136 // bigger than the last value and smaller than the first one.
137 if (PERFETTO_UNLIKELY(num_val > values_->back())) {
138 if (op == FilterOp::kLe || op == FilterOp::kLt || op == FilterOp::kNe) {
139 return SearchValidationResult::kAllData;
140 }
141 return SearchValidationResult::kNoData;
142 }
143 if (PERFETTO_UNLIKELY(num_val < values_->front())) {
144 if (op == FilterOp::kGe || op == FilterOp::kGt || op == FilterOp::kNe) {
145 return SearchValidationResult::kAllData;
146 }
147 return SearchValidationResult::kNoData;
148 }
149
150 return SearchValidationResult::kOk;
151 }
152
SearchValidated(FilterOp op,SqlValue sql_val,Range search_range) const153 RangeOrBitVector SetIdStorage::ChainImpl::SearchValidated(
154 FilterOp op,
155 SqlValue sql_val,
156 Range search_range) const {
157 PERFETTO_DCHECK(search_range.end <= size());
158
159 PERFETTO_TP_TRACE(metatrace::Category::DB, "SetIdStorage::ChainImpl::Search",
160 [&search_range, op](metatrace::Record* r) {
161 r->AddArg("Start", std::to_string(search_range.start));
162 r->AddArg("End", std::to_string(search_range.end));
163 r->AddArg("Op",
164 std::to_string(static_cast<uint32_t>(op)));
165 });
166
167 // It's a valid filter operation if |sql_val| is a double, although it
168 // requires special logic.
169 if (sql_val.type == SqlValue::kDouble) {
170 switch (utils::CompareIntColumnWithDouble(op, &sql_val)) {
171 case SearchValidationResult::kOk:
172 break;
173 case SearchValidationResult::kAllData:
174 return RangeOrBitVector(Range(0, search_range.end));
175 case SearchValidationResult::kNoData:
176 return RangeOrBitVector(Range());
177 }
178 }
179
180 auto val = static_cast<uint32_t>(sql_val.AsLong());
181 if (op == FilterOp::kNe) {
182 // Not equal is a special operation on binary search, as it doesn't define a
183 // range, and rather just `not` range returned with `equal` operation.
184 Range eq_range = BinarySearchIntrinsic(FilterOp::kEq, val, search_range);
185 BitVector bv(search_range.start, false);
186 bv.Resize(eq_range.start, true);
187 bv.Resize(eq_range.end, false);
188 bv.Resize(search_range.end, true);
189 return RangeOrBitVector(std::move(bv));
190 }
191 return RangeOrBitVector(BinarySearchIntrinsic(op, val, search_range));
192 }
193
IndexSearchValidated(FilterOp op,SqlValue sql_val,Indices & indices) const194 void SetIdStorage::ChainImpl::IndexSearchValidated(FilterOp op,
195 SqlValue sql_val,
196 Indices& indices) const {
197 PERFETTO_TP_TRACE(
198 metatrace::Category::DB, "SetIdStorage::ChainImpl::IndexSearch",
199 [&indices, op](metatrace::Record* r) {
200 r->AddArg("Count", std::to_string(indices.tokens.size()));
201 r->AddArg("Op", std::to_string(static_cast<uint32_t>(op)));
202 });
203
204 // It's a valid filter operation if |sql_val| is a double, although it
205 // requires special logic.
206 if (sql_val.type == SqlValue::kDouble) {
207 if (utils::CanReturnEarly(utils::CompareIntColumnWithDouble(op, &sql_val),
208 indices)) {
209 return;
210 }
211 }
212
213 // TODO(mayzner): Instead of utils::IndexSearchWithComparator, use the
214 // property of SetId data - that for each index i, data[i] <= i.
215 auto val = static_cast<uint32_t>(sql_val.AsLong());
216 switch (op) {
217 case FilterOp::kEq:
218 utils::IndexSearchWithComparator(val, values_->data(), indices,
219 std::equal_to<>());
220 break;
221 case FilterOp::kNe:
222 utils::IndexSearchWithComparator(val, values_->data(), indices,
223 std::not_equal_to<>());
224 break;
225 case FilterOp::kLe:
226 utils::IndexSearchWithComparator(val, values_->data(), indices,
227 std::less_equal<>());
228 break;
229 case FilterOp::kLt:
230 utils::IndexSearchWithComparator(val, values_->data(), indices,
231 std::less<>());
232 break;
233 case FilterOp::kGt:
234 utils::IndexSearchWithComparator(val, values_->data(), indices,
235 std::greater<>());
236 break;
237 case FilterOp::kGe:
238 utils::IndexSearchWithComparator(val, values_->data(), indices,
239 std::greater_equal<>());
240 break;
241 case FilterOp::kIsNotNull:
242 case FilterOp::kIsNull:
243 case FilterOp::kGlob:
244 case FilterOp::kRegex:
245 PERFETTO_FATAL("Illegal argument");
246 }
247 }
248
BinarySearchIntrinsic(FilterOp op,SetId val,Range range) const249 Range SetIdStorage::ChainImpl::BinarySearchIntrinsic(FilterOp op,
250 SetId val,
251 Range range) const {
252 switch (op) {
253 case FilterOp::kEq: {
254 if ((*values_)[val] != val) {
255 return {};
256 }
257 uint32_t start = std::max(val, range.start);
258 uint32_t end = UpperBoundIntrinsic(values_->data(), val, range);
259 return {std::min(start, end), end};
260 }
261 case FilterOp::kLe: {
262 return {range.start, UpperBoundIntrinsic(values_->data(), val, range)};
263 }
264 case FilterOp::kLt:
265 return {range.start, LowerBoundIntrinsic(values_->data(), val, range)};
266 case FilterOp::kGe:
267 return {LowerBoundIntrinsic(values_->data(), val, range), range.end};
268 case FilterOp::kGt:
269 return {UpperBoundIntrinsic(values_->data(), val, range), range.end};
270 case FilterOp::kIsNotNull:
271 return range;
272 case FilterOp::kNe:
273 PERFETTO_FATAL("Shouldn't be called");
274 case FilterOp::kIsNull:
275 case FilterOp::kGlob:
276 case FilterOp::kRegex:
277 return {};
278 }
279 return {};
280 }
281
StableSort(Token * start,Token * end,SortDirection direction) const282 void SetIdStorage::ChainImpl::StableSort(Token* start,
283 Token* end,
284 SortDirection direction) const {
285 PERFETTO_TP_TRACE(metatrace::Category::DB,
286 "SetIdStorage::ChainImpl::StableSort");
287 switch (direction) {
288 case SortDirection::kAscending:
289 std::stable_sort(start, end, [this](const Token& a, const Token& b) {
290 return (*values_)[a.index] < (*values_)[b.index];
291 });
292 break;
293 case SortDirection::kDescending:
294 std::stable_sort(start, end, [this](const Token& a, const Token& b) {
295 return (*values_)[a.index] > (*values_)[b.index];
296 });
297 break;
298 }
299 }
300
Distinct(Indices & indices) const301 void SetIdStorage::ChainImpl::Distinct(Indices& indices) const {
302 PERFETTO_TP_TRACE(metatrace::Category::DB,
303 "SetIdStorage::ChainImpl::Distinct");
304 std::unordered_set<uint32_t> s;
305 indices.tokens.erase(
306 std::remove_if(indices.tokens.begin(), indices.tokens.end(),
307 [&s, this](const Token& idx) {
308 return !s.insert((*values_)[idx.index]).second;
309 }),
310 indices.tokens.end());
311 }
312
MaxElement(Indices & indices) const313 std::optional<Token> SetIdStorage::ChainImpl::MaxElement(
314 Indices& indices) const {
315 PERFETTO_TP_TRACE(metatrace::Category::DB,
316 "SetIdStorage::ChainImpl::MaxElement");
317
318 auto tok =
319 std::max_element(indices.tokens.begin(), indices.tokens.end(),
320 [this](const Token& t1, const Token& t2) {
321 return (*values_)[t1.index] < (*values_)[t2.index];
322 });
323
324 if (tok == indices.tokens.end()) {
325 return std::nullopt;
326 }
327 return *tok;
328 }
329
MinElement(Indices & indices) const330 std::optional<Token> SetIdStorage::ChainImpl::MinElement(
331 Indices& indices) const {
332 PERFETTO_TP_TRACE(metatrace::Category::DB,
333 "SetIdStorage::ChainImpl::MinElement");
334 auto tok =
335 std::min_element(indices.tokens.begin(), indices.tokens.end(),
336 [this](const Token& t1, const Token& t2) {
337 return (*values_)[t1.index] < (*values_)[t2.index];
338 });
339 if (tok == indices.tokens.end()) {
340 return std::nullopt;
341 }
342
343 return *tok;
344 }
345
Get_AvoidUsingBecauseSlow(uint32_t index) const346 SqlValue SetIdStorage::ChainImpl::Get_AvoidUsingBecauseSlow(
347 uint32_t index) const {
348 return SqlValue::Long((*values_)[index]);
349 }
350
351 } // namespace perfetto::trace_processor::column
352