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