1 /* 2 * Copyright (C) 2018 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 #ifndef SRC_TRACE_PROCESSOR_SQLITE_SPAN_JOIN_OPERATOR_TABLE_H_ 18 #define SRC_TRACE_PROCESSOR_SQLITE_SPAN_JOIN_OPERATOR_TABLE_H_ 19 20 #include <sqlite3.h> 21 22 #include <array> 23 #include <deque> 24 #include <limits> 25 #include <map> 26 #include <memory> 27 #include <string> 28 #include <unordered_map> 29 #include <vector> 30 31 #include "perfetto/trace_processor/basic_types.h" 32 #include "perfetto/trace_processor/status.h" 33 #include "src/trace_processor/sqlite/scoped_db.h" 34 #include "src/trace_processor/sqlite/sqlite_table.h" 35 36 namespace perfetto { 37 namespace trace_processor { 38 39 // Implements the SPAN JOIN operation between two tables on a particular column. 40 // 41 // Span: 42 // A span is a row with a timestamp and a duration. It can is used to model 43 // operations which run for a particular *span* of time. 44 // 45 // We draw spans like so (time on the x-axis): 46 // start of span->[ time where opertion is running ]<- end of span 47 // 48 // Multiple spans can happen in parallel: 49 // [ ] 50 // [ ] 51 // [ ] 52 // [ ] 53 // 54 // The above for example, models scheduling activity on a 4-core computer for a 55 // short period of time. 56 // 57 // Span join: 58 // The span join operation can be thought of as the intersection of span tables. 59 // That is, the join table has a span for each pair of spans in the child tables 60 // where the spans overlap. Because many spans are possible in parallel, an 61 // extra metadata column (labelled the "join column") is used to distinguish 62 // between the spanned tables. 63 // 64 // For a given join key suppose these were the two span tables: 65 // Table 1: [ ] [ ] [ ] 66 // Table 2: [ ] [ ] [ ] 67 // Output : [ ] [ ] [] 68 // 69 // All other columns apart from timestamp (ts), duration (dur) and the join key 70 // are passed through unchanged. 71 class SpanJoinOperatorTable : public SqliteTable { 72 public: 73 static constexpr int kSourceGeqOpCode = SQLITE_INDEX_CONSTRAINT_FUNCTION + 1; 74 75 // Enum indicating whether the queries on the two inner tables should 76 // emit shadows. 77 enum class EmitShadowType { 78 // Used when the table should emit all shadow slices (both present and 79 // missing partition shadows). 80 kAll, 81 82 // Used when the table should only emit shadows for partitions which are 83 // present. 84 kPresentPartitionOnly, 85 86 // Used when the table should emit no shadow slices. 87 kNone, 88 }; 89 90 // Contains the definition of the child tables. 91 class TableDefinition { 92 public: 93 TableDefinition() = default; 94 95 TableDefinition(std::string name, 96 std::string partition_col, 97 std::vector<SqliteTable::Column> cols, 98 EmitShadowType emit_shadow_type, 99 uint32_t ts_idx, 100 uint32_t dur_idx, 101 uint32_t partition_idx); 102 103 // Returns whether this table should emit present partition shadow slices. ShouldEmitPresentPartitionShadow()104 bool ShouldEmitPresentPartitionShadow() const { 105 return emit_shadow_type_ == EmitShadowType::kAll || 106 emit_shadow_type_ == EmitShadowType::kPresentPartitionOnly; 107 } 108 109 // Returns whether this table should emit missing partition shadow slices. ShouldEmitMissingPartitionShadow()110 bool ShouldEmitMissingPartitionShadow() const { 111 return emit_shadow_type_ == EmitShadowType::kAll; 112 } 113 114 // Returns whether the table is partitioned. IsPartitioned()115 bool IsPartitioned() const { return !partition_col_.empty(); } 116 name()117 const std::string& name() const { return name_; } partition_col()118 const std::string& partition_col() const { return partition_col_; } columns()119 const std::vector<SqliteTable::Column>& columns() const { return cols_; } 120 ts_idx()121 uint32_t ts_idx() const { return ts_idx_; } dur_idx()122 uint32_t dur_idx() const { return dur_idx_; } partition_idx()123 uint32_t partition_idx() const { return partition_idx_; } 124 125 private: 126 EmitShadowType emit_shadow_type_ = EmitShadowType::kNone; 127 128 std::string name_; 129 std::string partition_col_; 130 std::vector<SqliteTable::Column> cols_; 131 132 uint32_t ts_idx_ = std::numeric_limits<uint32_t>::max(); 133 uint32_t dur_idx_ = std::numeric_limits<uint32_t>::max(); 134 uint32_t partition_idx_ = std::numeric_limits<uint32_t>::max(); 135 }; 136 137 // Stores information about a single subquery into one of the two child 138 // tables. 139 // 140 // This class is implemented as a state machine which steps from one slice to 141 // the next. 142 class Query { 143 public: 144 // Enum encoding the current state of the query in the state machine. 145 enum class State { 146 // Encodes that the current slice is a real slice (i.e. comes directly 147 // from the cursor). 148 kReal, 149 150 // Encodes that the current slice is on a partition for which there is a 151 // real slice present. 152 kPresentPartitionShadow, 153 154 // Encodes that the current slice is on a paritition(s) for which there is 155 // no real slice for those partition(s). 156 kMissingPartitionShadow, 157 158 // Encodes that this query has reached the end. 159 kEof, 160 }; 161 162 Query(SpanJoinOperatorTable*, const TableDefinition*, sqlite3* db); 163 virtual ~Query(); 164 165 Query(Query&&) noexcept = default; 166 Query& operator=(Query&&) = default; 167 168 enum class InitialEofBehavior { 169 kTreatAsEof, 170 kTreatAsMissingPartitionShadow 171 }; 172 173 // Initializes the query with the given constraints and query parameters. 174 util::Status Initialize( 175 const QueryConstraints& qc, 176 sqlite3_value** argv, 177 InitialEofBehavior eof_behavior = InitialEofBehavior::kTreatAsEof); 178 179 // Forwards the query to the next valid slice. 180 util::Status Next(); 181 182 // Rewinds the query to the first valid slice 183 // This is used in the mixed partitioning case where the query with no 184 // partitions is rewound to the start on every new partition. 185 util::Status Rewind(); 186 187 // Reports the column at the given index to given context. 188 void ReportSqliteResult(sqlite3_context* context, size_t index); 189 190 // Returns whether the cursor has reached eof. IsEof()191 bool IsEof() const { return state_ == State::kEof; } 192 193 // Returns whether the current slice pointed to is a real slice. IsReal()194 bool IsReal() const { return state_ == State::kReal; } 195 196 // Returns the first partition this slice covers (for real/single partition 197 // shadows, this is the same as partition()). 198 // This partition encodes a [start, end] (closed at start and at end) range 199 // of partitions which works as the partitions are integers. FirstPartition()200 int64_t FirstPartition() const { 201 PERFETTO_DCHECK(!IsEof()); 202 return IsMissingPartitionShadow() ? missing_partition_start_ 203 : partition(); 204 } 205 206 // Returns the last partition this slice covers (for real/single partition 207 // shadows, this is the same as partition()). 208 // This partition encodes a [start, end] (closed at start and at end) range 209 // of partitions which works as the partitions are integers. LastPartition()210 int64_t LastPartition() const { 211 PERFETTO_DCHECK(!IsEof()); 212 return IsMissingPartitionShadow() ? missing_partition_end_ - 1 213 : partition(); 214 } 215 216 // Returns the end timestamp of this slice adjusted to ensure that -1 217 // duration slices always returns ts. AdjustedTsEnd()218 int64_t AdjustedTsEnd() const { 219 PERFETTO_DCHECK(!IsEof()); 220 return ts_end_ - ts() == -1 ? ts() : ts_end_; 221 } 222 ts()223 int64_t ts() const { 224 PERFETTO_DCHECK(!IsEof()); 225 return ts_; 226 } partition()227 int64_t partition() const { 228 PERFETTO_DCHECK(!IsEof() && defn_->IsPartitioned()); 229 return partition_; 230 } 231 raw_ts_end()232 int64_t raw_ts_end() const { 233 PERFETTO_DCHECK(!IsEof()); 234 return ts_end_; 235 } 236 definition()237 const TableDefinition* definition() const { return defn_; } 238 239 private: 240 Query(Query&) = delete; 241 Query& operator=(const Query&) = delete; 242 243 // Returns whether the current slice pointed to is a valid slice. 244 bool IsValidSlice(); 245 246 // Forwards the query to the next valid slice. 247 util::Status FindNextValidSlice(); 248 249 // Advances the query state machine by one slice. 250 util::Status NextSliceState(); 251 252 // Forwards the cursor to point to the next real slice. 253 util::Status CursorNext(); 254 255 // Creates an SQL query from the given set of constraint strings. 256 std::string CreateSqlQuery(const std::vector<std::string>& cs) const; 257 258 // Returns whether the current slice pointed to is a present partition 259 // shadow. IsPresentPartitionShadow()260 bool IsPresentPartitionShadow() const { 261 return state_ == State::kPresentPartitionShadow; 262 } 263 264 // Returns whether the current slice pointed to is a missing partition 265 // shadow. IsMissingPartitionShadow()266 bool IsMissingPartitionShadow() const { 267 return state_ == State::kMissingPartitionShadow; 268 } 269 270 // Returns whether the current slice pointed to is an empty shadow. IsEmptyShadow()271 bool IsEmptyShadow() const { 272 PERFETTO_DCHECK(!IsEof()); 273 return (!IsReal() && ts_ == ts_end_) || 274 (IsMissingPartitionShadow() && 275 missing_partition_start_ == missing_partition_end_); 276 } 277 CursorTs()278 int64_t CursorTs() const { 279 PERFETTO_DCHECK(!cursor_eof_); 280 auto ts_idx = static_cast<int>(defn_->ts_idx()); 281 return sqlite3_column_int64(stmt_.get(), ts_idx); 282 } 283 CursorDur()284 int64_t CursorDur() const { 285 PERFETTO_DCHECK(!cursor_eof_); 286 auto dur_idx = static_cast<int>(defn_->dur_idx()); 287 return sqlite3_column_int64(stmt_.get(), dur_idx); 288 } 289 CursorPartition()290 int64_t CursorPartition() const { 291 PERFETTO_DCHECK(!cursor_eof_); 292 PERFETTO_DCHECK(defn_->IsPartitioned()); 293 auto partition_idx = static_cast<int>(defn_->partition_idx()); 294 return sqlite3_column_int64(stmt_.get(), partition_idx); 295 } 296 297 State state_ = State::kMissingPartitionShadow; 298 bool cursor_eof_ = false; 299 300 // Only valid when |state_| != kEof. 301 int64_t ts_ = 0; 302 int64_t ts_end_ = std::numeric_limits<int64_t>::max(); 303 304 // Only valid when |state_| == kReal or |state_| == kPresentPartitionShadow. 305 int64_t partition_ = std::numeric_limits<int64_t>::min(); 306 307 // Only valid when |state_| == kMissingPartitionShadow. 308 int64_t missing_partition_start_ = 0; 309 int64_t missing_partition_end_ = 0; 310 311 std::string sql_query_; 312 ScopedStmt stmt_; 313 314 const TableDefinition* defn_ = nullptr; 315 sqlite3* db_ = nullptr; 316 SpanJoinOperatorTable* table_ = nullptr; 317 }; 318 319 // Base class for a cursor on the span table. 320 class Cursor : public SqliteTable::Cursor { 321 public: 322 Cursor(SpanJoinOperatorTable*, sqlite3* db); 323 ~Cursor() override = default; 324 325 int Filter(const QueryConstraints& qc, 326 sqlite3_value** argv, 327 FilterHistory) override; 328 int Column(sqlite3_context* context, int N) override; 329 int Next() override; 330 int Eof() override; 331 332 private: 333 Cursor(Cursor&) = delete; 334 Cursor& operator=(const Cursor&) = delete; 335 336 Cursor(Cursor&&) noexcept = default; 337 Cursor& operator=(Cursor&&) = default; 338 339 bool IsOverlappingSpan(); 340 341 util::Status FindOverlappingSpan(); 342 343 Query* FindEarliestFinishQuery(); 344 345 Query t1_; 346 Query t2_; 347 348 Query* next_query_ = nullptr; 349 350 // Only valid for kMixedPartition. 351 int64_t last_mixed_partition_ = std::numeric_limits<int64_t>::min(); 352 353 SpanJoinOperatorTable* table_; 354 }; 355 356 SpanJoinOperatorTable(sqlite3*, const TraceStorage*); 357 358 static void RegisterTable(sqlite3* db, const TraceStorage* storage); 359 360 // Table implementation. 361 util::Status Init(int, const char* const*, SqliteTable::Schema*) override; 362 std::unique_ptr<SqliteTable::Cursor> CreateCursor() override; 363 int BestIndex(const QueryConstraints& qc, BestIndexInfo* info) override; 364 int FindFunction(const char* name, FindFunctionFn* fn, void** args) override; 365 366 private: 367 // Columns of the span operator table. 368 enum Column { 369 kTimestamp = 0, 370 kDuration = 1, 371 kPartition = 2, 372 // All other columns are dynamic depending on the joined tables. 373 }; 374 375 // Enum indicating the possible partitionings of the two tables in span join. 376 enum class PartitioningType { 377 // Used when both tables don't have a partition specified. 378 kNoPartitioning = 0, 379 380 // Used when both tables have the same partition specified. 381 kSamePartitioning = 1, 382 383 // Used when one table has a partition and the other table doesn't. 384 kMixedPartitioning = 2 385 }; 386 387 // Parsed version of a table descriptor. 388 struct TableDescriptor { 389 static util::Status Parse(const std::string& raw_descriptor, 390 TableDescriptor* descriptor); 391 IsPartitionedTableDescriptor392 bool IsPartitioned() const { return !partition_col.empty(); } 393 394 std::string name; 395 std::string partition_col; 396 }; 397 398 // Identifier for a column by index in a given table. 399 struct ColumnLocator { 400 const TableDefinition* defn; 401 size_t col_index; 402 }; 403 IsLeftJoin()404 bool IsLeftJoin() const { return name() == "span_left_join"; } IsOuterJoin()405 bool IsOuterJoin() const { return name() == "span_outer_join"; } 406 partition_col()407 const std::string& partition_col() const { 408 return t1_defn_.IsPartitioned() ? t1_defn_.partition_col() 409 : t2_defn_.partition_col(); 410 } 411 412 util::Status CreateTableDefinition( 413 const TableDescriptor& desc, 414 EmitShadowType emit_shadow_type, 415 SpanJoinOperatorTable::TableDefinition* defn); 416 417 std::vector<std::string> ComputeSqlConstraintsForDefinition( 418 const TableDefinition& defn, 419 const QueryConstraints& qc, 420 sqlite3_value** argv); 421 422 std::string GetNameForGlobalColumnIndex(const TableDefinition& defn, 423 int global_column); 424 425 void CreateSchemaColsForDefn(const TableDefinition& defn, 426 std::vector<SqliteTable::Column>* cols); 427 428 TableDefinition t1_defn_; 429 TableDefinition t2_defn_; 430 PartitioningType partitioning_; 431 std::unordered_map<size_t, ColumnLocator> global_index_to_column_locator_; 432 433 sqlite3* const db_; 434 }; 435 436 } // namespace trace_processor 437 } // namespace perfetto 438 439 #endif // SRC_TRACE_PROCESSOR_SQLITE_SPAN_JOIN_OPERATOR_TABLE_H_ 440