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