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 "thread_table.h"
17
18 namespace SysTuning {
19 namespace TraceStreamer {
20 namespace {
21 enum Index { ID = 0, ITID, TYPE, TID, NAME, START_TS, END_TS, INTERNAL_PID, IS_MAIN_THREAD, SWITCH_COUNT };
22 }
ThreadTable(const TraceDataCache * dataCache)23 ThreadTable::ThreadTable(const TraceDataCache* dataCache) : TableBase(dataCache)
24 {
25 tableColumn_.push_back(TableBase::ColumnInfo("id", "INTEGER"));
26 tableColumn_.push_back(TableBase::ColumnInfo("itid", "INTEGER"));
27 tableColumn_.push_back(TableBase::ColumnInfo("type", "TEXT"));
28 tableColumn_.push_back(TableBase::ColumnInfo("tid", "INTEGER"));
29 tableColumn_.push_back(TableBase::ColumnInfo("name", "TEXT"));
30 tableColumn_.push_back(TableBase::ColumnInfo("start_ts", "INTEGER"));
31 tableColumn_.push_back(TableBase::ColumnInfo("end_ts", "INTEGER"));
32 tableColumn_.push_back(TableBase::ColumnInfo("ipid", "INTEGER"));
33 tableColumn_.push_back(TableBase::ColumnInfo("is_main_thread", "INTEGER"));
34 tableColumn_.push_back(TableBase::ColumnInfo("switch_count", "INTEGER"));
35 tablePriKey_.push_back("id");
36 }
37
~ThreadTable()38 ThreadTable::~ThreadTable() {}
39
EstimateFilterCost(FilterConstraints & fc,EstimatedIndexInfo & ei)40 void ThreadTable::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_->ThreadSize();
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 ITID:
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 ThreadTable::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 ITID:
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 ThreadTable::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
CreateCursor()125 std::unique_ptr<TableBase::Cursor> ThreadTable::CreateCursor()
126 {
127 return std::make_unique<Cursor>(dataCache_, this);
128 }
129
Cursor(const TraceDataCache * dataCache,TableBase * table)130 ThreadTable::Cursor::Cursor(const TraceDataCache* dataCache, TableBase* table)
131 : TableBase::Cursor(dataCache, table, dataCache->ThreadSize())
132 {
133 }
134
~Cursor()135 ThreadTable::Cursor::~Cursor() {}
FilterTid(unsigned char op,uint64_t value)136 void ThreadTable::Cursor::FilterTid(unsigned char op, uint64_t value)
137 {
138 bool remove = false;
139 if (indexMapBack_->HasData()) {
140 indexMapBack_->CovertToIndexMap();
141 remove = true;
142 }
143 const auto& threadQueue = dataCache_->GetConstThreadData();
144 auto size = threadQueue.size();
145 switch (op) {
146 case SQLITE_INDEX_CONSTRAINT_EQ:
147 if (remove) {
148 for (auto i = indexMapBack_->rowIndex_.begin(); i != indexMapBack_->rowIndex_.end();) {
149 if (threadQueue[*i].tid_ != value) {
150 i = indexMapBack_->rowIndex_.erase(i);
151 } else {
152 i++;
153 }
154 }
155 } else {
156 for (auto i = 0; i < size; i++) {
157 if (threadQueue[i].tid_ == value) {
158 indexMapBack_->rowIndex_.push_back(i);
159 }
160 }
161 }
162 indexMapBack_->FixSize();
163 break;
164 case SQLITE_INDEX_CONSTRAINT_ISNOT:
165 case SQLITE_INDEX_CONSTRAINT_NE:
166 if (remove) {
167 for (auto i = indexMapBack_->rowIndex_.begin(); i != indexMapBack_->rowIndex_.end();) {
168 if (threadQueue[*i].tid_ == value) {
169 i = indexMapBack_->rowIndex_.erase(i);
170 } else {
171 i++;
172 }
173 }
174 } else {
175 for (auto i = 0; i < size; i++) {
176 if (threadQueue[i].tid_ != value) {
177 indexMapBack_->rowIndex_.push_back(i);
178 }
179 }
180 }
181 indexMapBack_->FixSize();
182 break;
183 default:
184 break;
185 } // end of switch (op)
186 }
FilterIpid(unsigned char op,uint64_t value)187 void ThreadTable::Cursor::FilterIpid(unsigned char op, uint64_t value)
188 {
189 bool remove = false;
190 if (indexMapBack_->HasData()) {
191 indexMapBack_->CovertToIndexMap();
192 remove = true;
193 }
194 const auto& threadQueue = dataCache_->GetConstThreadData();
195 auto size = threadQueue.size();
196 rowIndexBak_.clear();
197 bool changed = false;
198 switch (op) {
199 case SQLITE_INDEX_CONSTRAINT_EQ:
200 if (remove) {
201 for (auto i = indexMapBack_->rowIndex_.begin(); i != indexMapBack_->rowIndex_.end();) {
202 if (threadQueue[*i].internalPid_ != value) {
203 i++;
204 } else {
205 changed = true;
206 rowIndexBak_.push_back(*i);
207 i++;
208 }
209 }
210 if (changed) {
211 indexMapBack_->rowIndex_ = rowIndexBak_;
212 }
213 } else {
214 for (auto i = 0; i < size; i++) {
215 if (threadQueue[i].internalPid_ == value) {
216 indexMapBack_->rowIndex_.push_back(i);
217 }
218 }
219 }
220 indexMapBack_->FixSize();
221 break;
222 case SQLITE_INDEX_CONSTRAINT_ISNULL:
223 if (remove) {
224 for (auto i = indexMapBack_->rowIndex_.begin(); i != indexMapBack_->rowIndex_.end();) {
225 if (threadQueue[*i].internalPid_ != INVALID_UINT32) {
226 i++;
227 } else {
228 changed = true;
229 rowIndexBak_.push_back(*i);
230 i++;
231 }
232 }
233 if (changed) {
234 indexMapBack_->rowIndex_ = rowIndexBak_;
235 }
236 } else {
237 for (auto i = 0; i < size; i++) {
238 if (threadQueue[i].internalPid_ == INVALID_UINT32) {
239 indexMapBack_->rowIndex_.push_back(i);
240 }
241 }
242 }
243 indexMapBack_->FixSize();
244 break;
245 case SQLITE_INDEX_CONSTRAINT_ISNOTNULL:
246 if (remove) {
247 for (auto i = indexMapBack_->rowIndex_.begin(); i != indexMapBack_->rowIndex_.end();) {
248 if (threadQueue[*i].internalPid_ == INVALID_UINT32) {
249 i++;
250 } else {
251 changed = true;
252 rowIndexBak_.push_back(*i);
253 i++;
254 }
255 }
256 if (changed) {
257 indexMapBack_->rowIndex_ = rowIndexBak_;
258 }
259 } else {
260 for (auto i = 0; i < size; i++) {
261 if (threadQueue[i].internalPid_ != INVALID_UINT32) {
262 indexMapBack_->rowIndex_.push_back(i);
263 }
264 }
265 }
266 indexMapBack_->FixSize();
267 break;
268 default:
269 break;
270 } // end of switch (op)
271 }
FilterSwitchCount(unsigned char op,uint64_t value)272 void ThreadTable::Cursor::FilterSwitchCount(unsigned char op, uint64_t value)
273 {
274 bool remove = false;
275 if (indexMapBack_->HasData()) {
276 indexMapBack_->CovertToIndexMap();
277 remove = true;
278 }
279 const auto& threadQueue = dataCache_->GetConstThreadData();
280 auto size = threadQueue.size();
281 rowIndexBak_.clear();
282 bool changed = false;
283 switch (op) {
284 case SQLITE_INDEX_CONSTRAINT_EQ:
285 if (remove) {
286 for (auto i = indexMapBack_->rowIndex_.begin(); i != indexMapBack_->rowIndex_.end();) {
287 if (threadQueue[*i].switchCount_ != value) {
288 i++;
289 } else {
290 changed = true;
291 rowIndexBak_.push_back(*i);
292 i++;
293 }
294 }
295 if (changed) {
296 indexMapBack_->rowIndex_ = rowIndexBak_;
297 }
298 } else {
299 for (auto i = 0; i < size; i++) {
300 if (threadQueue[i].switchCount_ == value) {
301 indexMapBack_->rowIndex_.push_back(i);
302 }
303 }
304 }
305 indexMapBack_->FixSize();
306 break;
307 case SQLITE_INDEX_CONSTRAINT_ISNULL:
308 if (remove) {
309 for (auto i = indexMapBack_->rowIndex_.begin(); i != indexMapBack_->rowIndex_.end();) {
310 if (threadQueue[*i].switchCount_ != INVALID_UINT32) {
311 i++;
312 } else {
313 changed = true;
314 rowIndexBak_.push_back(*i);
315 i++;
316 }
317 }
318 if (changed) {
319 indexMapBack_->rowIndex_ = rowIndexBak_;
320 }
321 } else {
322 for (auto i = 0; i < size; i++) {
323 if (threadQueue[i].switchCount_ == INVALID_UINT32) {
324 indexMapBack_->rowIndex_.push_back(i);
325 }
326 }
327 }
328 indexMapBack_->FixSize();
329 break;
330 case SQLITE_INDEX_CONSTRAINT_ISNOTNULL:
331 if (remove) {
332 for (auto i = indexMapBack_->rowIndex_.begin(); i != indexMapBack_->rowIndex_.end();) {
333 if (threadQueue[*i].switchCount_ == INVALID_UINT32) {
334 i++;
335 } else {
336 changed = true;
337 rowIndexBak_.push_back(*i);
338 i++;
339 }
340 }
341 if (changed) {
342 indexMapBack_->rowIndex_ = rowIndexBak_;
343 }
344 } else {
345 for (auto i = 0; i < size; i++) {
346 if (threadQueue[i].switchCount_ != INVALID_UINT32) {
347 indexMapBack_->rowIndex_.push_back(i);
348 }
349 }
350 }
351 indexMapBack_->FixSize();
352 break;
353 default:
354 break;
355 } // end of switch (op)
356 }
FilterIndex(int col,unsigned char op,sqlite3_value * argv)357 void ThreadTable::Cursor::FilterIndex(int col, unsigned char op, sqlite3_value* argv)
358 {
359 switch (col) {
360 case INTERNAL_PID:
361 FilterIpid(op, static_cast<uint64_t>(sqlite3_value_int64(argv)));
362 break;
363 case TID:
364 FilterTid(op, static_cast<uint64_t>(sqlite3_value_int64(argv)));
365 break;
366 case SWITCH_COUNT:
367 FilterSwitchCount(op, static_cast<uint64_t>(sqlite3_value_int64(argv)));
368 break;
369 default:
370 // we can't filter all rows
371 break;
372 }
373 }
Filter(const FilterConstraints & fc,sqlite3_value ** argv)374 int ThreadTable::Cursor::Filter(const FilterConstraints& fc, sqlite3_value** argv)
375 {
376 // reset indexMapBack_
377 if (rowCount_ <= 0) {
378 return SQLITE_OK;
379 }
380 indexMapBack_ = indexMap_.get();
381 if (indexMap_->HasData()) {
382 indexMapBack_ = std::make_unique<IndexMap>(0, rowCount_).get();
383 }
384 auto& cs = fc.GetConstraints();
385 for (size_t i = 0; i < cs.size(); i++) {
386 const auto& c = cs[i];
387 switch (c.col) {
388 case ID:
389 case ITID:
390 FilterId(c.op, argv[i]);
391 break;
392 case TID:
393 case INTERNAL_PID:
394 case SWITCH_COUNT:
395 FilterIndex(c.col, c.op, argv[i]);
396 break;
397 default:
398 break;
399 }
400 }
401 if (indexMap_->HasData()) {
402 indexMap_->Merge(indexMapBack_);
403 }
404
405 auto orderbys = fc.GetOrderBys();
406 for (auto i = orderbys.size(); i > 0;) {
407 i--;
408 switch (orderbys[i].iColumn) {
409 case ID:
410 case ITID:
411 indexMap_->SortBy(orderbys[i].desc);
412 break;
413 default:
414 break;
415 }
416 }
417
418 return SQLITE_OK;
419 }
420
Column(int col) const421 int ThreadTable::Cursor::Column(int col) const
422 {
423 const auto& thread = dataCache_->GetConstThreadData(CurrentRow());
424 switch (col) {
425 case ID:
426 case ITID: {
427 sqlite3_result_int64(context_, CurrentRow());
428 break;
429 }
430 case TYPE: {
431 sqlite3_result_text(context_, "thread", strlen("thread"), nullptr);
432 break;
433 }
434 case TID: {
435 sqlite3_result_int64(context_, static_cast<int>(thread.tid_));
436 break;
437 }
438 case NAME: {
439 const auto& name = dataCache_->GetDataFromDict(thread.nameIndex_);
440 if (name.size()) {
441 sqlite3_result_text(context_, name.c_str(), static_cast<int>(name.length()), nullptr);
442 }
443 break;
444 }
445 case START_TS: {
446 if (thread.startT_) {
447 sqlite3_result_int64(context_, static_cast<int64_t>(thread.startT_));
448 }
449 break;
450 }
451 case END_TS: {
452 if (thread.endT_) {
453 sqlite3_result_int64(context_, static_cast<int64_t>(thread.endT_));
454 }
455 break;
456 }
457 case INTERNAL_PID: {
458 if (thread.internalPid_ != INVALID_UINT32) {
459 sqlite3_result_int(context_, static_cast<int>(thread.internalPid_));
460 }
461 break;
462 }
463 case IS_MAIN_THREAD: {
464 // When it is not clear which process the thread belongs to, is_main_thread should be set to null
465 if (thread.internalPid_ == INVALID_UINT32) {
466 break;
467 }
468 const auto& process = dataCache_->GetConstProcessData(thread.internalPid_);
469 sqlite3_result_int(context_, thread.tid_ == process.pid_);
470 break;
471 }
472 case SWITCH_COUNT: {
473 // When it is not clear which process the thread belongs to, is_main_thread should be set to null
474 sqlite3_result_int(context_, thread.switchCount_);
475 break;
476 }
477
478 default:
479 TS_LOGF("Unregistered column : %d", col);
480 break;
481 }
482 return SQLITE_OK;
483 }
484
Update(int argc,sqlite3_value ** argv,sqlite3_int64 * pRowid)485 int ThreadTable::Update(int argc, sqlite3_value** argv, sqlite3_int64* pRowid)
486 {
487 if (argc <= 1) {
488 return SQLITE_READONLY;
489 }
490 if (sqlite3_value_type(argv[0]) == SQLITE_NULL) {
491 return SQLITE_READONLY;
492 }
493 auto id = sqlite3_value_int64(argv[0]);
494 auto thread = wdataCache_->GetThreadData(static_cast<InternalPid>(id));
495 constexpr int colOffset = 2;
496 for (auto i = colOffset; i < argc; i++) {
497 auto col = i - colOffset;
498 if (col != INTERNAL_PID) {
499 continue;
500 }
501 auto ipid = static_cast<uint32_t>(sqlite3_value_int(argv[i]));
502 if (ipid) {
503 thread->internalPid_ = ipid;
504 }
505 break;
506 }
507 return SQLITE_OK;
508 }
FilterId(unsigned char op,sqlite3_value * argv)509 void ThreadTable::Cursor::FilterId(unsigned char op, sqlite3_value* argv)
510 {
511 auto type = sqlite3_value_type(argv);
512 if (type != SQLITE_INTEGER) {
513 // other type consider it NULL
514 indexMapBack_->Intersect(0, 0);
515 return;
516 }
517
518 auto v = static_cast<TableRowId>(sqlite3_value_int64(argv));
519 switch (op) {
520 case SQLITE_INDEX_CONSTRAINT_EQ:
521 indexMapBack_->Intersect(v, v + 1);
522 break;
523 case SQLITE_INDEX_CONSTRAINT_GE:
524 indexMapBack_->Intersect(v, rowCount_);
525 break;
526 case SQLITE_INDEX_CONSTRAINT_GT:
527 v++;
528 indexMapBack_->Intersect(v, rowCount_);
529 break;
530 case SQLITE_INDEX_CONSTRAINT_LE:
531 v++;
532 indexMapBack_->Intersect(0, v);
533 break;
534 case SQLITE_INDEX_CONSTRAINT_LT:
535 indexMapBack_->Intersect(0, v);
536 break;
537 default:
538 // can't filter, all rows
539 break;
540 }
541 }
542 } // namespace TraceStreamer
543 } // namespace SysTuning
544