• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "demo_trace_data_db.h"
17 #include <chrono>
18 #include <cmath>
19 #include <cstdio>
20 #include <cstring>
21 #include <fcntl.h>
22 #include <functional>
23 #include <iostream>
24 #include <string_view>
25 #include <unistd.h>
26 
27 #include "file.h"
28 #include "log.h"
29 #include "sqlite3.h"
30 #include "sqlite_ext/sqlite_ext_funcs.h"
31 #include "string_help.h"
32 
33 const int32_t ONCE_MAX_MB = 1024 * 1024 * 4;
34 namespace SysTuning {
35 namespace TraceStreamer {
36 #define UNUSED(expr)             \
37     do {                         \
38         static_cast<void>(expr); \
39     } while (0)
40 using namespace SysTuning::base;
DemoTraceDataDB()41 DemoTraceDataDB::DemoTraceDataDB() : demoDb_(nullptr)
42 {
43     if (sqlite3_threadsafe() > 0) {
44         int32_t ret = sqlite3_config(SQLITE_CONFIG_SERIALIZED);
45         if (ret == SQLITE_OK) {
46             TS_LOGI("Can now use sqlite on multiple threads, using the same connection");
47         } else {
48             TS_LOGE("setting sqlite thread safe mode to serialized failed!!! return code: %d", ret);
49         }
50     } else {
51         TS_LOGE("Your SQLite database is not compiled to be threadsafe.");
52     }
53     if (sqlite3_open(":memory:", &demoDb_)) {
54         TS_LOGF("open :memory db failed");
55     }
56     ts_create_extend_function(demoDb_);
57 }
58 
~DemoTraceDataDB()59 DemoTraceDataDB::~DemoTraceDataDB()
60 {
61     sqlite3_close(demoDb_);
62 }
63 
DemoSetCancel(bool cancel)64 void DemoTraceDataDB::DemoSetCancel(bool cancel)
65 {
66     demoCancelQuery_ = cancel;
67 }
68 
DemoAppendNewTable(std::string tableName)69 void DemoTraceDataDB::DemoAppendNewTable(std::string tableName)
70 {
71     demoInternalTables_.push_back(tableName);
72 }
DemoEnableMetaTable(bool enabled)73 void DemoTraceDataDB::DemoEnableMetaTable(bool enabled)
74 {
75     demoExportMetaTable_ = enabled;
76 }
DemoExportDatabase(const std::string & outputName)77 int32_t DemoTraceDataDB::DemoExportDatabase(const std::string &outputName)
78 {
79     {
80         int32_t demoFd(base::OpenFile(outputName, O_CREAT | O_RDWR, TS_PERMISSION_RW));
81         if (!demoFd) {
82             fprintf(stdout, "Failed to create file: %s", outputName.c_str());
83             return 1;
84         }
85         auto ret = ftruncate(demoFd, 0);
86         UNUSED(ret);
87         close(demoFd);
88     }
89 
90     std::string demoAttachSql("ATTACH DATABASE '" + outputName + "' AS systuning_export");
91 #ifdef _WIN32
92     if (!base::GetCoding(reinterpret_cast<const uint8_t *>(demoAttachSql.c_str()), demoAttachSql.length())) {
93         demoAttachSql = base::GbkToUtf8(demoAttachSql.c_str());
94     }
95 #endif
96     DemoExecuteSql(demoAttachSql);
97 
98     for (auto itor = demoInternalTables_.begin(); itor != demoInternalTables_.end(); itor++) {
99         if (*itor == "meta" && !demoExportMetaTable_) {
100             continue;
101         } else {
102             std::string demoExportSql("CREATE TABLE systuning_export." + (*itor) + " AS SELECT * FROM " + *itor);
103             DemoExecuteSql(demoExportSql);
104         }
105     }
106     std::string demoCreateArgsView =
107         "create view systuning_export.args_view AS select A.argset, V2.data as keyName, A.id, D.desc, (case when "
108         "A.datatype==1 then V.data else A.value end) as strValue from args as A left join data_type as D on (D.typeId "
109         "= A.datatype) left join data_dict as V on V.id = A.value left join data_dict as V2 on V2.id = A.key";
110     DemoExecuteSql(demoCreateArgsView);
111     std::string demoUpdateProcessName =
112         "update process set name =  (select name from thread t where t.ipid = process.id and t.name is not null and "
113         "is_main_thread = 1)";
114     DemoExecuteSql(demoUpdateProcessName);
115     std::string demoDetachSql("DETACH DATABASE systuning_export");
116     DemoExecuteSql(demoDetachSql);
117     return 0;
118 }
DemoPrepare()119 void DemoTraceDataDB::DemoPrepare()
120 {
121     if (demoPared_) {
122         return;
123     }
124     demoPared_ = true;
125     std::string demoCreateArgsView =
126         "create view args_view AS select A.argset, V2.data as keyName, A.id, D.desc, (case when "
127         "A.datatype==1 then V.data else A.value end) as strValue from args as A left join data_type as D on "
128         "(D.typeId "
129         "= A.datatype) left join data_dict as V on V.id = A.value left join data_dict as V2 on V2.id = A.key";
130     DemoExecuteSql(demoCreateArgsView);
131 
132     std::string demoUpdateProcessNewName =
133         "update process set name =  (select name from thread t where t.ipid = process.id and t.name is not "
134         "null and "
135         "is_main_thread = 1)";
136     DemoExecuteSql(demoUpdateProcessNewName);
137 }
DemoExecuteSql(const std::string_view & sql)138 void DemoTraceDataDB::DemoExecuteSql(const std::string_view &sql)
139 {
140     sqlite3_stmt *demoStmt = nullptr;
141     int32_t ret = sqlite3_prepare_v2(demoDb_, sql.data(), static_cast<int32_t>(sql.size()), &demoStmt, nullptr);
142 
143     while (!ret) {
144         int32_t err = sqlite3_step(demoStmt);
145         if (err == SQLITE_ROW) {
146             continue;
147         }
148         if (err == SQLITE_DONE) {
149             break;
150         }
151         ret = err;
152     }
153 
154     sqlite3_finalize(demoStmt);
155 }
DemoSearchData()156 int32_t DemoTraceDataDB::DemoSearchData()
157 {
158     DemoPrepare();
159     std::string strLine;
160     bool printResult = false;
161     for (;;) {
162         std::cout << "> ";
163         getline(std::cin, strLine);
164         if (strLine.empty()) {
165             std::cout << "If you want to quit either type -q or press CTRL-Z" << std::endl;
166             continue;
167         }
168         if (!strLine.compare("-q") || !strLine.compare("-quit")) {
169             break;
170         } else if (!strLine.compare("-e")) {
171             TS_LOGI("the db file will be at current folder, the name is default.db");
172             return DemoExportDatabase("default.db");
173         } else if (!strLine.compare("-help") || !strLine.compare("-h")) {
174             std::cout << "use info" << std::endl;
175             continue;
176         } else if (!strLine.compare("-p")) {
177             std::cout << "will print result of query" << std::endl;
178             printResult = true;
179             continue;
180         } else if (!strLine.compare("-up")) {
181             std::cout << "will not print result of query" << std::endl;
182             printResult = false;
183             continue;
184         } else if (strLine.find("-c:") != std::string::npos) {
185             strLine = strLine.substr(strlen("-c:"));
186             if (DemoOperateDatabase(strLine) == SQLITE_OK) {
187                 printf("operate SQL success\n");
188             }
189             continue;
190         }
191 
192         using namespace std::chrono;
193         const auto start = steady_clock::now();
194         int32_t rowCount = DemoSearchDatabase(strLine, printResult);
195         std::chrono::nanoseconds searchDur = duration_cast<nanoseconds>(steady_clock::now() - start);
196         printf("\"%s\"\n\tused %.3fms row: %d\n", strLine.c_str(), searchDur.count() / 1E6, rowCount);
197     }
198     return 0;
199 }
DemoSearchDatabase(const std::string & sql,bool print)200 int32_t DemoTraceDataDB::DemoSearchDatabase(const std::string &sql, bool print)
201 {
202     DemoPrepare();
203     int32_t demoRowCount = 0;
204     sqlite3_stmt *demoStmt = nullptr;
205     int32_t ret = sqlite3_prepare_v2(demoDb_, sql.c_str(), static_cast<int32_t>(sql.size()), &demoStmt, nullptr);
206     if (ret != SQLITE_OK) {
207         TS_LOGE("sqlite3_prepare_v2(%s) failed: %d:%s", sql.c_str(), ret, sqlite3_errmsg(demoDb_));
208         return 0;
209     }
210     int32_t demoColCount = sqlite3_column_count(demoStmt);
211     if (demoColCount == 0) {
212         TS_LOGI("sqlite3_column_count(%s) no column", sql.c_str());
213         sqlite3_finalize(demoStmt);
214         return 0;
215     }
216     if (print) {
217         for (int32_t i = 0; i < demoColCount; i++) {
218             printf("%s\t", sqlite3_column_name(demoStmt, i));
219         }
220         printf("\n");
221     }
222     while (sqlite3_step(demoStmt) == SQLITE_ROW) {
223         demoRowCount++;
224         for (int32_t i = 0; i < demoColCount; i++) {
225             const char *sqlVal = reinterpret_cast<const char *>(sqlite3_column_text(demoStmt, i));
226             int32_t type = sqlite3_column_type(demoStmt, i);
227             if (!print) {
228                 continue;
229             }
230             if (sqlVal == nullptr) {
231                 printf("null\t");
232                 continue;
233             }
234             if (type == SQLITE_TEXT) {
235                 printf("\"%s\"\t", sqlVal);
236             } else {
237                 printf("%s\t", sqlVal);
238             }
239         }
240         if (print) {
241             printf("\n");
242         }
243     }
244     sqlite3_finalize(demoStmt);
245     return demoRowCount;
246 }
DemoOperateDatabase(const std::string & sql)247 int32_t DemoTraceDataDB::DemoOperateDatabase(const std::string &sql)
248 {
249     DemoPrepare();
250     char *errMsg = nullptr;
251     int32_t ret = sqlite3_exec(demoDb_, sql.c_str(), NULL, NULL, &errMsg);
252     if (ret != SQLITE_OK && errMsg) {
253         TS_LOGE("sqlite3_exec(%s) failed: %d:%s", sql.c_str(), ret, errMsg);
254         sqlite3_free(errMsg);
255     }
256     return ret;
257 }
258 
DemoSearchDatabase(const std::string & sql,ResultCallBack resultCallBack)259 int32_t DemoTraceDataDB::DemoSearchDatabase(const std::string &sql, ResultCallBack resultCallBack)
260 {
261     DemoPrepare();
262     sqlite3_stmt *stmt = nullptr;
263     int32_t ret = sqlite3_prepare_v2(demoDb_, sql.c_str(), static_cast<int32_t>(sql.size()), &stmt, nullptr);
264     if (ret != SQLITE_OK) {
265         resultCallBack("false\r\n", SEND_FINISH, 0);
266         TS_LOGE("sqlite3_prepare_v2(%s) failed: %d:%s", sql.c_str(), ret, sqlite3_errmsg(demoDb_));
267         return ret;
268     }
269     if (!resultCallBack) {
270         return ret;
271     }
272     std::string resValue = "ok\r\n";
273     int32_t colCount = sqlite3_column_count(stmt);
274     if (colCount == 0) {
275         resultCallBack(resValue, SEND_FINISH, 0);
276         return ret;
277     }
278     resValue += "{\"columns\":[";
279     for (int32_t i = 0; i < colCount; i++) {
280         resValue += "\"";
281         resValue += sqlite3_column_name(stmt, i);
282         resValue += "\",";
283     }
284     resValue.pop_back(); // remove the last ","
285     resValue += "],\"values\":[";
286     bool hasRow = false;
287     constexpr int32_t defaultLenRowString = 1024;
288     std::string row;
289     row.reserve(defaultLenRowString);
290     while (sqlite3_step(stmt) == SQLITE_ROW) {
291         hasRow = true;
292         DemoGetRowString(stmt, colCount, row);
293         resValue += row + ",";
294         if (resValue.size() >= ONCE_MAX_MB) {
295             resultCallBack(resValue, SEND_CONTINUE, 0);
296             resValue = "";
297         }
298     }
299     if (hasRow) {
300         resValue.pop_back(); // remove the last ','
301     }
302     resValue += "]}\r\n";
303     resultCallBack(resValue, SEND_FINISH, 0);
304     sqlite3_finalize(stmt);
305     return ret;
306 }
307 
AddColumnsToJsonArray(sqlite3_stmt * stmtSql,char * resValue,const int32_t outLen,int32_t & pos,const int32_t colCount)308 bool DemoTraceDataDB::AddColumnsToJsonArray(sqlite3_stmt *stmtSql,
309                                             char *resValue,
310                                             const int32_t outLen,
311                                             int32_t &pos,
312                                             const int32_t colCount)
313 {
314     int32_t retSnprintf = snprintf_s(resValue + pos, outLen - pos, 1, "%s", "{\"columns\":[");
315     if (retSnprintf < 0) {
316         return false;
317     }
318     pos += retSnprintf;
319     for (int32_t i = 0; i < colCount; i++) {
320         retSnprintf =
321             snprintf_s(resValue + pos, outLen - pos, 1, "%s%s%s", "\"", sqlite3_column_name(stmtSql, i), "\",");
322         if (retSnprintf < 0) {
323             return false;
324         }
325         pos += retSnprintf;
326     }
327     pos--; // remove the last ','
328     retSnprintf = snprintf_s(resValue + pos, outLen - pos, 1, "],\"values\":[");
329     if (retSnprintf < 0) {
330         return false;
331     }
332     pos += retSnprintf;
333     return true;
334 }
AddRowsToJsonArray(sqlite3_stmt * stmtSql,char * resValue,const int32_t outLen,int32_t & pos,const int32_t colCount)335 bool DemoTraceDataDB::AddRowsToJsonArray(sqlite3_stmt *stmtSql,
336                                          char *resValue,
337                                          const int32_t outLen,
338                                          int32_t &pos,
339                                          const int32_t colCount)
340 {
341     bool hasRow = false;
342     constexpr int32_t defaultLenRowString = 1024;
343     std::string row;
344     row.reserve(defaultLenRowString);
345     while (sqlite3_step(stmtSql) == SQLITE_ROW) {
346         hasRow = true;
347         DemoGetRowString(stmtSql, colCount, row);
348         if (pos + row.size() + strlen(",]}\r\n") >= size_t(outLen)) {
349             int32_t retSnprintf = snprintf_s(resValue + pos, outLen - pos, 1, "]}\r\n");
350             if (retSnprintf < 0) {
351                 return false;
352             }
353             pos += retSnprintf;
354             return true;
355         }
356         int32_t retSnprintf = snprintf_s(resValue + pos, outLen - pos, 1, "%s%s", row.c_str(), ",");
357         if (retSnprintf < 0) {
358             return false;
359         }
360         pos += retSnprintf;
361     }
362     if (hasRow) {
363         pos--; // remove the last ','
364     }
365     int32_t retSnprintf = snprintf_s(resValue + pos, outLen - pos, 1, "]}\r\n");
366     if (retSnprintf < 0) {
367         return false;
368     }
369     pos += retSnprintf;
370     return true;
371 }
DemoSearchDatabase(const std::string & sql,uint8_t * out,int32_t outLen)372 int32_t DemoTraceDataDB::DemoSearchDatabase(const std::string &sql, uint8_t *out, int32_t outLen)
373 {
374     DemoPrepare();
375     sqlite3_stmt *stmtSql = nullptr;
376     int32_t ret = sqlite3_prepare_v2(demoDb_, sql.c_str(), static_cast<int32_t>(sql.size()), &stmtSql, nullptr);
377     if (ret != SQLITE_OK) {
378         TS_LOGE("sqlite3_prepare_v2(%s) failed: %d:%s", sql.c_str(), ret, sqlite3_errmsg(demoDb_));
379         return -1;
380     }
381     char *resValue = reinterpret_cast<char *>(out);
382     int32_t retSnprintf = snprintf_s(resValue, outLen, 1, "ok\r\n");
383     if (retSnprintf < 0) {
384         return -1;
385     }
386     int32_t pos = retSnprintf;
387     int32_t colCount = sqlite3_column_count(stmtSql);
388     if (colCount == 0) {
389         return pos;
390     }
391     if (!AddColumnsToJsonArray(stmtSql, resValue, outLen, pos, colCount)) {
392         return -1;
393     }
394     if (!AddRowsToJsonArray(stmtSql, resValue, outLen, pos, colCount)) {
395         return -1;
396     }
397     sqlite3_finalize(stmtSql);
398     return pos;
399 }
400 
DemoGetRowString(sqlite3_stmt * stmt,int32_t colCount,std::string & rowString)401 void DemoTraceDataDB::DemoGetRowString(sqlite3_stmt *stmt, int32_t colCount, std::string &rowString)
402 {
403     rowString.clear();
404     rowString = "[";
405     for (int32_t i = 0; i < colCount; i++) {
406         const char *p = reinterpret_cast<const char *>(sqlite3_column_text(stmt, i));
407         if (p == nullptr) {
408             rowString += "null,";
409             continue;
410         }
411         int32_t type = sqlite3_column_type(stmt, i);
412         switch (type) {
413             case SQLITE_TEXT:
414                 rowString += "\"";
415                 rowString += p;
416                 rowString += "\"";
417                 break;
418             default:
419                 rowString += p;
420                 break;
421         }
422         rowString += ",";
423     }
424     rowString.pop_back(); // remove the last ','
425     rowString += "]";
426 }
427 } // namespace TraceStreamer
428 } // namespace SysTuning
429