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 "native_hook_table.h"
17
18 namespace SysTuning {
19 namespace TraceStreamer {
20 namespace {
21 enum Index {
22 ID = 0,
23 CALLCHAIN_ID,
24 IPID,
25 ITID,
26 EVENT_TYPE,
27 SUB_TYPE_ID,
28 START_TS,
29 END_TS,
30 DURATION,
31 ADDR,
32 MEM_SIZE,
33 ALL_MEM_SIZE,
34 CURRENT_SIZE_DUR,
35 LAST_LIB_ID
36 };
37 }
NativeHookTable(const TraceDataCache * dataCache)38 NativeHookTable::NativeHookTable(const TraceDataCache* dataCache) : TableBase(dataCache)
39 {
40 tableColumn_.push_back(TableBase::ColumnInfo("id", "INTEGER"));
41 tableColumn_.push_back(TableBase::ColumnInfo("callchain_id", "INTEGER"));
42 tableColumn_.push_back(TableBase::ColumnInfo("ipid", "INTEGER"));
43 tableColumn_.push_back(TableBase::ColumnInfo("itid", "INTEGER"));
44 tableColumn_.push_back(TableBase::ColumnInfo("event_type", "TEXT"));
45 tableColumn_.push_back(TableBase::ColumnInfo("sub_type_id", "INTEGER"));
46 tableColumn_.push_back(TableBase::ColumnInfo("start_ts", "INTEGER"));
47 tableColumn_.push_back(TableBase::ColumnInfo("end_ts", "INTEGER"));
48 tableColumn_.push_back(TableBase::ColumnInfo("dur", "INTEGER"));
49 tableColumn_.push_back(TableBase::ColumnInfo("addr", "INTEGER"));
50 tableColumn_.push_back(TableBase::ColumnInfo("heap_size", "INTEGER"));
51 tableColumn_.push_back(TableBase::ColumnInfo("all_heap_size", "INTEGER"));
52 tableColumn_.push_back(TableBase::ColumnInfo("current_size_dur", "INTEGER"));
53 tableColumn_.push_back(TableBase::ColumnInfo("last_lib_id", "INTEGER"));
54 tablePriKey_.push_back("id");
55 }
56
~NativeHookTable()57 NativeHookTable::~NativeHookTable() {}
58
EstimateFilterCost(FilterConstraints & fc,EstimatedIndexInfo & ei)59 void NativeHookTable::EstimateFilterCost(FilterConstraints& fc, EstimatedIndexInfo& ei)
60 {
61 constexpr double filterBaseCost = 1000.0; // set-up and tear-down
62 constexpr double indexCost = 2.0;
63 ei.estimatedCost = filterBaseCost;
64
65 auto rowCount = dataCache_->GetConstNativeHookData().Size();
66 if (rowCount == 0 || rowCount == 1) {
67 ei.estimatedRows = rowCount;
68 ei.estimatedCost += indexCost * rowCount;
69 return;
70 }
71
72 double filterCost = 0.0;
73 auto constraints = fc.GetConstraints();
74 if (constraints.empty()) { // scan all rows
75 filterCost = rowCount;
76 } else {
77 FilterByConstraint(fc, filterCost, rowCount);
78 }
79 ei.estimatedCost += filterCost;
80 ei.estimatedRows = rowCount;
81 ei.estimatedCost += rowCount * indexCost;
82
83 ei.isOrdered = true;
84 auto orderbys = fc.GetOrderBys();
85 for (auto i = 0; i < orderbys.size(); i++) {
86 switch (orderbys[i].iColumn) {
87 case ID:
88 break;
89 default: // other columns can be sorted by SQLite
90 ei.isOrdered = false;
91 break;
92 }
93 }
94 }
95
FilterByConstraint(FilterConstraints & fc,double & filterCost,size_t rowCount)96 void NativeHookTable::FilterByConstraint(FilterConstraints& fc, double& filterCost, size_t rowCount)
97 {
98 auto fcConstraints = fc.GetConstraints();
99 for (int i = 0; i < static_cast<int>(fcConstraints.size()); i++) {
100 if (rowCount <= 1) {
101 // only one row or nothing, needn't filter by constraint
102 filterCost += rowCount;
103 break;
104 }
105 const auto& c = fcConstraints[i];
106 switch (c.col) {
107 case ID: {
108 if (CanFilterId(c.op, rowCount)) {
109 fc.UpdateConstraint(i, true);
110 filterCost += 1; // id can position by 1 step
111 } else {
112 filterCost += rowCount; // scan all rows
113 }
114 break;
115 }
116 default: // other column
117 filterCost += rowCount; // scan all rows
118 break;
119 }
120 }
121 }
122
CanFilterId(const char op,size_t & rowCount)123 bool NativeHookTable::CanFilterId(const char op, size_t& rowCount)
124 {
125 switch (op) {
126 case SQLITE_INDEX_CONSTRAINT_EQ:
127 rowCount = 1;
128 break;
129 case SQLITE_INDEX_CONSTRAINT_GT:
130 case SQLITE_INDEX_CONSTRAINT_GE:
131 case SQLITE_INDEX_CONSTRAINT_LE:
132 case SQLITE_INDEX_CONSTRAINT_LT:
133 // assume filter out a half of rows
134 rowCount = (rowCount >> 1);
135 break;
136 default:
137 return false;
138 }
139 return true;
140 }
141
CreateCursor()142 std::unique_ptr<TableBase::Cursor> NativeHookTable::CreateCursor()
143 {
144 return std::make_unique<Cursor>(dataCache_, this);
145 }
146
Cursor(const TraceDataCache * dataCache,TableBase * table)147 NativeHookTable::Cursor::Cursor(const TraceDataCache* dataCache, TableBase* table)
148 : TableBase::Cursor(dataCache, table, static_cast<uint32_t>(dataCache->GetConstNativeHookData().Size())),
149 nativeHookObj_(dataCache->GetConstNativeHookData())
150 {
151 }
152
~Cursor()153 NativeHookTable::Cursor::~Cursor() {}
154
Filter(const FilterConstraints & fc,sqlite3_value ** argv)155 int NativeHookTable::Cursor::Filter(const FilterConstraints& fc, sqlite3_value** argv)
156 {
157 // reset indexMap_
158 indexMap_ = std::make_unique<IndexMap>(0, rowCount_);
159
160 if (rowCount_ <= 0) {
161 return SQLITE_OK;
162 }
163
164 auto& cs = fc.GetConstraints();
165 for (size_t i = 0; i < cs.size(); i++) {
166 const auto& c = cs[i];
167 switch (c.col) {
168 case ID:
169 FilterId(c.op, argv[i]);
170 break;
171 case IPID:
172 indexMap_->MixRange(c.op, static_cast<uint32_t>(sqlite3_value_int(argv[i])), nativeHookObj_.Ipids());
173 break;
174 case ITID:
175 indexMap_->MixRange(c.op, static_cast<uint32_t>(sqlite3_value_int(argv[i])), nativeHookObj_.Itids());
176 break;
177 case CALLCHAIN_ID:
178 indexMap_->MixRange(c.op, static_cast<uint64_t>(sqlite3_value_int64(argv[i])),
179 nativeHookObj_.CallChainIds());
180 break;
181 default:
182 break;
183 }
184 }
185
186 auto orderbys = fc.GetOrderBys();
187 for (auto i = orderbys.size(); i > 0;) {
188 i--;
189 switch (orderbys[i].iColumn) {
190 case ID:
191 indexMap_->SortBy(orderbys[i].desc);
192 break;
193 default:
194 break;
195 }
196 }
197
198 return SQLITE_OK;
199 }
200
Column(int column) const201 int NativeHookTable::Cursor::Column(int column) const
202 {
203 switch (column) {
204 case ID:
205 sqlite3_result_int64(context_, static_cast<int32_t>(CurrentRow()));
206 break;
207 case CALLCHAIN_ID:
208 sqlite3_result_int64(context_, static_cast<int64_t>(nativeHookObj_.CallChainIds()[CurrentRow()]));
209 break;
210 case IPID:
211 sqlite3_result_int64(context_, static_cast<int64_t>(nativeHookObj_.Ipids()[CurrentRow()]));
212 break;
213 case ITID:
214 sqlite3_result_int64(context_, static_cast<int64_t>(nativeHookObj_.Itids()[CurrentRow()]));
215 break;
216 case EVENT_TYPE: {
217 if (!nativeHookObj_.EventTypes()[CurrentRow()].empty()) {
218 sqlite3_result_text(context_, nativeHookObj_.EventTypes()[CurrentRow()].c_str(), STR_DEFAULT_LEN,
219 nullptr);
220 }
221 break;
222 }
223 case SUB_TYPE_ID: {
224 if (nativeHookObj_.SubTypes()[CurrentRow()] != INVALID_UINT64) {
225 sqlite3_result_int64(context_, static_cast<int64_t>(nativeHookObj_.SubTypes()[CurrentRow()]));
226 }
227 break;
228 }
229 case START_TS:
230 sqlite3_result_int64(context_, static_cast<int64_t>(nativeHookObj_.TimeStamData()[CurrentRow()]));
231 break;
232 case END_TS:
233 if (static_cast<int64_t>(nativeHookObj_.EndTimeStamps()[CurrentRow()]) != 0) {
234 sqlite3_result_int64(context_, static_cast<int64_t>(nativeHookObj_.EndTimeStamps()[CurrentRow()]));
235 }
236 break;
237 case DURATION:
238 if (static_cast<int64_t>(nativeHookObj_.Durations()[CurrentRow()]) != 0) {
239 sqlite3_result_int64(context_, static_cast<int64_t>(nativeHookObj_.Durations()[CurrentRow()]));
240 }
241 break;
242 case ADDR: {
243 sqlite3_result_int64(context_, static_cast<int64_t>(nativeHookObj_.Addrs()[CurrentRow()]));
244 break;
245 }
246 case MEM_SIZE: {
247 sqlite3_result_int64(context_, static_cast<int64_t>(nativeHookObj_.MemSizes()[CurrentRow()]));
248 break;
249 }
250 case ALL_MEM_SIZE: {
251 sqlite3_result_int64(context_, static_cast<int64_t>(nativeHookObj_.AllMemSizes()[CurrentRow()]));
252 break;
253 }
254 case CURRENT_SIZE_DUR: {
255 sqlite3_result_int64(context_, static_cast<int64_t>(nativeHookObj_.CurrentSizeDurs()[CurrentRow()]));
256 break;
257 }
258 case LAST_LIB_ID: {
259 if (nativeHookObj_.LastCallerPathIndexs()[CurrentRow()] != INVALID_UINT64) {
260 sqlite3_result_int64(context_,
261 static_cast<int64_t>(nativeHookObj_.LastCallerPathIndexs()[CurrentRow()]));
262 }
263 break;
264 }
265 default:
266 TS_LOGF("Unregistered column : %d", column);
267 break;
268 }
269 return SQLITE_OK;
270 }
271 } // namespace TraceStreamer
272 } // namespace SysTuning
273