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 18namespace SysTuning { 19namespace TraceStreamer { 20enum class Index : int32_t { ID = 0, ITID, TID, NAME, START_TS, END_TS, INTERNAL_PID, IS_MAIN_THREAD, SWITCH_COUNT }; 21ThreadTable::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 35ThreadTable::~ThreadTable() {} 36 37void 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 62std::unique_ptr<TableBase::Cursor> ThreadTable::CreateCursor() 63{ 64 return std::make_unique<Cursor>(dataCache_, this); 65} 66 67ThreadTable::Cursor::Cursor(const TraceDataCache *dataCache, TableBase *table) 68 : TableBase::Cursor(dataCache, table, dataCache->ThreadSize()) 69{ 70} 71 72ThreadTable::Cursor::~Cursor() {} 73 74int32_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 118int32_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 166void 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 174void 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} 207void 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