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