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