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