• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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