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