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 "callstack_table.h"
17
18namespace SysTuning {
19namespace TraceStreamer {
20enum class Index : int32_t {
21    ID = 0,
22    TS,
23    DURS,
24    CALL_IDS,
25    CATS,
26    NAME,
27    DEPTHS,
28    COOKIES_ID,
29#if IS_WASM
30    COLORINDEX,
31#endif
32    PARENT_ID,
33    ARGSET,
34    CHAIN_IDS,
35    SPAN_IDS,
36    PARENT_SPAN_IDS,
37    FLAGS
38};
39CallStackTable::CallStackTable(const TraceDataCache *dataCache) : TableBase(dataCache)
40{
41    tableColumn_.push_back(TableBase::ColumnInfo("id", "INTEGER"));
42    tableColumn_.push_back(TableBase::ColumnInfo("ts", "INTEGER"));
43    tableColumn_.push_back(TableBase::ColumnInfo("dur", "INTEGER"));
44    tableColumn_.push_back(TableBase::ColumnInfo("callid", "INTEGER"));
45    tableColumn_.push_back(TableBase::ColumnInfo("cat", "TEXT"));
46    tableColumn_.push_back(TableBase::ColumnInfo("name", "TEXT"));
47    tableColumn_.push_back(TableBase::ColumnInfo("depth", "INTEGER"));
48    tableColumn_.push_back(TableBase::ColumnInfo("cookie", "INTEGER"));
49#if IS_WASM
50    tableColumn_.push_back(TableBase::ColumnInfo("colorIndex", "INTEGER"));
51#endif
52    tableColumn_.push_back(TableBase::ColumnInfo("parent_id", "INTEGER"));
53    tableColumn_.push_back(TableBase::ColumnInfo("argsetid", "INTEGER"));
54    tableColumn_.push_back(TableBase::ColumnInfo("chainId", "TEXT"));
55    tableColumn_.push_back(TableBase::ColumnInfo("spanId", "TEXT"));
56    tableColumn_.push_back(TableBase::ColumnInfo("parentSpanId", "TEXT"));
57    tableColumn_.push_back(TableBase::ColumnInfo("flag", "TEXT"));
58    tablePriKey_.push_back("callid");
59    tablePriKey_.push_back("ts");
60    tablePriKey_.push_back("depth");
61}
62
63CallStackTable::~CallStackTable() {}
64
65void CallStackTable::FilterByConstraint(FilterConstraints &callfc,
66                                        double &callfilterCost,
67                                        size_t callrowCount,
68                                        uint32_t callCurrenti)
69{
70    // To use the EstimateFilterCost function in the TableBase parent class function to calculate the i-value of each
71    // for loop
72    const auto &callc = callfc.GetConstraints()[callCurrenti];
73    switch (static_cast<Index>(callc.col)) {
74        case Index::ID: {
75            if (CanFilterId(callc.op, callrowCount)) {
76                callfc.UpdateConstraint(callCurrenti, true);
77                callfilterCost += 1; // id can position by 1 step
78            } else {
79                callfilterCost += callrowCount; // scan all rows
80            }
81            break;
82        }
83        default:                            // other column
84            callfilterCost += callrowCount; // scan all rows
85            break;
86    }
87}
88
89std::unique_ptr<TableBase::Cursor> CallStackTable::CreateCursor()
90{
91    return std::make_unique<Cursor>(dataCache_, this);
92}
93
94CallStackTable::Cursor::Cursor(const TraceDataCache *dataCache, TableBase *table)
95    : TableBase::Cursor(dataCache, table, static_cast<uint32_t>(dataCache->GetConstInternalSlicesData().Size())),
96      slicesObj_(dataCache->GetConstInternalSlicesData())
97{
98}
99
100CallStackTable::Cursor::~Cursor() {}
101
102int32_t CallStackTable::Cursor::Filter(const FilterConstraints &fc, sqlite3_value **argv)
103{
104    // reset indexMap_
105    indexMap_ = std::make_unique<IndexMap>(0, rowCount_);
106
107    if (rowCount_ <= 0) {
108        return SQLITE_OK;
109    }
110
111    auto callStackTabCs = fc.GetConstraints();
112    std::set<uint32_t> sId = {static_cast<uint32_t>(Index::TS)};
113    SwapIndexFront(callStackTabCs, sId);
114    for (size_t i = 0; i < callStackTabCs.size(); i++) {
115        const auto &c = callStackTabCs[i];
116        switch (static_cast<Index>(c.col)) {
117            case Index::ID:
118                FilterId(c.op, argv[c.idxInaConstraint]);
119                break;
120            case Index::TS:
121                FilterTS(c.op, argv[c.idxInaConstraint], slicesObj_.TimeStampData());
122                break;
123            case Index::CALL_IDS:
124                indexMap_->MixRange(c.op, static_cast<uint32_t>(sqlite3_value_int(argv[c.idxInaConstraint])),
125                                    slicesObj_.CallIds());
126                break;
127            case Index::COOKIES_ID:
128                indexMap_->MixRange(c.op, static_cast<int64_t>(sqlite3_value_int64(argv[c.idxInaConstraint])),
129                                    slicesObj_.Cookies());
130                break;
131            default:
132                break;
133        }
134    }
135
136    auto callStackTableOrderbys = fc.GetOrderBys();
137    for (auto i = callStackTableOrderbys.size(); i > 0;) {
138        i--;
139        switch (static_cast<Index>(callStackTableOrderbys[i].iColumn)) {
140            case Index::ID:
141                indexMap_->SortBy(callStackTableOrderbys[i].desc);
142                break;
143            default:
144                break;
145        }
146    }
147
148    return SQLITE_OK;
149}
150
151int32_t CallStackTable::Cursor::Column(int32_t col) const
152{
153    switch (static_cast<Index>(col)) {
154        case Index::ID:
155            sqlite3_result_int64(context_, static_cast<int64_t>(slicesObj_.IdsData()[CurrentRow()]));
156            break;
157        case Index::TS:
158            SetTypeColumnInt64(slicesObj_.TimeStampData()[CurrentRow()], INVALID_UINT64);
159            break;
160        case Index::DURS:
161            SetTypeColumnInt64(slicesObj_.DursData()[CurrentRow()], INVALID_UINT64);
162            break;
163        case Index::CALL_IDS:
164            SetTypeColumnInt64(slicesObj_.CallIds()[CurrentRow()], INVALID_UINT64);
165            break;
166#if IS_WASM
167        case Index::COLORINDEX:
168            SetTypeColumnInt64(slicesObj_.ColorIndexs()[CurrentRow()], INVALID_UINT64);
169            break;
170#endif
171        case Index::CATS: {
172            SetTypeColumnText(slicesObj_.CatsData()[CurrentRow()], INVALID_UINT64);
173            break;
174        }
175        case Index::NAME: {
176            SetTypeColumnText(slicesObj_.NamesData()[CurrentRow()], INVALID_UINT64);
177            break;
178            default:
179                HandleTypeColumns(col);
180        }
181    }
182    return SQLITE_OK;
183}
184void CallStackTable::Cursor::HandleTypeColumns(int32_t col) const
185{
186    switch (static_cast<Index>(col)) {
187        case Index::DEPTHS:
188            SetTypeColumnInt64(slicesObj_.Depths()[CurrentRow()], INVALID_UINT64);
189            break;
190        case Index::COOKIES_ID:
191            SetTypeColumnInt64(slicesObj_.Cookies()[CurrentRow()], INVALID_INT64);
192            break;
193        case Index::PARENT_ID: {
194            if (slicesObj_.ParentIdData()[CurrentRow()].has_value()) {
195                sqlite3_result_int64(context_, static_cast<int64_t>(slicesObj_.ParentIdData()[CurrentRow()].value()));
196            }
197            break;
198        }
199        case Index::ARGSET:
200            SetTypeColumnInt64(slicesObj_.ArgSetIdsData()[CurrentRow()], INVALID_UINT32);
201            break;
202        case Index::CHAIN_IDS:
203            SetTypeColumnTextNotEmpty(slicesObj_.ChainIds()[CurrentRow()].empty(),
204                                      slicesObj_.ChainIds()[CurrentRow()].c_str());
205            break;
206        case Index::SPAN_IDS:
207            SetTypeColumnTextNotEmpty(slicesObj_.SpanIds()[CurrentRow()].empty(),
208                                      slicesObj_.SpanIds()[CurrentRow()].c_str());
209            break;
210        case Index::PARENT_SPAN_IDS:
211            SetTypeColumnTextNotEmpty(slicesObj_.ParentSpanIds()[CurrentRow()].empty(),
212                                      slicesObj_.ParentSpanIds()[CurrentRow()].c_str());
213            break;
214        case Index::FLAGS:
215            SetTypeColumnTextNotEmpty(slicesObj_.Flags()[CurrentRow()].empty(),
216                                      slicesObj_.Flags()[CurrentRow()].c_str());
217            break;
218        default:
219            TS_LOGF("Unregistered column : %d", col);
220            break;
221    }
222}
223void CallStackTable::GetOrbyes(FilterConstraints &callfc, EstimatedIndexInfo &callei)
224{
225    auto orderbys = callfc.GetOrderBys();
226    for (auto i = 0; i < orderbys.size(); i++) {
227        switch (static_cast<Index>(orderbys[i].iColumn)) {
228            case Index::ID:
229                break;
230            default: // other columns can be sorted by SQLite
231                callei.isOrdered = false;
232                break;
233        }
234    }
235}
236} // namespace TraceStreamer
237} // namespace SysTuning
238