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 #include "src/trace_processor/process_table.h"
18 
19 #include "perfetto/base/logging.h"
20 #include "src/trace_processor/query_constraints.h"
21 #include "src/trace_processor/sqlite_utils.h"
22 
23 namespace perfetto {
24 namespace trace_processor {
25 
26 namespace {
27 
28 using namespace sqlite_utils;
29 
30 }  // namespace
31 
ProcessTable(sqlite3 *,const TraceStorage * storage)32 ProcessTable::ProcessTable(sqlite3*, const TraceStorage* storage)
33     : storage_(storage) {}
34 
RegisterTable(sqlite3 * db,const TraceStorage * storage)35 void ProcessTable::RegisterTable(sqlite3* db, const TraceStorage* storage) {
36   Table::Register<ProcessTable>(db, storage, "process");
37 }
38 
Init(int,const char * const *)39 base::Optional<Table::Schema> ProcessTable::Init(int, const char* const*) {
40   return Schema(
41       {
42           Table::Column(Column::kUpid, "upid", ColumnType::kInt),
43           Table::Column(Column::kName, "name", ColumnType::kString),
44           Table::Column(Column::kPid, "pid", ColumnType::kUint),
45           Table::Column(Column::kStartTs, "start_ts", ColumnType::kLong),
46       },
47       {Column::kUpid});
48 }
49 
CreateCursor()50 std::unique_ptr<Table::Cursor> ProcessTable::CreateCursor() {
51   return std::unique_ptr<Table::Cursor>(new Cursor(this));
52 }
53 
BestIndex(const QueryConstraints & qc,BestIndexInfo * info)54 int ProcessTable::BestIndex(const QueryConstraints& qc, BestIndexInfo* info) {
55   info->estimated_cost = static_cast<uint32_t>(storage_->process_count());
56 
57   // If the query has a constraint on the |upid| field, return a reduced cost
58   // because we can do that filter efficiently.
59   const auto& constraints = qc.constraints();
60   if (constraints.size() == 1 && constraints.front().iColumn == Column::kUpid) {
61     info->estimated_cost = IsOpEq(constraints.front().op) ? 1 : 10;
62   }
63 
64   return SQLITE_OK;
65 }
66 
Cursor(ProcessTable * table)67 ProcessTable::Cursor::Cursor(ProcessTable* table)
68     : Table::Cursor(table), storage_(table->storage_) {}
69 
Filter(const QueryConstraints & qc,sqlite3_value ** argv)70 int ProcessTable::Cursor::Filter(const QueryConstraints& qc,
71                                  sqlite3_value** argv) {
72   min = 0;
73   max = static_cast<uint32_t>(storage_->process_count()) - 1;
74   desc = false;
75   current = min;
76 
77   for (size_t j = 0; j < qc.constraints().size(); j++) {
78     const auto& cs = qc.constraints()[j];
79     if (cs.iColumn == Column::kUpid) {
80       auto constraint_upid = static_cast<UniquePid>(sqlite3_value_int(argv[j]));
81       // Set the range of upids that we are interested in, based on the
82       // constraints in the query. Everything between min and max (inclusive)
83       // will be returned.
84       if (IsOpEq(cs.op)) {
85         min = constraint_upid;
86         max = constraint_upid;
87       } else if (IsOpGe(cs.op) || IsOpGt(cs.op)) {
88         min = IsOpGt(cs.op) ? constraint_upid + 1 : constraint_upid;
89       } else if (IsOpLe(cs.op) || IsOpLt(cs.op)) {
90         max = IsOpLt(cs.op) ? constraint_upid - 1 : constraint_upid;
91       }
92     }
93   }
94   for (const auto& ob : qc.order_by()) {
95     if (ob.iColumn == Column::kUpid) {
96       desc = ob.desc;
97       current = desc ? max : min;
98     }
99   }
100   return SQLITE_OK;
101 }
102 
Column(sqlite3_context * context,int N)103 int ProcessTable::Cursor::Column(sqlite3_context* context, int N) {
104   switch (N) {
105     case Column::kUpid: {
106       sqlite3_result_int64(context, current);
107       break;
108     }
109     case Column::kName: {
110       const auto& process = storage_->GetProcess(current);
111       const auto& name = storage_->GetString(process.name_id);
112       sqlite3_result_text(context, name.c_str(), -1, kSqliteStatic);
113       break;
114     }
115     case Column::kPid: {
116       const auto& process = storage_->GetProcess(current);
117       sqlite3_result_int64(context, process.pid);
118       break;
119     }
120     case Column::kStartTs: {
121       const auto& process = storage_->GetProcess(current);
122       if (process.start_ns != 0) {
123         sqlite3_result_int64(context, process.start_ns);
124       } else {
125         sqlite3_result_null(context);
126       }
127       break;
128     }
129     default:
130       PERFETTO_FATAL("Unknown column %d", N);
131       break;
132   }
133   return SQLITE_OK;
134 }
135 
Next()136 int ProcessTable::Cursor::Next() {
137   if (desc) {
138     --current;
139   } else {
140     ++current;
141   }
142   return SQLITE_OK;
143 }
144 
Eof()145 int ProcessTable::Cursor::Eof() {
146   return desc ? current < min : current > max;
147 }
148 
149 }  // namespace trace_processor
150 }  // namespace perfetto
151