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