1 /* 2 * Copyright (C) 2020 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_ITERATOR_IMPL_H_ 18 #define SRC_TRACE_PROCESSOR_ITERATOR_IMPL_H_ 19 20 #include <sqlite3.h> 21 #include <cstddef> 22 #include <cstdint> 23 #include <string> 24 25 #include "perfetto/base/logging.h" 26 #include "perfetto/base/status.h" 27 #include "perfetto/ext/base/scoped_file.h" 28 #include "perfetto/ext/base/status_or.h" 29 #include "perfetto/trace_processor/basic_types.h" 30 #include "perfetto/trace_processor/iterator.h" 31 #include "src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.h" 32 #include "src/trace_processor/sqlite/sqlite_engine.h" 33 34 namespace perfetto { 35 namespace trace_processor { 36 37 class TraceProcessorImpl; 38 39 class IteratorImpl { 40 public: 41 IteratorImpl(TraceProcessorImpl* impl, 42 base::StatusOr<PerfettoSqlEngine::ExecutionResult>, 43 uint32_t sql_stats_row); 44 ~IteratorImpl(); 45 46 IteratorImpl(IteratorImpl&) noexcept = delete; 47 IteratorImpl& operator=(IteratorImpl&) = delete; 48 49 IteratorImpl(IteratorImpl&&) noexcept = default; 50 IteratorImpl& operator=(IteratorImpl&&) = default; 51 52 // Methods called by the base Iterator class. Next()53 bool Next() { 54 // In the past, we used to call sqlite3_step for the first time in this 55 // function which 1:1 matched Next calls to sqlite3_step calls. However, 56 // with the introduction of multi-statement support, we tokenize the 57 // queries and so we need to *not* call step the first time Next is 58 // called. 59 // 60 // Aside: if we could, we would change the API to match the new setup 61 // (i.e. implement operator bool, make Next return nothing similar to C++ 62 // iterators); however, too many clients depend on the current behavior so 63 // we have to keep the API as is. 64 if (!called_next_) { 65 // Delegate to the cc file to prevent trace_storage.h include in this 66 // file. 67 RecordFirstNextInSqlStats(); 68 called_next_ = true; 69 return result_.ok() && !result_->stmt.IsDone(); 70 } 71 if (!result_.ok()) { 72 return false; 73 } 74 75 bool has_more = result_->stmt.Step(); 76 if (!result_->stmt.status().ok()) { 77 PERFETTO_DCHECK(!has_more); 78 result_ = result_->stmt.status(); 79 } 80 return has_more; 81 } 82 Get(uint32_t col)83 SqlValue Get(uint32_t col) const { 84 PERFETTO_DCHECK(result_.ok()); 85 86 auto column = static_cast<int>(col); 87 sqlite3_stmt* stmt = result_->stmt.sqlite_stmt(); 88 auto col_type = sqlite3_column_type(stmt, column); 89 SqlValue value; 90 switch (col_type) { 91 case SQLITE_INTEGER: 92 value.type = SqlValue::kLong; 93 value.long_value = sqlite3_column_int64(stmt, column); 94 break; 95 case SQLITE_TEXT: 96 value.type = SqlValue::kString; 97 value.string_value = 98 reinterpret_cast<const char*>(sqlite3_column_text(stmt, column)); 99 break; 100 case SQLITE_FLOAT: 101 value.type = SqlValue::kDouble; 102 value.double_value = sqlite3_column_double(stmt, column); 103 break; 104 case SQLITE_BLOB: 105 value.type = SqlValue::kBytes; 106 value.bytes_value = sqlite3_column_blob(stmt, column); 107 value.bytes_count = 108 static_cast<size_t>(sqlite3_column_bytes(stmt, column)); 109 break; 110 case SQLITE_NULL: 111 value.type = SqlValue::kNull; 112 break; 113 } 114 return value; 115 } 116 GetColumnName(uint32_t col)117 std::string GetColumnName(uint32_t col) const { 118 return result_.ok() ? sqlite3_column_name(result_->stmt.sqlite_stmt(), 119 static_cast<int>(col)) 120 : ""; 121 } 122 Status()123 base::Status Status() const { return result_.status(); } 124 ColumnCount()125 uint32_t ColumnCount() const { 126 return result_.ok() ? result_->stats.column_count : 0; 127 } 128 StatementCount()129 uint32_t StatementCount() const { 130 return result_.ok() ? result_->stats.statement_count : 0; 131 } 132 StatementCountWithOutput()133 uint32_t StatementCountWithOutput() const { 134 return result_.ok() ? result_->stats.statement_count_with_output : 0; 135 } 136 LastStatementSql()137 std::string LastStatementSql() const { 138 return result_.ok() ? result_->stmt.sql() : ""; 139 } 140 141 private: 142 // Dummy function to pass to ScopedResource. DummyClose(TraceProcessorImpl *)143 static int DummyClose(TraceProcessorImpl*) { return 0; } 144 145 // Iterators hold onto an instance of TraceProcessor to track when the query 146 // ends in the sql stats table. As iterators are movable, we need to null out 147 // the TraceProcessor in the moved out iterator to avoid double recording 148 // query ends. We could manually define a move constructor instead, but given 149 // the error prone nature of keeping functions up to date, this seems like a 150 // nicer approach. 151 using ScopedTraceProcessor = 152 base::ScopedResource<TraceProcessorImpl*, &DummyClose, nullptr>; 153 154 void RecordFirstNextInSqlStats(); 155 156 ScopedTraceProcessor trace_processor_; 157 base::StatusOr<PerfettoSqlEngine::ExecutionResult> result_; 158 uint32_t sql_stats_row_ = 0; 159 bool called_next_ = false; 160 }; 161 162 } // namespace trace_processor 163 } // namespace perfetto 164 165 #endif // SRC_TRACE_PROCESSOR_ITERATOR_IMPL_H_ 166