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