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 "process_table.h" 17 18namespace SysTuning { 19namespace TraceStreamer { 20enum class Index : int32_t { ID = 0, IPID, PID, NAME, START_TS, SWITCH_COUNT, THREAD_COUNT, SLICE_COUNT, MEM_COUNT }; 21ProcessTable::ProcessTable(const TraceDataCache *dataCache) : TableBase(dataCache) 22{ 23 tableColumn_.push_back(TableBase::ColumnInfo("id", "INTEGER")); 24 tableColumn_.push_back(TableBase::ColumnInfo("ipid", "INTEGER")); 25 tableColumn_.push_back(TableBase::ColumnInfo("pid", "INTEGER")); 26 tableColumn_.push_back(TableBase::ColumnInfo("name", "TEXT")); 27 tableColumn_.push_back(TableBase::ColumnInfo("start_ts", "INTEGER")); 28 tableColumn_.push_back(TableBase::ColumnInfo("switch_count", "INTEGER")); 29 tableColumn_.push_back(TableBase::ColumnInfo("thread_count", "INTEGER")); 30 tableColumn_.push_back(TableBase::ColumnInfo("slice_count", "INTEGER")); 31 tableColumn_.push_back(TableBase::ColumnInfo("mem_count", "INTEGER")); 32 tablePriKey_.push_back("id"); 33} 34 35ProcessTable::~ProcessTable() {} 36 37void ProcessTable::FilterByConstraint(FilterConstraints &processfc, 38 double &processfilterCost, 39 size_t processrowCount, 40 uint32_t processcurrenti) 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 &processc = processfc.GetConstraints()[processcurrenti]; 45 switch (static_cast<Index>(processc.col)) { 46 case Index::IPID: 47 case Index::ID: { 48 if (CanFilterId(processc.op, processrowCount)) { 49 processfc.UpdateConstraint(processcurrenti, true); 50 processfilterCost += 1; // id can position by 1 step 51 } else { 52 processfilterCost += processrowCount; // scan all rows 53 } 54 break; 55 } 56 default: // other column 57 processfilterCost += processrowCount; // scan all rows 58 break; 59 } 60} 61 62int32_t ProcessTable::Update(int32_t argc, sqlite3_value **argv, sqlite3_int64 *pRowid) 63{ 64 if (argc <= 1) { 65 return SQLITE_READONLY; 66 } 67 if (sqlite3_value_type(argv[0]) == SQLITE_NULL) { 68 return SQLITE_READONLY; 69 } 70 auto id = sqlite3_value_int64(argv[0]); 71 auto process = wdataCache_->GetProcessData(static_cast<InternalPid>(id)); 72 constexpr int32_t colOffset = 2; 73 for (auto i = colOffset; i < argc; i++) { 74 auto col = i - colOffset; 75 if (static_cast<Index>(col) != Index::NAME) { 76 continue; 77 } 78 const char *name = reinterpret_cast<const char *>(sqlite3_value_text(argv[i])); 79 if (name == nullptr) { 80 process->cmdLine_.clear(); 81 } else { 82 process->cmdLine_ = name; 83 } 84 break; 85 } 86 return SQLITE_OK; 87} 88 89std::unique_ptr<TableBase::Cursor> ProcessTable::CreateCursor() 90{ 91 return std::make_unique<Cursor>(dataCache_, this); 92} 93 94ProcessTable::Cursor::Cursor(const TraceDataCache *dataCache, TableBase *table) 95 : TableBase::Cursor(dataCache, table, dataCache->ProcessSize()) 96{ 97} 98 99ProcessTable::Cursor::~Cursor() {} 100 101int32_t ProcessTable::Cursor::Filter(const FilterConstraints &fc, sqlite3_value **argv) 102{ 103 // reset indexMap_ 104 indexMap_ = std::make_unique<IndexMap>(0, rowCount_); 105 106 if (rowCount_ <= 0) { 107 return SQLITE_OK; 108 } 109 110 auto processTableCs = fc.GetConstraints(); 111 std::set<uint32_t> sId = {static_cast<uint32_t>(Index::ID)}; 112 SwapIndexFront(processTableCs, sId); 113 for (size_t i = 0; i < processTableCs.size(); i++) { 114 const auto &c = processTableCs[i]; 115 switch (static_cast<Index>(c.col)) { 116 case Index::ID: 117 case Index::IPID: 118 FilterId(c.op, argv[c.idxInaConstraint]); 119 break; 120 case Index::PID: 121 FilterIndex(c.col, c.op, argv[c.idxInaConstraint]); 122 break; 123 default: 124 break; 125 } 126 } 127 128 auto processTableOrderbys = fc.GetOrderBys(); 129 for (auto i = processTableOrderbys.size(); i > 0;) { 130 i--; 131 switch (static_cast<Index>(processTableOrderbys[i].iColumn)) { 132 case Index::ID: 133 case Index::IPID: 134 indexMap_->SortBy(processTableOrderbys[i].desc); 135 break; 136 default: 137 break; 138 } 139 } 140 141 return SQLITE_OK; 142} 143 144int32_t ProcessTable::Cursor::Column(int32_t col) const 145{ 146 const auto &process = dataCache_->GetConstProcessData(CurrentRow()); 147 switch (static_cast<Index>(col)) { 148 case Index::ID: 149 case Index::IPID: 150 sqlite3_result_int64(context_, CurrentRow()); 151 break; 152 case Index::PID: 153 sqlite3_result_int64(context_, process.pid_); 154 break; 155 case Index::NAME: 156 if (process.cmdLine_.size()) { 157 sqlite3_result_text(context_, process.cmdLine_.c_str(), static_cast<int32_t>(process.cmdLine_.length()), 158 nullptr); 159 } 160 break; 161 case Index::START_TS: 162 if (process.startT_) { 163 sqlite3_result_int64(context_, static_cast<int64_t>(process.startT_)); 164 } 165 break; 166 case Index::SWITCH_COUNT: 167 sqlite3_result_int64(context_, process.switchCount_); 168 break; 169 case Index::THREAD_COUNT: 170 sqlite3_result_int64(context_, process.threadCount_); 171 break; 172 case Index::SLICE_COUNT: 173 sqlite3_result_int64(context_, process.sliceSize_); 174 break; 175 case Index::MEM_COUNT: 176 sqlite3_result_int64(context_, process.memSize_); 177 break; 178 default: 179 TS_LOGF("Unregistered column : %d", col); 180 break; 181 } 182 return SQLITE_OK; 183} 184 185void ProcessTable::Cursor::FilterPid(unsigned char op, uint64_t value) 186{ 187 bool remove = false; 188 if (indexMap_->HasData()) { 189 indexMap_->ConvertToIndexMap(); 190 remove = true; 191 } 192 switch (op) { 193 case SQLITE_INDEX_CONSTRAINT_EQ: 194 HandleIndexConstraintEQ(remove, value); 195 break; 196 case SQLITE_INDEX_CONSTRAINT_NE: 197 HandleIndexConstraintNQ(remove, value); 198 break; 199 case SQLITE_INDEX_CONSTRAINT_ISNOTNULL: 200 break; 201 default: 202 break; 203 } // end of switch (op) 204} 205void ProcessTable::Cursor::HandleIndexConstraintEQ(bool remove, uint64_t value) 206{ 207 if (remove) { 208 for (auto i = indexMap_->rowIndex_.begin(); i != indexMap_->rowIndex_.end();) { 209 if (dataCache_->GetConstProcessData()[*i].pid_ != value) { 210 i = indexMap_->rowIndex_.erase(i); 211 } else { 212 i++; 213 } 214 } 215 } else { 216 for (auto i = 0; i < dataCache_->GetConstProcessData().size(); i++) { 217 if (dataCache_->GetConstProcessData()[i].pid_ == value) { 218 indexMap_->rowIndex_.push_back(i); 219 } 220 } 221 } 222 indexMap_->FixSize(); 223} 224void ProcessTable::Cursor::HandleIndexConstraintNQ(bool remove, uint64_t value) 225{ 226 if (remove) { 227 for (auto i = indexMap_->rowIndex_.begin(); i != indexMap_->rowIndex_.end();) { 228 if (dataCache_->GetConstProcessData()[*i].pid_ == value) { 229 i = indexMap_->rowIndex_.erase(i); 230 } else { 231 i++; 232 } 233 } 234 } else { 235 for (auto i = 0; i < dataCache_->GetConstProcessData().size(); i++) { 236 if (dataCache_->GetConstProcessData()[i].pid_ != value) { 237 indexMap_->rowIndex_.push_back(i); 238 } 239 } 240 } 241 indexMap_->FixSize(); 242} 243void ProcessTable::Cursor::FilterIndex(int32_t col, unsigned char op, sqlite3_value *argv) 244{ 245 switch (static_cast<Index>(col)) { 246 case Index::PID: 247 /* code */ 248 FilterPid(op, static_cast<uint64_t>(sqlite3_value_int64(argv))); 249 break; 250 251 default: 252 break; 253 } 254} 255void ProcessTable::Cursor::FilterId(unsigned char op, sqlite3_value *argv) 256{ 257 auto procArgv = static_cast<TableRowId>(sqlite3_value_int64(argv)); 258 switch (op) { 259 case SQLITE_INDEX_CONSTRAINT_EQ: 260 indexMap_->Intersect(procArgv, procArgv + 1); 261 break; 262 case SQLITE_INDEX_CONSTRAINT_GE: 263 indexMap_->Intersect(procArgv, rowCount_); 264 break; 265 case SQLITE_INDEX_CONSTRAINT_GT: 266 procArgv++; 267 indexMap_->Intersect(procArgv, rowCount_); 268 break; 269 case SQLITE_INDEX_CONSTRAINT_LE: 270 procArgv++; 271 indexMap_->Intersect(0, procArgv); 272 break; 273 case SQLITE_INDEX_CONSTRAINT_LT: 274 indexMap_->Intersect(0, procArgv); 275 break; 276 default: 277 // can't filter, all rows 278 break; 279 } 280} 281 282void ProcessTable::GetOrbyes(FilterConstraints &processfc, EstimatedIndexInfo &processei) 283{ 284 auto processorderbys = processfc.GetOrderBys(); 285 for (auto i = 0; i < processorderbys.size(); i++) { 286 switch (static_cast<Index>(processorderbys[i].iColumn)) { 287 case Index::IPID: 288 case Index::ID: 289 break; 290 default: // other columns can be sorted by SQLite 291 processei.isOrdered = false; 292 break; 293 } 294 } 295} 296} // namespace TraceStreamer 297} // namespace SysTuning 298