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