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