1 /*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
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
18 namespace SysTuning {
19 namespace TraceStreamer {
20 namespace {
21 enum Index {
22 ID = 0,
23 TS,
24 DUR,
25 CALL_ID,
26 CAT,
27 IDENTIFY,
28 NAME,
29 DEPTH,
30 COOKIE_ID,
31 PARENT_ID,
32 ARGSET,
33 CHAIN_ID,
34 SPAN_ID,
35 PARENT_SPAN_ID,
36 FLAG,
37 ARGS
38 };
39 }
CallStackTable(const TraceDataCache * dataCache)40 CallStackTable::CallStackTable(const TraceDataCache* dataCache) : TableBase(dataCache)
41 {
42 tableColumn_.push_back(TableBase::ColumnInfo("id", "INTEGER"));
43 tableColumn_.push_back(TableBase::ColumnInfo("ts", "INTEGER"));
44 tableColumn_.push_back(TableBase::ColumnInfo("dur", "INTEGER"));
45 tableColumn_.push_back(TableBase::ColumnInfo("callid", "INTEGER"));
46 tableColumn_.push_back(TableBase::ColumnInfo("cat", "TEXT"));
47 tableColumn_.push_back(TableBase::ColumnInfo("identify", "INTEGER"));
48 tableColumn_.push_back(TableBase::ColumnInfo("name", "TEXT"));
49 tableColumn_.push_back(TableBase::ColumnInfo("depth", "INTEGER"));
50 tableColumn_.push_back(TableBase::ColumnInfo("cookie", "INTEGER"));
51 tableColumn_.push_back(TableBase::ColumnInfo("parent_id", "INTEGER"));
52 tableColumn_.push_back(TableBase::ColumnInfo("argsetid", "INTEGER"));
53 tableColumn_.push_back(TableBase::ColumnInfo("chainId", "TEXT"));
54 tableColumn_.push_back(TableBase::ColumnInfo("spanId", "TEXT"));
55 tableColumn_.push_back(TableBase::ColumnInfo("parentSpanId", "TEXT"));
56 tableColumn_.push_back(TableBase::ColumnInfo("flag", "TEXT"));
57 tableColumn_.push_back(TableBase::ColumnInfo("args", "TEXT"));
58 tablePriKey_.push_back("callid");
59 tablePriKey_.push_back("ts");
60 tablePriKey_.push_back("depth");
61 }
62
~CallStackTable()63 CallStackTable::~CallStackTable() {}
64
EstimateFilterCost(FilterConstraints & fc,EstimatedIndexInfo & ei)65 void CallStackTable::EstimateFilterCost(FilterConstraints& fc, EstimatedIndexInfo& ei)
66 {
67 constexpr double filterBaseCost = 1000.0; // set-up and tear-down
68 constexpr double indexCost = 2.0;
69 ei.estimatedCost = filterBaseCost;
70
71 auto rowCount = dataCache_->GetConstInternalSlicesData().Size();
72 if (rowCount == 0 || rowCount == 1) {
73 ei.estimatedRows = rowCount;
74 ei.estimatedCost += indexCost * rowCount;
75 return;
76 }
77
78 double filterCost = 0.0;
79 auto constraints = fc.GetConstraints();
80 if (constraints.empty()) { // scan all rows
81 filterCost = rowCount;
82 } else {
83 FilterByConstraint(fc, filterCost, rowCount);
84 }
85 ei.estimatedCost += filterCost;
86 ei.estimatedRows = rowCount;
87 ei.estimatedCost += rowCount * indexCost;
88
89 ei.isOrdered = true;
90 auto orderbys = fc.GetOrderBys();
91 for (auto i = 0; i < orderbys.size(); i++) {
92 switch (orderbys[i].iColumn) {
93 case ID:
94 break;
95 default: // other columns can be sorted by SQLite
96 ei.isOrdered = false;
97 break;
98 }
99 }
100 }
101
FilterByConstraint(FilterConstraints & fc,double & filterCost,size_t rowCount)102 void CallStackTable::FilterByConstraint(FilterConstraints& fc, double& filterCost, size_t rowCount)
103 {
104 auto fcConstraints = fc.GetConstraints();
105 for (int i = 0; i < static_cast<int>(fcConstraints.size()); i++) {
106 if (rowCount <= 1) {
107 // only one row or nothing, needn't filter by constraint
108 filterCost += rowCount;
109 break;
110 }
111 const auto& c = fcConstraints[i];
112 switch (c.col) {
113 case ID: {
114 if (CanFilterId(c.op, rowCount)) {
115 fc.UpdateConstraint(i, true);
116 filterCost += 1; // id can position by 1 step
117 } else {
118 filterCost += rowCount; // scan all rows
119 }
120 break;
121 }
122 default: // other column
123 filterCost += rowCount; // scan all rows
124 break;
125 }
126 }
127 }
128
CanFilterId(const char op,size_t & rowCount)129 bool CallStackTable::CanFilterId(const char op, size_t& rowCount)
130 {
131 switch (op) {
132 case SQLITE_INDEX_CONSTRAINT_EQ:
133 rowCount = 1;
134 break;
135 case SQLITE_INDEX_CONSTRAINT_GT:
136 case SQLITE_INDEX_CONSTRAINT_GE:
137 case SQLITE_INDEX_CONSTRAINT_LE:
138 case SQLITE_INDEX_CONSTRAINT_LT:
139 // assume filter out a half of rows
140 rowCount = (rowCount >> 1);
141 break;
142 default:
143 return false;
144 }
145 return true;
146 }
147
CreateCursor()148 std::unique_ptr<TableBase::Cursor> CallStackTable::CreateCursor()
149 {
150 return std::make_unique<Cursor>(dataCache_, this);
151 }
152
Cursor(const TraceDataCache * dataCache,TableBase * table)153 CallStackTable::Cursor::Cursor(const TraceDataCache* dataCache, TableBase* table)
154 : TableBase::Cursor(dataCache, table, static_cast<uint32_t>(dataCache->GetConstInternalSlicesData().Size())),
155 slicesObj_(dataCache->GetConstInternalSlicesData())
156 {
157 }
158
~Cursor()159 CallStackTable::Cursor::~Cursor() {}
160
Filter(const FilterConstraints & fc,sqlite3_value ** argv)161 int CallStackTable::Cursor::Filter(const FilterConstraints& fc, sqlite3_value** argv)
162 {
163 // reset indexMap_
164 indexMap_ = std::make_unique<IndexMap>(0, rowCount_);
165
166 if (rowCount_ <= 0) {
167 return SQLITE_OK;
168 }
169
170 auto& cs = fc.GetConstraints();
171 for (size_t i = 0; i < cs.size(); i++) {
172 const auto& c = cs[i];
173 switch (c.col) {
174 case ID:
175 FilterId(c.op, argv[i]);
176 break;
177 case TS:
178 FilterTS(c.op, argv[i], slicesObj_.TimeStamData());
179 break;
180 case CALL_ID:
181 indexMap_->MixRange(c.op, static_cast<uint32_t>(sqlite3_value_int(argv[i])), slicesObj_.CallIds());
182 break;
183 case COOKIE_ID:
184 indexMap_->MixRange(c.op, static_cast<uint64_t>(sqlite3_value_int64(argv[i])), slicesObj_.Cookies());
185 break;
186 default:
187 break;
188 }
189 }
190
191 auto orderbys = fc.GetOrderBys();
192 for (auto i = orderbys.size(); i > 0;) {
193 i--;
194 switch (orderbys[i].iColumn) {
195 case ID:
196 indexMap_->SortBy(orderbys[i].desc);
197 break;
198 default:
199 break;
200 }
201 }
202
203 return SQLITE_OK;
204 }
205
Column(int col) const206 int CallStackTable::Cursor::Column(int col) const
207 {
208 switch (col) {
209 case ID:
210 sqlite3_result_int64(context_, CurrentRow());
211 break;
212 case TS:
213 sqlite3_result_int64(context_, static_cast<int64_t>(slicesObj_.TimeStamData()[CurrentRow()]));
214 break;
215 case DUR:
216 sqlite3_result_int64(context_, static_cast<int64_t>(slicesObj_.DursData()[CurrentRow()]));
217 break;
218 case CALL_ID:
219 sqlite3_result_int64(context_, static_cast<int64_t>(slicesObj_.CallIds()[CurrentRow()]));
220 break;
221 case CAT: {
222 if (slicesObj_.CatsData()[CurrentRow()] != INVALID_UINT64) {
223 auto catsDataIndex = static_cast<size_t>(slicesObj_.CatsData()[CurrentRow()]);
224 sqlite3_result_text(context_, dataCache_->GetDataFromDict(catsDataIndex).c_str(), STR_DEFAULT_LEN,
225 nullptr);
226 }
227 break;
228 }
229 case IDENTIFY:
230 sqlite3_result_int(context_, slicesObj_.IdentifysData()[CurrentRow()]);
231 break;
232 case NAME: {
233 if (slicesObj_.NamesData()[CurrentRow()] != INVALID_UINT64) {
234 auto nameDataIndex = static_cast<size_t>(slicesObj_.NamesData()[CurrentRow()]);
235 sqlite3_result_text(context_, dataCache_->GetDataFromDict(nameDataIndex).c_str(), STR_DEFAULT_LEN,
236 nullptr);
237 }
238 break;
239 }
240 case DEPTH:
241 sqlite3_result_int64(context_, static_cast<int64_t>(slicesObj_.Depths()[CurrentRow()]));
242 break;
243 case COOKIE_ID:
244 if (slicesObj_.Cookies()[CurrentRow()] != INVALID_UINT64) {
245 sqlite3_result_int64(context_, static_cast<int64_t>(slicesObj_.Cookies()[CurrentRow()]));
246 }
247 break;
248 case PARENT_ID: {
249 if (slicesObj_.ParentIdData()[CurrentRow()].has_value()) {
250 sqlite3_result_int64(context_, static_cast<int64_t>(slicesObj_.ParentIdData()[CurrentRow()].value()));
251 }
252 break;
253 }
254 case ARGSET:
255 if (slicesObj_.ArgSetIdsData()[CurrentRow()] != INVALID_UINT32) {
256 sqlite3_result_int64(context_, static_cast<int64_t>(slicesObj_.ArgSetIdsData()[CurrentRow()]));
257 }
258 break;
259 case CHAIN_ID:
260 sqlite3_result_text(context_, slicesObj_.ChainIds()[CurrentRow()].c_str(), STR_DEFAULT_LEN, nullptr);
261 break;
262 case SPAN_ID:
263 sqlite3_result_text(context_, slicesObj_.SpanIds()[CurrentRow()].c_str(), STR_DEFAULT_LEN, nullptr);
264 break;
265 case PARENT_SPAN_ID:
266 sqlite3_result_text(context_, slicesObj_.ParentSpanIds()[CurrentRow()].c_str(), STR_DEFAULT_LEN, nullptr);
267 break;
268 case FLAG:
269 sqlite3_result_text(context_, slicesObj_.Flags()[CurrentRow()].c_str(), STR_DEFAULT_LEN, nullptr);
270 break;
271 case ARGS:
272 sqlite3_result_text(context_, slicesObj_.ArgsData()[CurrentRow()].c_str(), STR_DEFAULT_LEN, nullptr);
273 break;
274 default:
275 TS_LOGF("Unregistered column : %d", col);
276 break;
277 }
278 return SQLITE_OK;
279 }
280 } // namespace TraceStreamer
281 } // namespace SysTuning
282