huangcm
2024-12-18 9d29be7f7249789d6ffd0440067187a9f040c2cd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
#ifndef SRC_TRACE_PROCESSOR_STORAGE_COLUMNS_H_
#define SRC_TRACE_PROCESSOR_STORAGE_COLUMNS_H_
 
#include <deque>
#include <limits>
#include <memory>
#include <string>
#include <vector>
 
#include "src/trace_processor/filtered_row_index.h"
#include "src/trace_processor/sqlite_utils.h"
#include "src/trace_processor/trace_storage.h"
 
namespace perfetto {
namespace trace_processor {
 
// A column of data backed by data storage.
class StorageColumn {
 public:
  struct Bounds {
    uint32_t min_idx = 0;
    uint32_t max_idx = std::numeric_limits<uint32_t>::max();
    bool consumed = false;
  };
  using Predicate = std::function<bool(uint32_t)>;
  using Comparator = std::function<int(uint32_t, uint32_t)>;
 
  StorageColumn(std::string col_name, bool hidden);
  virtual ~StorageColumn();
 
  // Implements StorageCursor::ColumnReporter.
  virtual void ReportResult(sqlite3_context*, uint32_t row) const = 0;
 
  // Given a SQLite operator and value for the comparision, returns a
  // predicate which takes in a row index and returns whether the row should
  // be returned.
  virtual void Filter(int op, sqlite3_value*, FilteredRowIndex*) const = 0;
 
  // Given a order by constraint for this column, returns a comparator
  // function which compares data in this column at two indices.
  virtual Comparator Sort(const QueryConstraints::OrderBy& ob) const = 0;
 
  // Returns the type of this column.
  virtual Table::ColumnType GetType() const = 0;
 
  // Bounds a filter on this column between a minimum and maximum index.
  // Generally this is only possible if the column is sorted.
  virtual Bounds BoundFilter(int, sqlite3_value*) const { return Bounds{}; }
 
  // Returns whether this column is ordered.
  virtual bool HasOrdering() const { return false; }
 
  const std::string& name() const { return col_name_; }
  bool hidden() const { return hidden_; }
 
 private:
  std::string col_name_;
  bool hidden_ = false;
};
 
// The implementation of StorageColumn for Strings.
// The actual retrieval of the numerics from the data types is left to the
// Acessor trait (see below for definition).
template <typename Accessor /* <NullTermStringView> */>
class StringColumn final : public StorageColumn {
 public:
  StringColumn(std::string col_name, Accessor accessor, bool hidden = false)
      : StorageColumn(col_name, hidden), accessor_(accessor) {}
 
  void ReportResult(sqlite3_context* ctx, uint32_t row) const override {
    NullTermStringView str = accessor_.Get(row);
    if (str.c_str() == nullptr) {
      sqlite3_result_null(ctx);
    } else {
      sqlite3_result_text(ctx, str.c_str(), -1, sqlite_utils::kSqliteStatic);
    }
  }
 
  Bounds BoundFilter(int, sqlite3_value*) const override {
    Bounds bounds;
    bounds.max_idx = static_cast<uint32_t>(accessor_.Size());
    return bounds;
  }
 
  void Filter(int, sqlite3_value*, FilteredRowIndex*) const override {}
 
  Comparator Sort(const QueryConstraints::OrderBy& ob) const override {
    if (ob.desc) {
      return [this](uint32_t f, uint32_t s) {
        NullTermStringView a = accessor_.Get(f);
        NullTermStringView b = accessor_.Get(s);
        return sqlite_utils::CompareValuesDesc(a, b);
      };
    }
    return [this](uint32_t f, uint32_t s) {
      NullTermStringView a = accessor_.Get(f);
      NullTermStringView b = accessor_.Get(s);
      return sqlite_utils::CompareValuesAsc(a, b);
    };
  }
 
  Table::ColumnType GetType() const override {
    return Table::ColumnType::kString;
  }
 
  bool HasOrdering() const override { return accessor_.HasOrdering(); }
 
 private:
  Accessor accessor_;
};
 
// The implementation of StorageColumn for numeric data types.
// The actual retrieval of the numerics from the data types is left to the
// Acessor trait (see below for definition).
template <typename Accessor,
          typename sqlite_utils::is_numeric<typename Accessor::Type>* = nullptr>
class NumericColumn : public StorageColumn {
 public:
  // The type of the column. This is one of uint32_t, int32_t, uint64_t etc.
  using NumericType = typename Accessor::Type;
 
  NumericColumn(std::string col_name, bool hidden, Accessor accessor)
      : StorageColumn(col_name, hidden), accessor_(accessor) {}
  ~NumericColumn() override = default;
 
  void ReportResult(sqlite3_context* ctx, uint32_t row) const override {
    sqlite_utils::ReportSqliteResult(ctx, accessor_.Get(row));
  }
 
  Bounds BoundFilter(int op, sqlite3_value* sqlite_val) const override {
    Bounds bounds;
    bounds.max_idx = accessor_.Size();
 
    if (!accessor_.HasOrdering())
      return bounds;
 
    // Makes the below code much more readable.
    using namespace sqlite_utils;
 
    NumericType min = kTMin;
    NumericType max = kTMax;
    if (IsOpGe(op) || IsOpGt(op)) {
      min = FindGtBound<NumericType>(IsOpGe(op), sqlite_val);
    } else if (IsOpLe(op) || IsOpLt(op)) {
      max = FindLtBound<NumericType>(IsOpLe(op), sqlite_val);
    } else if (IsOpEq(op)) {
      auto val = FindEqBound<NumericType>(sqlite_val);
      min = val;
      max = val;
    }
 
    if (min <= kTMin && max >= kTMax)
      return bounds;
 
    bounds.min_idx = accessor_.LowerBoundIndex(min);
    bounds.max_idx = accessor_.UpperBoundIndex(max);
    bounds.consumed = true;
 
    return bounds;
  }
 
  void Filter(int op,
              sqlite3_value* value,
              FilteredRowIndex* index) const override {
    auto type = sqlite3_value_type(value);
 
    bool same_type = (kIsIntegralType && type == SQLITE_INTEGER) ||
                     (kIsRealType && type == SQLITE_FLOAT);
    if (sqlite_utils::IsOpEq(op) && same_type &&
        accessor_.CanFindEqualIndices()) {
      auto raw = sqlite_utils::ExtractSqliteValue<NumericType>(value);
      index->IntersectRows(accessor_.EqualIndices(raw));
      return;
    }
 
    if (kIsIntegralType && (type == SQLITE_INTEGER || type == SQLITE_NULL)) {
      FilterWithCast<int64_t>(op, value, index);
    } else if (type == SQLITE_INTEGER || type == SQLITE_FLOAT ||
               type == SQLITE_NULL) {
      FilterWithCast<double>(op, value, index);
    } else {
      PERFETTO_FATAL("Unexpected sqlite value to compare against");
    }
  }
 
  Comparator Sort(const QueryConstraints::OrderBy& ob) const override {
    if (ob.desc) {
      return [this](uint32_t f, uint32_t s) {
        return sqlite_utils::CompareValuesDesc(accessor_.Get(f),
                                               accessor_.Get(s));
      };
    }
    return [this](uint32_t f, uint32_t s) {
      return sqlite_utils::CompareValuesAsc(accessor_.Get(f), accessor_.Get(s));
    };
  }
 
  bool HasOrdering() const override { return accessor_.HasOrdering(); }
 
  Table::ColumnType GetType() const override {
    if (std::is_same<NumericType, int32_t>::value) {
      return Table::ColumnType::kInt;
    } else if (std::is_same<NumericType, uint8_t>::value ||
               std::is_same<NumericType, uint32_t>::value) {
      return Table::ColumnType::kUint;
    } else if (std::is_same<NumericType, int64_t>::value) {
      return Table::ColumnType::kLong;
    } else if (std::is_same<NumericType, double>::value) {
      return Table::ColumnType::kDouble;
    }
    PERFETTO_FATAL("Unexpected column type");
  }
 
 private:
  static constexpr bool kIsIntegralType = std::is_integral<NumericType>::value;
  static constexpr bool kIsRealType =
      std::is_floating_point<NumericType>::value;
 
  NumericType kTMin = std::numeric_limits<NumericType>::lowest();
  NumericType kTMax = std::numeric_limits<NumericType>::max();
 
  // Filters the rows of this column by creating the predicate from the sqlite
  // value using type |UpcastNumericType| and casting data from the column
  // to also be this type.
  // Note: We cast here to make numeric comparisions as accurate as possible.
  // For example, suppose NumericType == uint32_t and the sqlite value has
  // an integer. Then UpcastNumericType == int64_t because uint32_t can be
  // upcast to an int64_t and it's the most generic type we can compare using.
  // Alternatively if either the column or sqlite value is real, we will always
  // cast to a double before comparing.
  template <typename UpcastNumericType>
  void FilterWithCast(int op,
                      sqlite3_value* value,
                      FilteredRowIndex* index) const {
    auto predicate =
        sqlite_utils::CreateNumericPredicate<UpcastNumericType>(op, value);
    auto cast_predicate = [this,
                           predicate](uint32_t row) PERFETTO_ALWAYS_INLINE {
      return predicate(static_cast<UpcastNumericType>(accessor_.Get(row)));
    };
    index->FilterRows(cast_predicate);
  }
 
  Accessor accessor_;
};
 
// Defines an accessor for columns.
// An accessor is a abstraction over the method to retrieve data in a column. As
// there are many possible types of backing data (std::vector, std::deque,
// creating on the flight etc.), this class hides this complexity behind an
// interface to let the column implementation focus on actually interfacing
// with SQLite and rest of trace processor.
// This class exists as an interface for documentation purposes. There should
// be no use of it apart from classes inheriting from it to ensure they comply
// with the requirements of the interface.
template <typename DataType>
class Accessor {
 public:
  using Type = DataType;
 
  virtual ~Accessor() = default;
 
  // Returns the number of elements in the backing storage.
  virtual uint32_t Size() const = 0;
 
  // Returns the element located at index |idx|.
  virtual Type Get(uint32_t idx) const = 0;
 
  // Returns whether the backing data source is ordered. |LowerBoundIndex| and
  // |UpperBoundIndex| will be called only if HasOrdering() returns true.
  virtual bool HasOrdering() const { return false; }
 
  // Returns the index of the lower bound of the value.
  virtual uint32_t LowerBoundIndex(Type) const { PERFETTO_CHECK(false); }
 
  // Returns the index of the lower bound of the value.
  virtual uint32_t UpperBoundIndex(Type) const { PERFETTO_CHECK(false); }
 
  // Returns whether the backing data sources can efficiently provide the
  // indices of elements equal to a given value. |EqualIndices| will be called
  // only if |CanFindEqualIndices| returns true.
  virtual bool CanFindEqualIndices() const { return false; }
 
  // Returns the indices into the backing data source with value equal to
  // |value|.
  virtual std::vector<uint32_t> EqualIndices(Type) const {
    PERFETTO_CHECK(false);
  }
};
 
// An accessor implementation for string which uses a deque to store offsets
// into a StringPool.
class StringPoolAccessor : public Accessor<NullTermStringView> {
 public:
  StringPoolAccessor(const std::deque<StringPool::Id>* deque,
                     const StringPool* string_pool);
  ~StringPoolAccessor() override;
 
  uint32_t Size() const override {
    return static_cast<uint32_t>(deque_->size());
  }
 
  NullTermStringView Get(uint32_t idx) const override {
    return string_pool_->Get((*deque_)[idx]);
  }
 
 private:
  const std::deque<StringPool::Id>* deque_;
  const StringPool* string_pool_;
};
 
// An accessor implementation for string which uses a deque to store indices
// into a vector of strings.
template <typename Id>
class StringVectorAccessor : public Accessor<NullTermStringView> {
 public:
  StringVectorAccessor(const std::deque<Id>* deque,
                       const std::vector<const char*>* string_map)
      : deque_(deque), string_map_(string_map) {}
  ~StringVectorAccessor() override = default;
 
  uint32_t Size() const override {
    return static_cast<uint32_t>(deque_->size());
  }
 
  NullTermStringView Get(uint32_t idx) const override {
    const char* ptr = (*string_map_)[(*deque_)[idx]];
    return ptr ? NullTermStringView(ptr) : NullTermStringView();
  }
 
 private:
  const std::deque<Id>* deque_;
  const std::vector<const char*>* string_map_;
};
 
// An accessor implementation for numeric columns which uses a deque as the
// backing storage with an opitonal index for quick equality filtering.
template <typename NumericType>
class NumericDequeAccessor : public Accessor<NumericType> {
 public:
  NumericDequeAccessor(const std::deque<NumericType>* deque,
                       const std::deque<std::vector<uint32_t>>* index,
                       bool has_ordering)
      : deque_(deque), index_(index), has_ordering_(has_ordering) {}
  ~NumericDequeAccessor() override = default;
 
  uint32_t Size() const override {
    return static_cast<uint32_t>(deque_->size());
  }
 
  NumericType Get(uint32_t idx) const override { return (*deque_)[idx]; }
 
  bool HasOrdering() const override { return has_ordering_; }
 
  uint32_t LowerBoundIndex(NumericType value) const override {
    PERFETTO_DCHECK(HasOrdering());
    auto it = std::lower_bound(deque_->begin(), deque_->end(), value);
    return static_cast<uint32_t>(std::distance(deque_->begin(), it));
  }
 
  uint32_t UpperBoundIndex(NumericType value) const override {
    PERFETTO_DCHECK(HasOrdering());
    auto it = std::upper_bound(deque_->begin(), deque_->end(), value);
    return static_cast<uint32_t>(std::distance(deque_->begin(), it));
  }
 
  bool CanFindEqualIndices() const override {
    return std::is_integral<NumericType>::value && index_ != nullptr;
  }
 
  std::vector<uint32_t> EqualIndices(NumericType value) const override {
    PERFETTO_DCHECK(CanFindEqualIndices());
    if (value < 0 || static_cast<size_t>(value) >= index_->size())
      return {};
    return (*index_)[static_cast<size_t>(value)];
  }
 
 private:
  const std::deque<NumericType>* deque_ = nullptr;
  const std::deque<std::vector<uint32_t>>* index_ = nullptr;
  bool has_ordering_ = false;
};
 
class TsEndAccessor : public Accessor<int64_t> {
 public:
  TsEndAccessor(const std::deque<int64_t>* ts, const std::deque<int64_t>* dur);
  ~TsEndAccessor() override;
 
  uint32_t Size() const override { return static_cast<uint32_t>(ts_->size()); }
 
  int64_t Get(uint32_t idx) const override {
    return (*ts_)[idx] + (*dur_)[idx];
  }
 
 private:
  const std::deque<int64_t>* ts_ = nullptr;
  const std::deque<int64_t>* dur_ = nullptr;
};
 
class RowIdAccessor : public Accessor<int64_t> {
 public:
  RowIdAccessor(TableId table_id);
  ~RowIdAccessor() override;
 
  uint32_t Size() const override {
    return std::numeric_limits<uint32_t>::max();
  }
 
  int64_t Get(uint32_t idx) const override {
    return TraceStorage::CreateRowId(table_id_, idx);
  }
 
 private:
  TableId table_id_;
};
 
class RowAccessor : public Accessor<uint32_t> {
 public:
  RowAccessor();
  ~RowAccessor() override;
 
  uint32_t Size() const override {
    return std::numeric_limits<uint32_t>::max();
  }
 
  uint32_t Get(uint32_t idx) const override { return idx; }
 
  bool HasOrdering() const override { return true; }
 
  uint32_t LowerBoundIndex(uint32_t idx) const override { return idx; }
 
  uint32_t UpperBoundIndex(uint32_t idx) const override { return idx + 1; }
};
 
}  // namespace trace_processor
}  // namespace perfetto
 
#endif  // SRC_TRACE_PROCESSOR_STORAGE_COLUMNS_H_