1 /*
2  * Copyright (c) Huawei Technologies Co., Ltd. 2023. All rights reserved.
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 class Index : int32_t { ID = 0, ITID, 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("tid", "INTEGER"));
26     tableColumn_.push_back(TableBase::ColumnInfo("name", "TEXT"));
27     tableColumn_.push_back(TableBase::ColumnInfo("start_ts", "INTEGER"));
28     tableColumn_.push_back(TableBase::ColumnInfo("end_ts", "INTEGER"));
29     tableColumn_.push_back(TableBase::ColumnInfo("ipid", "INTEGER"));
30     tableColumn_.push_back(TableBase::ColumnInfo("is_main_thread", "INTEGER"));
31     tableColumn_.push_back(TableBase::ColumnInfo("switch_count", "INTEGER"));
32     tablePriKey_.push_back("id");
33 }
34 
~ThreadTable()35 ThreadTable::~ThreadTable() {}
36 
FilterByConstraint(FilterConstraints &threadfc, double &threadfilterCost, size_t threadrowCount, uint32_t threadcurrenti)37 void ThreadTable::FilterByConstraint(FilterConstraints &threadfc,
38                                      double &threadfilterCost,
39                                      size_t threadrowCount,
40                                      uint32_t threadcurrenti)
41 {
42     // To use the EstimateFilterCost function in the TableBase parent class function to calculate the i-value of each
43     // for loop
44     const auto &threadc = threadfc.GetConstraints()[threadcurrenti];
45     switch (static_cast<Index>(threadc.col)) {
46         case Index::ITID:
47         case Index::ID: {
48             if (CanFilterId(threadc.op, threadrowCount)) {
49                 threadfc.UpdateConstraint(threadcurrenti, true);
50                 threadfilterCost += 1; // id can position by 1 step
51             } else {
52                 threadfilterCost += threadrowCount; // scan all rows
53             }
54             break;
55         }
56         default:                                // other column
57             threadfilterCost += threadrowCount; // scan all rows
58             break;
59     }
60 }
61 
CreateCursor()62 std::unique_ptr<TableBase::Cursor> ThreadTable::CreateCursor()
63 {
64     return std::make_unique<Cursor>(dataCache_, this);
65 }
66 
Cursor(const TraceDataCache *dataCache, TableBase *table)67 ThreadTable::Cursor::Cursor(const TraceDataCache *dataCache, TableBase *table)
68     : TableBase::Cursor(dataCache, table, dataCache->ThreadSize())
69 {
70 }
71 
~Cursor()72 ThreadTable::Cursor::~Cursor() {}
73 
Filter(const FilterConstraints &fc, sqlite3_value **argv)74 int32_t ThreadTable::Cursor::Filter(const FilterConstraints &fc, sqlite3_value **argv)
75 {
76     // reset indexMapBack_
77     if (rowCount_ <= 0) {
78         return SQLITE_OK;
79     }
80     indexMapBack_ = indexMap_.get();
81     if (indexMap_->HasData()) {
82         indexMapBack_ = std::make_unique<IndexMap>(0, rowCount_).get();
83     }
84     auto cs = fc.GetConstraints();
85     std::set<uint32_t> sId = {static_cast<uint32_t>(Index::ID)};
86     SwapIndexFront(cs, sId);
87     for (size_t i = 0; i < cs.size(); i++) {
88         const auto &c = cs[i];
89         switch (static_cast<Index>(c.col)) {
90             case Index::ID:
91             case Index::ITID:
92                 FilterId(c.op, argv[c.idxInaConstraint]);
93                 break;
94             default:
95                 break;
96         }
97     }
98     if (indexMap_->HasData()) {
99         indexMap_->Merge(indexMapBack_);
100     }
101 
102     auto orderbys = fc.GetOrderBys();
103     for (auto i = orderbys.size(); i > 0;) {
104         i--;
105         switch (static_cast<Index>(orderbys[i].iColumn)) {
106             case Index::ID:
107             case Index::ITID:
108                 indexMap_->SortBy(orderbys[i].desc);
109                 break;
110             default:
111                 break;
112         }
113     }
114 
115     return SQLITE_OK;
116 }
117 
Column(int32_t col) const118 int32_t ThreadTable::Cursor::Column(int32_t col) const
119 {
120     const auto &thread = dataCache_->GetConstThreadData(CurrentRow());
121     switch (static_cast<Index>(col)) {
122         case Index::ID:
123         case Index::ITID: {
124             sqlite3_result_int64(context_, CurrentRow());
125             break;
126         }
127         case Index::TID: {
128             SetTypeColumnInt64(thread.tid_, INVALID_UINT32);
129             break;
130         }
131         case Index::NAME: {
132             SetNameColumn(thread);
133             break;
134         }
135         case Index::START_TS:
136             SetTypeColumnInt64NotZero(thread.startT_);
137             break;
138         case Index::END_TS:
139             SetTypeColumnInt64NotZero(thread.endT_);
140 
141             break;
142         case Index::INTERNAL_PID:
143             SetTypeColumnInt32(thread.internalPid_, INVALID_UINT32);
144             break;
145         case Index::IS_MAIN_THREAD: {
146             // When it is not clear which process the thread belongs to, is_main_thread should be set to null
147             if (thread.internalPid_ == INVALID_UINT32) {
148                 break;
149             }
150             const auto &process = dataCache_->GetConstProcessData(thread.internalPid_);
151             sqlite3_result_int(context_, thread.tid_ == process.pid_);
152             break;
153         }
154         case Index::SWITCH_COUNT: {
155             // When it is not clear which process the thread belongs to, is_main_thread should be set to null
156             sqlite3_result_int(context_, thread.switchCount_);
157             break;
158         }
159         default:
160             TS_LOGF("Unregistered column : %d", col);
161             break;
162     }
163     return SQLITE_OK;
164 }
165 
SetNameColumn(const Thread &thread) const166 void ThreadTable::Cursor::SetNameColumn(const Thread &thread) const
167 {
168     const auto &name = dataCache_->GetDataFromDict(thread.nameIndex_);
169     if (name.size()) {
170         sqlite3_result_text(context_, name.c_str(), static_cast<int32_t>(name.length()), nullptr);
171     }
172 }
173 
FilterId(unsigned char op, sqlite3_value *argv)174 void ThreadTable::Cursor::FilterId(unsigned char op, sqlite3_value *argv)
175 {
176     auto type = sqlite3_value_type(argv);
177     if (type != SQLITE_INTEGER) {
178         // other type consider it NULL
179         indexMapBack_->Intersect(0, 0);
180         return;
181     }
182 
183     auto threadTabArgv = static_cast<TableRowId>(sqlite3_value_int64(argv));
184     switch (op) {
185         case SQLITE_INDEX_CONSTRAINT_EQ:
186             indexMapBack_->Intersect(threadTabArgv, threadTabArgv + 1);
187             break;
188         case SQLITE_INDEX_CONSTRAINT_GE:
189             indexMapBack_->Intersect(threadTabArgv, rowCount_);
190             break;
191         case SQLITE_INDEX_CONSTRAINT_GT:
192             threadTabArgv++;
193             indexMapBack_->Intersect(threadTabArgv, rowCount_);
194             break;
195         case SQLITE_INDEX_CONSTRAINT_LE:
196             threadTabArgv++;
197             indexMapBack_->Intersect(0, threadTabArgv);
198             break;
199         case SQLITE_INDEX_CONSTRAINT_LT:
200             indexMapBack_->Intersect(0, threadTabArgv);
201             break;
202         default:
203             // can't filter, all rows
204             break;
205     }
206 }
GetOrbyes(FilterConstraints &fc, EstimatedIndexInfo &ei)207 void ThreadTable::GetOrbyes(FilterConstraints &fc, EstimatedIndexInfo &ei)
208 {
209     auto orderbys = fc.GetOrderBys();
210     for (auto i = 0; i < orderbys.size(); i++) {
211         switch (static_cast<Index>(orderbys[i].iColumn)) {
212             case Index::ITID:
213             case Index::ID:
214                 break;
215             default: // other columns can be sorted by SQLite
216                 ei.isOrdered = false;
217                 break;
218         }
219     }
220 }
221 } // namespace TraceStreamer
222 } // namespace SysTuning
223