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 "process_table.h"
17
18 namespace SysTuning {
19 namespace TraceStreamer {
20 namespace {
21 enum Index {ID = 0, IPID, TYPE, PID, NAME, START_TS, SWITCH_COUNT, THREAD_COUNT, SLICE_COUNT, MEM_COUNT };
22 }
ProcessTable(const TraceDataCache * dataCache)23 ProcessTable::ProcessTable(const TraceDataCache* dataCache) : TableBase(dataCache)
24 {
25 tableColumn_.push_back(TableBase::ColumnInfo("id", "INTEGER"));
26 tableColumn_.push_back(TableBase::ColumnInfo("ipid", "INTEGER"));
27 tableColumn_.push_back(TableBase::ColumnInfo("type", "TEXT"));
28 tableColumn_.push_back(TableBase::ColumnInfo("pid", "INTEGER"));
29 tableColumn_.push_back(TableBase::ColumnInfo("name", "TEXT"));
30 tableColumn_.push_back(TableBase::ColumnInfo("start_ts", "INTEGER"));
31 tableColumn_.push_back(TableBase::ColumnInfo("swtich_count", "INTEGER"));
32 tableColumn_.push_back(TableBase::ColumnInfo("thread_count", "INTEGER"));
33 tableColumn_.push_back(TableBase::ColumnInfo("slice_count", "INTEGER"));
34 tableColumn_.push_back(TableBase::ColumnInfo("mem_count", "INTEGER"));
35 tablePriKey_.push_back("id");
36 }
37
~ProcessTable()38 ProcessTable::~ProcessTable() {}
39
EstimateFilterCost(FilterConstraints & fc,EstimatedIndexInfo & ei)40 void ProcessTable::EstimateFilterCost(FilterConstraints& fc, EstimatedIndexInfo& ei)
41 {
42 constexpr double filterBaseCost = 1000.0; // set-up and tear-down
43 constexpr double indexCost = 2.0;
44 ei.estimatedCost = filterBaseCost;
45
46 auto rowCount = dataCache_->ProcessSize();
47 if (rowCount == 0 || rowCount == 1) {
48 ei.estimatedRows = rowCount;
49 ei.estimatedCost += indexCost * rowCount;
50 return;
51 }
52
53 double filterCost = 0.0;
54 auto constraints = fc.GetConstraints();
55 if (constraints.empty()) { // scan all rows
56 filterCost = rowCount;
57 } else {
58 FilterByConstraint(fc, filterCost, rowCount);
59 }
60 ei.estimatedCost += filterCost;
61 ei.estimatedRows = rowCount;
62 ei.estimatedCost += rowCount * indexCost;
63
64 ei.isOrdered = true;
65 auto orderbys = fc.GetOrderBys();
66 for (auto i = 0; i < orderbys.size(); i++) {
67 switch (orderbys[i].iColumn) {
68 case IPID:
69 case ID:
70 break;
71 default: // other columns can be sorted by SQLite
72 ei.isOrdered = false;
73 break;
74 }
75 }
76 }
77
FilterByConstraint(FilterConstraints & fc,double & filterCost,size_t rowCount)78 void ProcessTable::FilterByConstraint(FilterConstraints& fc, double& filterCost, size_t rowCount)
79 {
80 auto fcConstraints = fc.GetConstraints();
81 for (int i = 0; i < static_cast<int>(fcConstraints.size()); i++) {
82 if (rowCount <= 1) {
83 // only one row or nothing, needn't filter by constraint
84 filterCost += rowCount;
85 break;
86 }
87 const auto& c = fcConstraints[i];
88 switch (c.col) {
89 case IPID:
90 case ID: {
91 if (CanFilterId(c.op, rowCount)) {
92 fc.UpdateConstraint(i, true);
93 filterCost += 1; // id can position by 1 step
94 } else {
95 filterCost += rowCount; // scan all rows
96 }
97 break;
98 }
99 default: // other column
100 filterCost += rowCount; // scan all rows
101 break;
102 }
103 }
104 }
105
CanFilterId(const char op,size_t & rowCount)106 bool ProcessTable::CanFilterId(const char op, size_t& rowCount)
107 {
108 switch (op) {
109 case SQLITE_INDEX_CONSTRAINT_EQ:
110 rowCount = 1;
111 break;
112 case SQLITE_INDEX_CONSTRAINT_GT:
113 case SQLITE_INDEX_CONSTRAINT_GE:
114 case SQLITE_INDEX_CONSTRAINT_LE:
115 case SQLITE_INDEX_CONSTRAINT_LT:
116 // assume filter out a half of rows
117 rowCount = (rowCount >> 1);
118 break;
119 default:
120 return false;
121 }
122 return true;
123 }
124
Update(int argc,sqlite3_value ** argv,sqlite3_int64 * pRowid)125 int ProcessTable::Update(int argc, sqlite3_value** argv, sqlite3_int64* pRowid)
126 {
127 if (argc <= 1) {
128 return SQLITE_READONLY;
129 }
130 if (sqlite3_value_type(argv[0]) == SQLITE_NULL) {
131 return SQLITE_READONLY;
132 }
133 auto id = sqlite3_value_int64(argv[0]);
134 auto process = wdataCache_->GetProcessData(static_cast<InternalPid>(id));
135 constexpr int colOffset = 2;
136 for (auto i = colOffset; i < argc; i++) {
137 auto col = i - colOffset;
138 if (col != NAME) {
139 continue;
140 }
141 const char* name = reinterpret_cast<const char*>(sqlite3_value_text(argv[i]));
142 if (name == nullptr) {
143 process->cmdLine_.clear();
144 } else {
145 process->cmdLine_ = name;
146 }
147 break;
148 }
149 return SQLITE_OK;
150 }
151
CreateCursor()152 std::unique_ptr<TableBase::Cursor> ProcessTable::CreateCursor()
153 {
154 return std::make_unique<Cursor>(dataCache_, this);
155 }
156
Cursor(const TraceDataCache * dataCache,TableBase * table)157 ProcessTable::Cursor::Cursor(const TraceDataCache* dataCache, TableBase* table)
158 : TableBase::Cursor(dataCache, table, dataCache->ProcessSize())
159 {
160 }
161
~Cursor()162 ProcessTable::Cursor::~Cursor() {}
163
Filter(const FilterConstraints & fc,sqlite3_value ** argv)164 int ProcessTable::Cursor::Filter(const FilterConstraints& fc, sqlite3_value** argv)
165 {
166 // reset indexMap_
167 indexMap_ = std::make_unique<IndexMap>(0, rowCount_);
168
169 if (rowCount_ <= 0) {
170 return SQLITE_OK;
171 }
172
173 auto& cs = fc.GetConstraints();
174 for (size_t i = 0; i < cs.size(); i++) {
175 const auto& c = cs[i];
176 switch (c.col) {
177 case ID:
178 case IPID:
179 FilterId(c.op, argv[i]);
180 break;
181 case PID:
182 FilterIndex(c.col, c.op, argv[i]);
183 break;
184 default:
185 break;
186 }
187 }
188
189 auto orderbys = fc.GetOrderBys();
190 for (auto i = orderbys.size(); i > 0;) {
191 i--;
192 switch (orderbys[i].iColumn) {
193 case ID:
194 case IPID:
195 indexMap_->SortBy(orderbys[i].desc);
196 break;
197 default:
198 break;
199 }
200 }
201
202 return SQLITE_OK;
203 }
204
Column(int col) const205 int ProcessTable::Cursor::Column(int col) const
206 {
207 const auto& process = dataCache_->GetConstProcessData(CurrentRow());
208 switch (col) {
209 case ID:
210 case IPID:
211 sqlite3_result_int64(context_, CurrentRow());
212 break;
213 case TYPE:
214 sqlite3_result_text(context_, "process", STR_DEFAULT_LEN, nullptr);
215 break;
216 case PID:
217 sqlite3_result_int64(context_, process.pid_);
218 break;
219 case NAME:
220 if (process.cmdLine_.size()) {
221 sqlite3_result_text(context_, process.cmdLine_.c_str(), static_cast<int>(process.cmdLine_.length()),
222 nullptr);
223 }
224 break;
225 case START_TS:
226 if (process.startT_) {
227 sqlite3_result_int64(context_, static_cast<int64_t>(process.startT_));
228 }
229 break;
230 case SWITCH_COUNT:
231 sqlite3_result_int64(context_, process.switchCount_);
232 break;
233 case THREAD_COUNT:
234 sqlite3_result_int64(context_, process.threadCount_);
235 break;
236 case SLICE_COUNT:
237 sqlite3_result_int64(context_, process.sliceSize_);
238 break;
239 case MEM_COUNT:
240 sqlite3_result_int64(context_, process.memSize_);
241 break;
242 default:
243 TS_LOGF("Unregistered column : %d", col);
244 break;
245 }
246 return SQLITE_OK;
247 }
248
FilterPid(unsigned char op,uint64_t value)249 void ProcessTable::Cursor::FilterPid(unsigned char op, uint64_t value)
250 {
251 bool remove = false;
252 if (indexMap_->HasData()) {
253 indexMap_->CovertToIndexMap();
254 remove = true;
255 }
256 const auto& processQueue = dataCache_->GetConstProcessData();
257 auto size = processQueue.size();
258 switch (op) {
259 case SQLITE_INDEX_CONSTRAINT_EQ:
260 if (remove) {
261 for (auto i = indexMap_->rowIndex_.begin(); i != indexMap_->rowIndex_.end();) {
262 if (processQueue[*i].pid_ != value) {
263 i = indexMap_->rowIndex_.erase(i);
264 } else {
265 i++;
266 }
267 }
268 } else {
269 for (auto i = 0; i < size; i++) {
270 if (processQueue[i].pid_ == value) {
271 indexMap_->rowIndex_.push_back(i);
272 }
273 }
274 }
275 indexMap_->FixSize();
276 break;
277 case SQLITE_INDEX_CONSTRAINT_NE:
278 if (remove) {
279 for (auto i = indexMap_->rowIndex_.begin(); i != indexMap_->rowIndex_.end();) {
280 if (processQueue[*i].pid_ == value) {
281 i = indexMap_->rowIndex_.erase(i);
282 } else {
283 i++;
284 }
285 }
286 } else {
287 for (auto i = 0; i < size; i++) {
288 if (processQueue[i].pid_ != value) {
289 indexMap_->rowIndex_.push_back(i);
290 }
291 }
292 }
293 indexMap_->FixSize();
294 break;
295 case SQLITE_INDEX_CONSTRAINT_ISNOTNULL:
296 break;
297 default:
298 break;
299 } // end of switch (op)
300 }
FilterIndex(int col,unsigned char op,sqlite3_value * argv)301 void ProcessTable::Cursor::FilterIndex(int col, unsigned char op, sqlite3_value* argv)
302 {
303 auto type = sqlite3_value_type(argv);
304 switch (col) {
305 case PID:
306 /* code */
307 FilterPid(op, static_cast<uint64_t>(sqlite3_value_int64(argv)));
308 break;
309
310 default:
311 break;
312 }
313 }
FilterId(unsigned char op,sqlite3_value * argv)314 void ProcessTable::Cursor::FilterId(unsigned char op, sqlite3_value* argv)
315 {
316 auto type = sqlite3_value_type(argv);
317 auto v = static_cast<TableRowId>(sqlite3_value_int64(argv));
318 switch (op) {
319 case SQLITE_INDEX_CONSTRAINT_EQ:
320 indexMap_->Intersect(v, v + 1);
321 break;
322 case SQLITE_INDEX_CONSTRAINT_GE:
323 indexMap_->Intersect(v, rowCount_);
324 break;
325 case SQLITE_INDEX_CONSTRAINT_GT:
326 v++;
327 indexMap_->Intersect(v, rowCount_);
328 break;
329 case SQLITE_INDEX_CONSTRAINT_LE:
330 v++;
331 indexMap_->Intersect(0, v);
332 break;
333 case SQLITE_INDEX_CONSTRAINT_LT:
334 indexMap_->Intersect(0, v);
335 break;
336 default:
337 // can't filter, all rows
338 break;
339 }
340 }
341 } // namespace TraceStreamer
342 } // namespace SysTuning
343