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