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 
18 namespace SysTuning {
19 namespace TraceStreamer {
20 enum class Index : int32_t { ID = 0, IPID, PID, NAME, START_TS, SWITCH_COUNT, THREAD_COUNT, SLICE_COUNT, MEM_COUNT };
ProcessTable(const TraceDataCache *dataCache)21 ProcessTable::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 
~ProcessTable()35 ProcessTable::~ProcessTable() {}
36 
FilterByConstraint(FilterConstraints &processfc, double &processfilterCost, size_t processrowCount, uint32_t processcurrenti)37 void 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 
Update(int32_t argc, sqlite3_value **argv, sqlite3_int64 *pRowid)62 int32_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 
CreateCursor()89 std::unique_ptr<TableBase::Cursor> ProcessTable::CreateCursor()
90 {
91     return std::make_unique<Cursor>(dataCache_, this);
92 }
93 
Cursor(const TraceDataCache *dataCache, TableBase *table)94 ProcessTable::Cursor::Cursor(const TraceDataCache *dataCache, TableBase *table)
95     : TableBase::Cursor(dataCache, table, dataCache->ProcessSize())
96 {
97 }
98 
~Cursor()99 ProcessTable::Cursor::~Cursor() {}
100 
Filter(const FilterConstraints &fc, sqlite3_value **argv)101 int32_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 
Column(int32_t col) const144 int32_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 
FilterPid(unsigned char op, uint64_t value)185 void 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 }
HandleIndexConstraintEQ(bool remove, uint64_t value)205 void 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 }
HandleIndexConstraintNQ(bool remove, uint64_t value)224 void 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 }
FilterIndex(int32_t col, unsigned char op, sqlite3_value *argv)243 void 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 }
FilterId(unsigned char op, sqlite3_value *argv)255 void 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 
GetOrbyes(FilterConstraints &processfc, EstimatedIndexInfo &processei)282 void 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