/* * 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/process_table.h" #include "perfetto/base/logging.h" #include "src/trace_processor/query_constraints.h" #include "src/trace_processor/sqlite_utils.h" namespace perfetto { namespace trace_processor { namespace { using namespace sqlite_utils; } // namespace ProcessTable::ProcessTable(sqlite3*, const TraceStorage* storage) : storage_(storage) {} void ProcessTable::RegisterTable(sqlite3* db, const TraceStorage* storage) { Table::Register(db, storage, "process"); } base::Optional ProcessTable::Init(int, const char* const*) { return Schema( { Table::Column(Column::kUpid, "upid", ColumnType::kInt), Table::Column(Column::kName, "name", ColumnType::kString), Table::Column(Column::kPid, "pid", ColumnType::kUint), Table::Column(Column::kStartTs, "start_ts", ColumnType::kLong), }, {Column::kUpid}); } std::unique_ptr ProcessTable::CreateCursor() { return std::unique_ptr(new Cursor(this)); } int ProcessTable::BestIndex(const QueryConstraints& qc, BestIndexInfo* info) { info->estimated_cost = static_cast(storage_->process_count()); // If the query has a constraint on the |upid| field, return a reduced cost // because we can do that filter efficiently. const auto& constraints = qc.constraints(); if (constraints.size() == 1 && constraints.front().iColumn == Column::kUpid) { info->estimated_cost = IsOpEq(constraints.front().op) ? 1 : 10; } return SQLITE_OK; } ProcessTable::Cursor::Cursor(ProcessTable* table) : Table::Cursor(table), storage_(table->storage_) {} int ProcessTable::Cursor::Filter(const QueryConstraints& qc, sqlite3_value** argv) { min = 0; max = static_cast(storage_->process_count()) - 1; desc = false; current = min; for (size_t j = 0; j < qc.constraints().size(); j++) { const auto& cs = qc.constraints()[j]; if (cs.iColumn == Column::kUpid) { auto constraint_upid = static_cast(sqlite3_value_int(argv[j])); // Set the range of upids that we are interested in, based on the // constraints in the query. Everything between min and max (inclusive) // will be returned. if (IsOpEq(cs.op)) { min = constraint_upid; max = constraint_upid; } else if (IsOpGe(cs.op) || IsOpGt(cs.op)) { min = IsOpGt(cs.op) ? constraint_upid + 1 : constraint_upid; } else if (IsOpLe(cs.op) || IsOpLt(cs.op)) { max = IsOpLt(cs.op) ? constraint_upid - 1 : constraint_upid; } } } for (const auto& ob : qc.order_by()) { if (ob.iColumn == Column::kUpid) { desc = ob.desc; current = desc ? max : min; } } return SQLITE_OK; } int ProcessTable::Cursor::Column(sqlite3_context* context, int N) { switch (N) { case Column::kUpid: { sqlite3_result_int64(context, current); break; } case Column::kName: { const auto& process = storage_->GetProcess(current); const auto& name = storage_->GetString(process.name_id); sqlite3_result_text(context, name.c_str(), -1, kSqliteStatic); break; } case Column::kPid: { const auto& process = storage_->GetProcess(current); sqlite3_result_int64(context, process.pid); break; } case Column::kStartTs: { const auto& process = storage_->GetProcess(current); if (process.start_ns != 0) { sqlite3_result_int64(context, process.start_ns); } else { sqlite3_result_null(context); } break; } default: PERFETTO_FATAL("Unknown column %d", N); break; } return SQLITE_OK; } int ProcessTable::Cursor::Next() { if (desc) { --current; } else { ++current; } return SQLITE_OK; } int ProcessTable::Cursor::Eof() { return desc ? current < min : current > max; } } // namespace trace_processor } // namespace perfetto