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