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