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/sched_slice_table.h"
18
19 namespace perfetto {
20 namespace trace_processor {
21
SchedSliceTable(sqlite3 *,const TraceStorage * storage)22 SchedSliceTable::SchedSliceTable(sqlite3*, const TraceStorage* storage)
23 : storage_(storage) {}
24
RegisterTable(sqlite3 * db,const TraceStorage * storage)25 void SchedSliceTable::RegisterTable(sqlite3* db, const TraceStorage* storage) {
26 Table::Register<SchedSliceTable>(db, storage, "sched");
27 }
28
CreateStorageSchema()29 StorageSchema SchedSliceTable::CreateStorageSchema() {
30 const auto& slices = storage_->slices();
31 return StorageSchema::Builder()
32 .AddOrderedNumericColumn("ts", &slices.start_ns())
33 .AddNumericColumn("cpu", &slices.cpus())
34 .AddNumericColumn("dur", &slices.durations())
35 .AddGenericNumericColumn(
36 "ts_end", TsEndAccessor(&slices.start_ns(), &slices.durations()))
37 .AddNumericColumn("utid", &slices.utids(), &slices.rows_for_utids())
38 .AddColumn<EndStateColumn>("end_state", &slices.end_state())
39 .AddNumericColumn("priority", &slices.priorities())
40 .AddGenericNumericColumn("row_id", RowIdAccessor(TableId::kSched))
41 .Build({"cpu", "ts"});
42 }
43
RowCount()44 uint32_t SchedSliceTable::RowCount() {
45 return static_cast<uint32_t>(storage_->slices().slice_count());
46 }
47
BestIndex(const QueryConstraints & qc,BestIndexInfo * info)48 int SchedSliceTable::BestIndex(const QueryConstraints& qc,
49 BestIndexInfo* info) {
50 info->estimated_cost = EstimateQueryCost(qc);
51
52 // We should be able to handle any constraint and any order by clause given
53 // to us.
54 info->order_by_consumed = true;
55 std::fill(info->omit.begin(), info->omit.end(), true);
56
57 return SQLITE_OK;
58 }
59
EstimateQueryCost(const QueryConstraints & qc)60 uint32_t SchedSliceTable::EstimateQueryCost(const QueryConstraints& qc) {
61 const auto& cs = qc.constraints();
62
63 size_t ts_idx = schema().ColumnIndexFromName("ts");
64 auto has_ts_column = [ts_idx](const QueryConstraints::Constraint& c) {
65 return c.iColumn == static_cast<int>(ts_idx);
66 };
67 bool has_time_constraint = std::any_of(cs.begin(), cs.end(), has_ts_column);
68 if (has_time_constraint) {
69 // If there is a constraint on ts, we can do queries very fast (O(log n))
70 // so always make this preferred if available.
71 return 10;
72 }
73
74 size_t utid_idx = schema().ColumnIndexFromName("utid");
75 auto has_utid_eq_cs = [utid_idx](const QueryConstraints::Constraint& c) {
76 return c.iColumn == static_cast<int>(utid_idx) &&
77 sqlite_utils::IsOpEq(c.op);
78 };
79 bool has_utid_eq = std::any_of(cs.begin(), cs.end(), has_utid_eq_cs);
80 if (has_utid_eq) {
81 // The other column which is often joined on is utid. Sometimes, doing
82 // nested subqueries on the thread table is faster but with some queries,
83 // it's actually better to do subqueries on this table. Estimate the cost
84 // of filtering on utid equality constraint by dividing the number of slices
85 // by the number of threads.
86 return RowCount() / storage_->thread_count();
87 }
88
89 // If we get to this point, we do not have any special filter logic so
90 // simply return the number of rows.
91 return RowCount();
92 }
93
EndStateColumn(std::string col_name,const std::deque<ftrace_utils::TaskState> * deque)94 SchedSliceTable::EndStateColumn::EndStateColumn(
95 std::string col_name,
96 const std::deque<ftrace_utils::TaskState>* deque)
97 : StorageColumn(col_name, false), deque_(deque) {
98 for (uint16_t i = 0; i < state_strings_.size(); i++) {
99 state_strings_[i] = ftrace_utils::TaskState(i).ToString();
100 }
101 }
102 SchedSliceTable::EndStateColumn::~EndStateColumn() = default;
103
ReportResult(sqlite3_context * ctx,uint32_t row) const104 void SchedSliceTable::EndStateColumn::ReportResult(sqlite3_context* ctx,
105 uint32_t row) const {
106 const auto& state = (*deque_)[row];
107 if (state.is_valid()) {
108 PERFETTO_CHECK(state.raw_state() < state_strings_.size());
109 sqlite3_result_text(ctx, state_strings_[state.raw_state()].data(), -1,
110 sqlite_utils::kSqliteStatic);
111 } else {
112 sqlite3_result_null(ctx);
113 }
114 }
115
Filter(int op,sqlite3_value * value,FilteredRowIndex * index) const116 void SchedSliceTable::EndStateColumn::Filter(int op,
117 sqlite3_value* value,
118 FilteredRowIndex* index) const {
119 switch (op) {
120 case SQLITE_INDEX_CONSTRAINT_ISNULL:
121 case SQLITE_INDEX_CONSTRAINT_ISNOTNULL: {
122 bool non_nulls = op == SQLITE_INDEX_CONSTRAINT_ISNOTNULL;
123 index->FilterRows([this, non_nulls](uint32_t row) {
124 const auto& state = (*deque_)[row];
125 return state.is_valid() == non_nulls;
126 });
127 break;
128 }
129 case SQLITE_INDEX_CONSTRAINT_EQ:
130 case SQLITE_INDEX_CONSTRAINT_NE:
131 case SQLITE_INDEX_CONSTRAINT_MATCH:
132 FilterOnState(op, value, index);
133 break;
134 default:
135 index->set_error("Unsupported op given to filter on end_state");
136 break;
137 }
138 }
139
FilterOnState(int op,sqlite3_value * value,FilteredRowIndex * index) const140 void SchedSliceTable::EndStateColumn::FilterOnState(
141 int op,
142 sqlite3_value* value,
143 FilteredRowIndex* index) const {
144 if (sqlite3_value_type(value) != SQLITE_TEXT) {
145 index->set_error("end_state can only be filtered using strings");
146 return;
147 }
148
149 const char* str = reinterpret_cast<const char*>(sqlite3_value_text(value));
150 ftrace_utils::TaskState compare(str);
151 if (!compare.is_valid()) {
152 index->set_error("Invalid end_state string given to filter");
153 return;
154 }
155
156 uint16_t raw_state = compare.raw_state();
157 if (op == SQLITE_INDEX_CONSTRAINT_EQ) {
158 index->FilterRows([this, raw_state](uint32_t row) {
159 const auto& state = (*deque_)[row];
160 return state.is_valid() && state.raw_state() == raw_state;
161 });
162 } else if (op == SQLITE_INDEX_CONSTRAINT_NE) {
163 index->FilterRows([this, raw_state](uint32_t row) {
164 const auto& state = (*deque_)[row];
165 return state.is_valid() && state.raw_state() != raw_state;
166 });
167 } else if (op == SQLITE_INDEX_CONSTRAINT_MATCH) {
168 index->FilterRows([this, compare](uint32_t row) {
169 const auto& state = (*deque_)[row];
170 if (!state.is_valid())
171 return false;
172 return (state.raw_state() & compare.raw_state()) == compare.raw_state();
173 });
174 } else {
175 PERFETTO_FATAL("Should never reach this state");
176 }
177 }
178
Sort(const QueryConstraints::OrderBy & ob) const179 StorageColumn::Comparator SchedSliceTable::EndStateColumn::Sort(
180 const QueryConstraints::OrderBy& ob) const {
181 if (ob.desc) {
182 return [this](uint32_t f, uint32_t s) {
183 const auto& a = (*deque_)[f];
184 const auto& b = (*deque_)[s];
185 if (!a.is_valid()) {
186 return !b.is_valid() ? 0 : 1;
187 } else if (!b.is_valid()) {
188 return -1;
189 }
190 return sqlite_utils::CompareValuesAsc(a.raw_state(), b.raw_state());
191 };
192 }
193 return [this](uint32_t f, uint32_t s) {
194 const auto& a = (*deque_)[f];
195 const auto& b = (*deque_)[s];
196 if (!a.is_valid()) {
197 return !b.is_valid() ? 0 : -1;
198 } else if (!b.is_valid()) {
199 return 1;
200 }
201 return sqlite_utils::CompareValuesAsc(a.raw_state(), b.raw_state());
202 };
203 }
204
GetType() const205 Table::ColumnType SchedSliceTable::EndStateColumn::GetType() const {
206 return Table::ColumnType::kString;
207 }
208
209 } // namespace trace_processor
210 } // namespace perfetto
211