1 /*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "perf_sample_table.h"
17
18 namespace SysTuning {
19 namespace TraceStreamer {
20 namespace {
21 enum Index {
22 ID = 0,
23 CALLCHAIN_ID,
24 TIMESTAMP,
25 THREAD_ID,
26 EVENT_COUNT,
27 EVENT_TYPE_ID,
28 TIMESTAMP_TRACE,
29 CPU_ID,
30 THREAD_STATE
31 };
32 }
PerfSampleTable(const TraceDataCache * dataCache)33 PerfSampleTable::PerfSampleTable(const TraceDataCache* dataCache) : TableBase(dataCache)
34 {
35 tableColumn_.push_back(TableBase::ColumnInfo("id", "INTEGER"));
36 tableColumn_.push_back(TableBase::ColumnInfo("callchain_id", "INTEGER"));
37 tableColumn_.push_back(TableBase::ColumnInfo("timestamp", "INTEGER"));
38 tableColumn_.push_back(TableBase::ColumnInfo("thread_id", "INTEGER"));
39 tableColumn_.push_back(TableBase::ColumnInfo("event_count", "INTEGER"));
40 tableColumn_.push_back(TableBase::ColumnInfo("event_type_id", "INTEGER"));
41 tableColumn_.push_back(TableBase::ColumnInfo("timestamp_trace", "INTEGER"));
42 tableColumn_.push_back(TableBase::ColumnInfo("cpu_id", "INTEGER"));
43 tableColumn_.push_back(TableBase::ColumnInfo("thread_state", "TEXT"));
44 tablePriKey_.push_back("id");
45 }
46
~PerfSampleTable()47 PerfSampleTable::~PerfSampleTable() {}
48
EstimateFilterCost(FilterConstraints & fc,EstimatedIndexInfo & ei)49 void PerfSampleTable::EstimateFilterCost(FilterConstraints& fc, EstimatedIndexInfo& ei)
50 {
51 constexpr double filterBaseCost = 1000.0; // set-up and tear-down
52 constexpr double indexCost = 2.0;
53 ei.estimatedCost = filterBaseCost;
54
55 auto rowCount = dataCache_->GetConstPerfSampleData().Size();
56 if (rowCount == 0 || rowCount == 1) {
57 ei.estimatedRows = rowCount;
58 ei.estimatedCost += indexCost * rowCount;
59 return;
60 }
61
62 double filterCost = 0.0;
63 auto constraints = fc.GetConstraints();
64 if (constraints.empty()) { // scan all rows
65 filterCost = rowCount;
66 } else {
67 FilterByConstraint(fc, filterCost, rowCount);
68 }
69 ei.estimatedCost += filterCost;
70 ei.estimatedRows = rowCount;
71 ei.estimatedCost += rowCount * indexCost;
72
73 ei.isOrdered = true;
74 auto orderbys = fc.GetOrderBys();
75 for (auto i = 0; i < orderbys.size(); i++) {
76 switch (orderbys[i].iColumn) {
77 case ID:
78 break;
79 default: // other columns can be sorted by SQLite
80 ei.isOrdered = false;
81 break;
82 }
83 }
84 }
85
FilterByConstraint(FilterConstraints & fc,double & filterCost,size_t rowCount)86 void PerfSampleTable::FilterByConstraint(FilterConstraints& fc, double& filterCost, size_t rowCount)
87 {
88 auto fcConstraints = fc.GetConstraints();
89 for (int i = 0; i < static_cast<int>(fcConstraints.size()); i++) {
90 if (rowCount <= 1) {
91 // only one row or nothing, needn't filter by constraint
92 filterCost += rowCount;
93 break;
94 }
95 const auto& c = fcConstraints[i];
96 switch (c.col) {
97 case ID: {
98 if (CanFilterId(c.op, rowCount)) {
99 fc.UpdateConstraint(i, true);
100 filterCost += 1; // id can position by 1 step
101 } else {
102 filterCost += rowCount; // scan all rows
103 }
104 break;
105 }
106 default: // other column
107 filterCost += rowCount; // scan all rows
108 break;
109 }
110 }
111 }
112
CanFilterId(const char op,size_t & rowCount)113 bool PerfSampleTable::CanFilterId(const char op, size_t& rowCount)
114 {
115 switch (op) {
116 case SQLITE_INDEX_CONSTRAINT_EQ:
117 rowCount = 1;
118 break;
119 case SQLITE_INDEX_CONSTRAINT_GT:
120 case SQLITE_INDEX_CONSTRAINT_GE:
121 case SQLITE_INDEX_CONSTRAINT_LE:
122 case SQLITE_INDEX_CONSTRAINT_LT:
123 // assume filter out a half of rows
124 rowCount = (rowCount >> 1);
125 break;
126 default:
127 return false;
128 }
129 return true;
130 }
131
CreateCursor()132 std::unique_ptr<TableBase::Cursor> PerfSampleTable::CreateCursor()
133 {
134 return std::make_unique<Cursor>(dataCache_, this);
135 }
136
Cursor(const TraceDataCache * dataCache,TableBase * table)137 PerfSampleTable::Cursor::Cursor(const TraceDataCache* dataCache, TableBase* table)
138 : TableBase::Cursor(dataCache, table, static_cast<uint32_t>(dataCache->GetConstPerfSampleData().Size())),
139 perfSampleObj_(dataCache->GetConstPerfSampleData())
140 {
141 }
142
~Cursor()143 PerfSampleTable::Cursor::~Cursor() {}
144
Filter(const FilterConstraints & fc,sqlite3_value ** argv)145 int PerfSampleTable::Cursor::Filter(const FilterConstraints& fc, sqlite3_value** argv)
146 {
147 // reset indexMap_
148 indexMap_ = std::make_unique<IndexMap>(0, rowCount_);
149
150 if (rowCount_ <= 0) {
151 return SQLITE_OK;
152 }
153
154 auto& cs = fc.GetConstraints();
155 for (size_t i = 0; i < cs.size(); i++) {
156 const auto& c = cs[i];
157 switch (c.col) {
158 case ID:
159 FilterId(c.op, argv[i]);
160 break;
161 case CALLCHAIN_ID:
162 indexMap_->MixRange(c.op, static_cast<uint64_t>(sqlite3_value_int64(argv[i])),
163 perfSampleObj_.SampleIds());
164 break;
165 case THREAD_ID:
166 indexMap_->MixRange(c.op, static_cast<uint64_t>(sqlite3_value_int64(argv[i])), perfSampleObj_.Tids());
167 break;
168 case EVENT_TYPE_ID:
169 indexMap_->MixRange(c.op, static_cast<uint64_t>(sqlite3_value_int64(argv[i])),
170 perfSampleObj_.EventTypeIds());
171 break;
172 case CPU_ID:
173 indexMap_->MixRange(c.op, static_cast<uint64_t>(sqlite3_value_int64(argv[i])), perfSampleObj_.CpuIds());
174 break;
175 default:
176 break;
177 }
178 }
179 auto orderbys = fc.GetOrderBys();
180 for (auto i = orderbys.size(); i > 0;) {
181 i--;
182 switch (orderbys[i].iColumn) {
183 case ID:
184 indexMap_->SortBy(orderbys[i].desc);
185 break;
186 default:
187 break;
188 }
189 }
190
191 return SQLITE_OK;
192 }
193
Column(int column) const194 int PerfSampleTable::Cursor::Column(int column) const
195 {
196 switch (column) {
197 case ID:
198 sqlite3_result_int64(context_, static_cast<int32_t>(perfSampleObj_.IdsData()[CurrentRow()]));
199 break;
200 case CALLCHAIN_ID:
201 sqlite3_result_int64(context_, static_cast<int64_t>(perfSampleObj_.SampleIds()[CurrentRow()]));
202 break;
203 case TIMESTAMP:
204 sqlite3_result_int64(context_, static_cast<int64_t>(perfSampleObj_.TimeStamData()[CurrentRow()]));
205 break;
206 case THREAD_ID:
207 sqlite3_result_int64(context_, static_cast<int64_t>(perfSampleObj_.Tids()[CurrentRow()]));
208 break;
209 case EVENT_COUNT:
210 sqlite3_result_int64(context_, static_cast<int64_t>(perfSampleObj_.EventCounts()[CurrentRow()]));
211 break;
212 case EVENT_TYPE_ID:
213 sqlite3_result_int64(context_, static_cast<int64_t>(perfSampleObj_.EventTypeIds()[CurrentRow()]));
214 break;
215 case TIMESTAMP_TRACE:
216 sqlite3_result_int64(context_, static_cast<int64_t>(perfSampleObj_.TimestampTraces()[CurrentRow()]));
217 break;
218 case CPU_ID:
219 sqlite3_result_int64(context_, static_cast<int64_t>(perfSampleObj_.CpuIds()[CurrentRow()]));
220 break;
221 case THREAD_STATE:
222 if (perfSampleObj_.ThreadStates()[CurrentRow()] != INVALID_UINT64) {
223 auto threadStateIndex = static_cast<size_t>(perfSampleObj_.ThreadStates()[CurrentRow()]);
224 sqlite3_result_text(context_, dataCache_->GetDataFromDict(threadStateIndex).c_str(), STR_DEFAULT_LEN,
225 nullptr);
226 }
227 break;
228 default:
229 TS_LOGF("Unregistered column : %d", column);
230 break;
231 }
232 return SQLITE_OK;
233 }
234 } // namespace TraceStreamer
235 } // namespace SysTuning
236