• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "table_base.h"
17 
18 #include <cctype>
19 #include <cstring>
20 
21 #include "log.h"
22 
23 namespace SysTuning {
24 namespace TraceStreamer {
25 namespace {
26 struct TableContext {
27     TabTemplate tmplate;
28     TraceDataCache* dataCache;
29     sqlite3_module module;
30     std::string tableName;
31 };
32 } // namespace
33 
~TableBase()34 TableBase::~TableBase()
35 {
36     dataCache_ = nullptr;
37     cursor_ = nullptr;
38 }
39 
TableRegister(sqlite3 & db,TraceDataCache * cache,const std::string & tableName,TabTemplate tmplate)40 void TableBase::TableRegister(sqlite3& db, TraceDataCache* cache, const std::string& tableName, TabTemplate tmplate)
41 {
42     std::unique_ptr<TableContext> context(std::make_unique<TableContext>());
43     context->dataCache = cache;
44     context->tmplate = tmplate;
45     context->tableName = tableName;
46     sqlite3_module& module = context->module;
47     module = {0};
48 
49     auto createFn = [](sqlite3* xdb, void* pAux, int32_t argc, const char* const* argv, sqlite3_vtab** ppVTab,
50                        char** pzErr) {
51         Unused(argc);
52         Unused(argv);
53         Unused(pzErr);
54         auto xdesc = static_cast<const TableContext*>(pAux);
55         auto table = xdesc->tmplate(xdesc->dataCache);
56         table->name_ = xdesc->tableName;
57         if (table->name_ == "process" || table->name_ == "thread") {
58             table->wdataCache_ = xdesc->dataCache;
59         }
60 
61         table->Init(argc, argv);
62         std::string createStmt = table->CreateTableSql();
63         TS_LOGD("xCreate table %s, statement: %s", table->name_.c_str(), createStmt.c_str());
64         int32_t ret = sqlite3_declare_vtab(xdb, createStmt.c_str());
65         if (ret != SQLITE_OK) {
66             if ((table->name_ == "span_join") || (table->name_ == "_span_join")) {
67                 return ret;
68             }
69             TS_LOGE("sqlite3_declare_vtab %s faild: %s", table->name_.c_str(), createStmt.c_str());
70             return ret;
71         }
72         *ppVTab = table.release();
73         return SQLITE_OK;
74     };
75 
76     auto destroyFn = [](sqlite3_vtab* t) {
77         TS_LOGD("xDestroy table %s", static_cast<TableBase*>(t)->name_.c_str());
78         delete static_cast<TableBase*>(t);
79         return SQLITE_OK;
80     };
81     module.xCreate = createFn;
82     module.xConnect = createFn;
83     module.xDisconnect = destroyFn;
84     module.xDestroy = destroyFn;
85 
86     SetModuleCallbacks(module, tableName);
87     sqlite3_create_module_v2(&db, tableName.c_str(), &module, context.release(),
88                              [](void* arg) { delete static_cast<TableContext*>(arg); });
89 }
90 
SetModuleCallbacks(sqlite3_module & module,const std::string & tableName)91 void TableBase::SetModuleCallbacks(sqlite3_module& module, const std::string& tableName)
92 {
93     module.xOpen = [](sqlite3_vtab* pVTab, sqlite3_vtab_cursor** ppCursor) {
94         TS_LOGD("xOpen: %s", static_cast<TableBase*>(pVTab)->name_.c_str());
95         return static_cast<TableBase*>(pVTab)->Open(ppCursor);
96     };
97 
98     module.xClose = [](sqlite3_vtab_cursor* vc) {
99         TS_LOGD("xClose: %s", static_cast<Cursor*>(vc)->table_->name_.c_str());
100         delete static_cast<Cursor*>(vc);
101         return SQLITE_OK;
102     };
103 
104     module.xBestIndex = [](sqlite3_vtab* pVTab, sqlite3_index_info* idxInfo) {
105         TS_LOGD("xBestIndex: %s %d", static_cast<TableBase*>(pVTab)->name_.c_str(), idxInfo->nConstraint);
106         return static_cast<TableBase*>(pVTab)->BestIndex(idxInfo);
107     };
108 
109     module.xFilter = [](sqlite3_vtab_cursor* vc, int32_t idxNum, const char* idxStr, int32_t argc,
110                         sqlite3_value** argv) {
111         auto* c = static_cast<Cursor*>(vc);
112         c->Reset();
113         TS_LOGD("xFilter %s: [%d]%s", static_cast<Cursor*>(vc)->table_->name_.c_str(), idxNum, idxStr);
114         if (c->table_->cacheIdxNum_ != idxNum) {
115             c->table_->cacheConstraint_.Clear();
116             c->table_->cacheConstraint_.FromString(idxStr);
117             c->table_->cacheIdxNum_ = idxNum;
118         }
119         return c->Filter(c->table_->cacheConstraint_, argv);
120     };
121     module.xNext = [](sqlite3_vtab_cursor* vc) { return static_cast<TableBase::Cursor*>(vc)->Next(); };
122     module.xEof = [](sqlite3_vtab_cursor* vc) { return static_cast<TableBase::Cursor*>(vc)->Eof(); };
123     module.xColumn = [](sqlite3_vtab_cursor* vc, sqlite3_context* ctx, int32_t col) {
124         static_cast<TableBase::Cursor*>(vc)->context_ = ctx;
125         return static_cast<TableBase::Cursor*>(vc)->Column(col);
126     };
127     if (tableName == "process" || tableName == "thread") {
128         module.xUpdate = [](sqlite3_vtab* pVTab, int32_t argc, sqlite3_value** argv, sqlite3_int64* pRowid) {
129             TS_LOGD("xUpdate: %s", static_cast<TableBase*>(pVTab)->name_.c_str());
130             return static_cast<TableBase*>(pVTab)->Update(argc, argv, pRowid);
131         };
132     }
133 }
134 
CreateTableSql() const135 std::string TableBase::CreateTableSql() const
136 {
137     std::string stmt = "CREATE TABLE x(";
138     for (const auto& col : tableColumn_) {
139         stmt += " " + col.name_ + " " + col.type_;
140         stmt += ",";
141     }
142     stmt += " PRIMARY KEY(";
143     for (size_t i = 0; i < tablePriKey_.size(); i++) {
144         if (i != 0)
145             stmt += ", ";
146         stmt += tablePriKey_.at(i);
147     }
148     stmt += ")) WITHOUT ROWID;";
149     return stmt;
150 }
Next()151 int32_t TableBase::Cursor::Next()
152 {
153     indexMap_->Next();
154     return SQLITE_OK;
155 }
156 
Eof()157 int32_t TableBase::Cursor::Eof()
158 {
159     return dataCache_->Cancel() || indexMap_->Eof();
160 }
CurrentRow() const161 uint32_t TableBase::Cursor::CurrentRow() const
162 {
163     return indexMap_->CurrentRow();
164 }
FilterEnd()165 void TableBase::Cursor::FilterEnd()
166 {
167     indexMap_->Sort();
168 }
BestIndex(sqlite3_index_info * idxInfo)169 int32_t TableBase::BestIndex(sqlite3_index_info* idxInfo)
170 {
171     FilterConstraints filterConstraints;
172     for (int32_t i = 0; i < idxInfo->nConstraint; i++) {
173         const auto& constraint = idxInfo->aConstraint[i];
174         if (constraint.usable) {
175             filterConstraints.AddConstraint(i, constraint.iColumn, constraint.op);
176         }
177     }
178     for (int32_t i = 0; i < idxInfo->nOrderBy; i++) {
179         filterConstraints.AddOrderBy(idxInfo->aOrderBy[i].iColumn, idxInfo->aOrderBy[i].desc);
180     }
181 
182     EstimatedIndexInfo estimate = {idxInfo->estimatedRows, idxInfo->estimatedCost, false};
183     EstimateFilterCost(filterConstraints, estimate);
184 
185     idxInfo->orderByConsumed = estimate.isOrdered;
186     idxInfo->estimatedCost = estimate.estimatedCost;
187     idxInfo->estimatedRows = estimate.estimatedRows;
188 
189     auto cs = filterConstraints.GetConstraints();
190     for (size_t i = 0; i < cs.size(); i++) {
191         auto& c = cs[i];
192         idxInfo->aConstraintUsage[c.idxInaConstraint].argvIndex = static_cast<int32_t>(i + 1);
193         idxInfo->aConstraintUsage[c.idxInaConstraint].omit = c.isSupport;
194     }
195 
196     std::string str;
197     filterConstraints.ToString(str);
198     char* pIdxStr = static_cast<char*>(sqlite3_malloc(str.size() + 1));
199     std::copy(str.begin(), str.end(), pIdxStr);
200     pIdxStr[str.size()] = '\0';
201     idxInfo->idxStr = pIdxStr;
202     idxInfo->needToFreeIdxStr = true;
203     idxInfo->idxNum = ++bestIndexNum_;
204 
205     TS_LOGD("%s BestIndex return: %d: %s", name_.c_str(), idxInfo->idxNum, str.c_str());
206     TS_LOGD("%s, aConstraintUsage[%d]", idxInfo->idxStr, idxInfo->nConstraint);
207     for (int32_t i = 0; i < idxInfo->nConstraint; i++) {
208         TS_LOGD("col: %d op: %d, argvindex: %d omit: %d", idxInfo->aConstraint[i].iColumn, idxInfo->aConstraint[i].op,
209                 idxInfo->aConstraintUsage[i].argvIndex, idxInfo->aConstraintUsage[i].omit);
210     }
211     TS_LOGD("estimated: %lld cost:%.3f", idxInfo->estimatedRows, idxInfo->estimatedCost);
212 
213     return SQLITE_OK;
214 }
215 
Open(sqlite3_vtab_cursor ** ppCursor)216 int32_t TableBase::Open(sqlite3_vtab_cursor** ppCursor)
217 {
218     *ppCursor = static_cast<sqlite3_vtab_cursor*>(CreateCursor().release());
219     return SQLITE_OK;
220 }
221 
Cursor(const TraceDataCache * dataCache,TableBase * table,uint32_t rowCount)222 TableBase::Cursor::Cursor(const TraceDataCache* dataCache, TableBase* table, uint32_t rowCount)
223     : context_(nullptr),
224       table_(table),
225       dataCache_(dataCache),
226       indexMap_(std::make_unique<IndexMap>(0, rowCount)),
227       rowCount_(rowCount)
228 {
229 }
230 
CanFilterId(const char op,size_t & rowCount)231 bool TableBase::CanFilterId(const char op, size_t& rowCount)
232 {
233     switch (op) {
234         case SQLITE_INDEX_CONSTRAINT_EQ:
235             rowCount = 1;
236             break;
237         case SQLITE_INDEX_CONSTRAINT_GT:
238         case SQLITE_INDEX_CONSTRAINT_GE:
239         case SQLITE_INDEX_CONSTRAINT_LE:
240         case SQLITE_INDEX_CONSTRAINT_LT:
241             // assume filter out a half of rows
242             rowCount = (rowCount >> 1);
243             break;
244         default:
245             return false;
246     }
247     return true;
248 }
249 
~Cursor()250 TableBase::Cursor::~Cursor()
251 {
252     context_ = nullptr;
253     dataCache_ = nullptr;
254 }
FilterTS(unsigned char op,sqlite3_value * argv,const std::deque<InternalTime> & times)255 void TableBase::Cursor::FilterTS(unsigned char op, sqlite3_value* argv, const std::deque<InternalTime>& times)
256 {
257     auto v = static_cast<uint64_t>(sqlite3_value_int64(argv));
258     auto getValue = [](const uint64_t& row) { return row; };
259     switch (op) {
260         case SQLITE_INDEX_CONSTRAINT_EQ:
261             indexMap_->IntersectabcEqual(times, v, getValue);
262             break;
263         case SQLITE_INDEX_CONSTRAINT_GT:
264             v++;
265         case SQLITE_INDEX_CONSTRAINT_GE: {
266             indexMap_->IntersectGreaterEqual(times, v, getValue);
267             break;
268         }
269         case SQLITE_INDEX_CONSTRAINT_LE:
270             v++;
271         case SQLITE_INDEX_CONSTRAINT_LT: {
272             indexMap_->IntersectLessEqual(times, v, getValue);
273             break;
274         }
275         case SQLITE_INDEX_CONSTRAINT_ISNOTNULL: {
276             indexMap_->RemoveNullElements(times, v);
277             break;
278         }
279         default:
280             break;
281     } // end of switch (op)
282 }
283 
RowId(sqlite3_int64 * id)284 int32_t TableBase::Cursor::RowId(sqlite3_int64* id)
285 {
286     if (dataCache_->Cancel() || indexMap_->Eof()) {
287         return SQLITE_ERROR;
288     }
289     *id = static_cast<sqlite3_int64>(indexMap_->CurrentRow());
290     return SQLITE_OK;
291 }
FilterId(unsigned char op,sqlite3_value * argv)292 void TableBase::Cursor::FilterId(unsigned char op, sqlite3_value* argv)
293 {
294     auto type = sqlite3_value_type(argv);
295     if (type != SQLITE_INTEGER) {
296         // other type consider it NULL
297         indexMap_->Intersect(0, 0);
298         return;
299     }
300     if (indexMap_->HasData()) {
301         indexMap_->CovertToIndexMap();
302     }
303 
304     auto v = static_cast<TableRowId>(sqlite3_value_int64(argv));
305     switch (op) {
306         case SQLITE_INDEX_CONSTRAINT_EQ:
307             indexMap_->Intersect(v, v + 1);
308             break;
309         case SQLITE_INDEX_CONSTRAINT_GE:
310             indexMap_->Intersect(v, rowCount_);
311             break;
312         case SQLITE_INDEX_CONSTRAINT_GT:
313             v++;
314             indexMap_->Intersect(v, rowCount_);
315             break;
316         case SQLITE_INDEX_CONSTRAINT_LE:
317             v++;
318             indexMap_->Intersect(0, v);
319             break;
320         case SQLITE_INDEX_CONSTRAINT_LT:
321             indexMap_->Intersect(0, v);
322             break;
323         default:
324             // can't filter, all rows
325             break;
326     }
327 }
328 
EstimateFilterCost(FilterConstraints & fc,EstimatedIndexInfo & ei)329 void TableBase::EstimateFilterCost(FilterConstraints& fc, EstimatedIndexInfo& ei)
330 {
331     constexpr double filterBaseCost = 1000.0; // set-up and tear-down
332     constexpr double indexCost = 2.0;
333     ei.estimatedCost = filterBaseCost;
334     auto rowCount = GetSize();
335     if (rowCount > -1) {
336         if (rowCount == 0 || rowCount == 1) {
337             ei.estimatedRows = rowCount;
338             ei.estimatedCost += indexCost * rowCount;
339             return;
340         }
341         double filterCost = 0.0;
342         auto constraints = fc.GetConstraints();
343         if (constraints.empty()) { // scan all rows
344             filterCost = rowCount;
345         } else {
346             for (int32_t i = 0; i < static_cast<int32_t>(constraints.size()); i++) {
347                 if (rowCount <= 1) {
348                     // only one row or nothing, needn't filter by constraint
349                     filterCost += rowCount;
350                     break;
351                 }
352                 FilterByConstraint(fc, filterCost, rowCount, i);
353             }
354         }
355 
356         ei.estimatedCost += filterCost;
357         ei.estimatedRows = rowCount;
358         ei.estimatedCost += rowCount * indexCost;
359         ei.isOrdered = true;
360 
361         GetOrbyes(fc, ei);
362     } else {
363         return;
364     }
365 }
366 
367 } // namespace TraceStreamer
368 } // namespace SysTuning
369