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