/* * Copyright (C) 2018 The Android Open Source Project * * 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. */ #include "src/trace_processor/sched_slice_table.h" namespace perfetto { namespace trace_processor { SchedSliceTable::SchedSliceTable(sqlite3*, const TraceStorage* storage) : storage_(storage) {} void SchedSliceTable::RegisterTable(sqlite3* db, const TraceStorage* storage) { Table::Register<SchedSliceTable>(db, storage, "sched"); } StorageSchema SchedSliceTable::CreateStorageSchema() { const auto& slices = storage_->slices(); return StorageSchema::Builder() .AddOrderedNumericColumn("ts", &slices.start_ns()) .AddNumericColumn("cpu", &slices.cpus()) .AddNumericColumn("dur", &slices.durations()) .AddGenericNumericColumn( "ts_end", TsEndAccessor(&slices.start_ns(), &slices.durations())) .AddNumericColumn("utid", &slices.utids(), &slices.rows_for_utids()) .AddColumn<EndStateColumn>("end_state", &slices.end_state()) .AddNumericColumn("priority", &slices.priorities()) .AddGenericNumericColumn("row_id", RowIdAccessor(TableId::kSched)) .Build({"cpu", "ts"}); } uint32_t SchedSliceTable::RowCount() { return static_cast<uint32_t>(storage_->slices().slice_count()); } int SchedSliceTable::BestIndex(const QueryConstraints& qc, BestIndexInfo* info) { info->estimated_cost = EstimateQueryCost(qc); // We should be able to handle any constraint and any order by clause given // to us. info->order_by_consumed = true; std::fill(info->omit.begin(), info->omit.end(), true); return SQLITE_OK; } uint32_t SchedSliceTable::EstimateQueryCost(const QueryConstraints& qc) { const auto& cs = qc.constraints(); size_t ts_idx = schema().ColumnIndexFromName("ts"); auto has_ts_column = [ts_idx](const QueryConstraints::Constraint& c) { return c.iColumn == static_cast<int>(ts_idx); }; bool has_time_constraint = std::any_of(cs.begin(), cs.end(), has_ts_column); if (has_time_constraint) { // If there is a constraint on ts, we can do queries very fast (O(log n)) // so always make this preferred if available. return 10; } size_t utid_idx = schema().ColumnIndexFromName("utid"); auto has_utid_eq_cs = [utid_idx](const QueryConstraints::Constraint& c) { return c.iColumn == static_cast<int>(utid_idx) && sqlite_utils::IsOpEq(c.op); }; bool has_utid_eq = std::any_of(cs.begin(), cs.end(), has_utid_eq_cs); if (has_utid_eq) { // The other column which is often joined on is utid. Sometimes, doing // nested subqueries on the thread table is faster but with some queries, // it's actually better to do subqueries on this table. Estimate the cost // of filtering on utid equality constraint by dividing the number of slices // by the number of threads. return RowCount() / storage_->thread_count(); } // If we get to this point, we do not have any special filter logic so // simply return the number of rows. return RowCount(); } SchedSliceTable::EndStateColumn::EndStateColumn( std::string col_name, const std::deque<ftrace_utils::TaskState>* deque) : StorageColumn(col_name, false), deque_(deque) { for (uint16_t i = 0; i < state_strings_.size(); i++) { state_strings_[i] = ftrace_utils::TaskState(i).ToString(); } } SchedSliceTable::EndStateColumn::~EndStateColumn() = default; void SchedSliceTable::EndStateColumn::ReportResult(sqlite3_context* ctx, uint32_t row) const { const auto& state = (*deque_)[row]; if (state.is_valid()) { PERFETTO_CHECK(state.raw_state() < state_strings_.size()); sqlite3_result_text(ctx, state_strings_[state.raw_state()].data(), -1, sqlite_utils::kSqliteStatic); } else { sqlite3_result_null(ctx); } } void SchedSliceTable::EndStateColumn::Filter(int op, sqlite3_value* value, FilteredRowIndex* index) const { switch (op) { case SQLITE_INDEX_CONSTRAINT_ISNULL: case SQLITE_INDEX_CONSTRAINT_ISNOTNULL: { bool non_nulls = op == SQLITE_INDEX_CONSTRAINT_ISNOTNULL; index->FilterRows([this, non_nulls](uint32_t row) { const auto& state = (*deque_)[row]; return state.is_valid() == non_nulls; }); break; } case SQLITE_INDEX_CONSTRAINT_EQ: case SQLITE_INDEX_CONSTRAINT_NE: case SQLITE_INDEX_CONSTRAINT_MATCH: FilterOnState(op, value, index); break; default: index->set_error("Unsupported op given to filter on end_state"); break; } } void SchedSliceTable::EndStateColumn::FilterOnState( int op, sqlite3_value* value, FilteredRowIndex* index) const { if (sqlite3_value_type(value) != SQLITE_TEXT) { index->set_error("end_state can only be filtered using strings"); return; } const char* str = reinterpret_cast<const char*>(sqlite3_value_text(value)); ftrace_utils::TaskState compare(str); if (!compare.is_valid()) { index->set_error("Invalid end_state string given to filter"); return; } uint16_t raw_state = compare.raw_state(); if (op == SQLITE_INDEX_CONSTRAINT_EQ) { index->FilterRows([this, raw_state](uint32_t row) { const auto& state = (*deque_)[row]; return state.is_valid() && state.raw_state() == raw_state; }); } else if (op == SQLITE_INDEX_CONSTRAINT_NE) { index->FilterRows([this, raw_state](uint32_t row) { const auto& state = (*deque_)[row]; return state.is_valid() && state.raw_state() != raw_state; }); } else if (op == SQLITE_INDEX_CONSTRAINT_MATCH) { index->FilterRows([this, compare](uint32_t row) { const auto& state = (*deque_)[row]; if (!state.is_valid()) return false; return (state.raw_state() & compare.raw_state()) == compare.raw_state(); }); } else { PERFETTO_FATAL("Should never reach this state"); } } StorageColumn::Comparator SchedSliceTable::EndStateColumn::Sort( const QueryConstraints::OrderBy& ob) const { if (ob.desc) { return [this](uint32_t f, uint32_t s) { const auto& a = (*deque_)[f]; const auto& b = (*deque_)[s]; if (!a.is_valid()) { return !b.is_valid() ? 0 : 1; } else if (!b.is_valid()) { return -1; } return sqlite_utils::CompareValuesAsc(a.raw_state(), b.raw_state()); }; } return [this](uint32_t f, uint32_t s) { const auto& a = (*deque_)[f]; const auto& b = (*deque_)[s]; if (!a.is_valid()) { return !b.is_valid() ? 0 : -1; } else if (!b.is_valid()) { return 1; } return sqlite_utils::CompareValuesAsc(a.raw_state(), b.raw_state()); }; } Table::ColumnType SchedSliceTable::EndStateColumn::GetType() const { return Table::ColumnType::kString; } } // namespace trace_processor } // namespace perfetto