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