1 /*
2 * Copyright (C) 2025 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_dump_executor.h"
17
18 #include <algorithm>
19 #include <securec.h>
20 #include <sys/prctl.h>
21 #include <sys/stat.h>
22 #include <thread>
23 #include <unistd.h>
24
25 #include "common_define.h"
26 #include "common_utils.h"
27 #include "file_ageing_utils.h"
28 #include "hilog/log.h"
29 #include "trace_file_utils.h"
30 #include "trace_json_parser.h"
31
32 namespace OHOS {
33 namespace HiviewDFX {
34 namespace Hitrace {
35 #ifdef LOG_DOMAIN
36 #undef LOG_DOMAIN
37 #define LOG_DOMAIN 0xD002D33
38 #endif
39 #ifdef LOG_TAG
40 #undef LOG_TAG
41 #define LOG_TAG "HitraceDumpExecutor"
42 #endif
43 namespace {
44 constexpr int BYTE_PER_KB = 1024;
45 constexpr int MAX_NEW_TRACE_FILE_LIMIT = 5;
46 #ifdef HITRACE_UNITTEST
47 constexpr int DEFAULT_CACHE_FILE_SIZE = 15 * 1024;
48 #else
49 constexpr int DEFAULT_CACHE_FILE_SIZE = 150 * 1024;
50 #endif
51 constexpr uint64_t SYNC_RETURN_TIMEOUT_NS = 5000000000; // 5s
52 constexpr int64_t ASYNC_DUMP_FILE_SIZE_ADDITION = 1024 * 1024; // 1MB
53
54 static bool g_isRootVer = IsRootVersion();
55
56 uint64_t g_sliceMaxDuration = 30; // 30 : 30 seconds as default cache trace slice duration
57
58 // use for control status of trace dump loop.
59 std::atomic<bool> g_isDumpRunning(false);
60 std::atomic<bool> g_isDumpEnded(true);
61 std::atomic<bool> g_interruptCache(false);
62 std::atomic<bool> g_readFlag(true);
63 std::atomic<bool> g_writeFlag(true);
64
FilterLoopTraceResult(const std::vector<TraceFileInfo> & looptraceFiles)65 std::vector<std::string> FilterLoopTraceResult(const std::vector<TraceFileInfo>& looptraceFiles)
66 {
67 std::vector<std::string> outputFiles = {};
68 for (const auto& output : looptraceFiles) {
69 if (output.isNewFile) {
70 outputFiles.emplace_back(output.filename);
71 }
72 }
73 return outputFiles;
74 }
75
IsGenerateNewFile(std::shared_ptr<ITraceSource> traceSource,const TraceDumpType traceType,int & count)76 bool IsGenerateNewFile(std::shared_ptr<ITraceSource> traceSource, const TraceDumpType traceType, int& count)
77 {
78 if (access(traceSource->GetTraceFilePath().c_str(), F_OK) == 0) {
79 return false;
80 }
81 if (count > MAX_NEW_TRACE_FILE_LIMIT) {
82 HILOG_ERROR(LOG_CORE, "create new trace file limited.");
83 return false;
84 }
85 count++;
86 auto newFileName = GenerateTraceFileName(traceType);
87 traceSource->UpdateTraceFile(newFileName);
88 HILOG_INFO(LOG_CORE, "IsGenerateNewFile: update tracesource filename : %{public}s", newFileName.c_str());
89 return true;
90 }
91
PreTraceContentDump(const TraceContentPtr & traceContentPtr)92 void PreTraceContentDump(const TraceContentPtr& traceContentPtr)
93 {
94 traceContentPtr.fileHdr->ResetCurrentFileSize();
95 if (!traceContentPtr.fileHdr->WriteTraceContent()) {
96 HILOG_INFO(LOG_CORE, "fileHdr WriteTraceContent failed.");
97 }
98 if (!traceContentPtr.baseInfo->WriteTraceContent()) {
99 HILOG_INFO(LOG_CORE, "baseInfo WriteTraceContent failed.");
100 }
101 if (!traceContentPtr.eventFmt->WriteTraceContent()) {
102 HILOG_INFO(LOG_CORE, "fileHdr WriteTraceContent failed.");
103 }
104 }
105
AfterTraceContentDump(const TraceContentPtr & traceContentPtr)106 void AfterTraceContentDump(const TraceContentPtr& traceContentPtr)
107 {
108 if (!traceContentPtr.cmdLines->WriteTraceContent()) {
109 HILOG_INFO(LOG_CORE, "cmdLines WriteTraceContent failed.");
110 }
111 if (!traceContentPtr.tgids->WriteTraceContent()) {
112 HILOG_INFO(LOG_CORE, "tgids WriteTraceContent failed.");
113 }
114 if (!traceContentPtr.headerPage->WriteTraceContent()) {
115 HILOG_INFO(LOG_CORE, "headerPage WriteTraceContent failed.");
116 }
117 if (!traceContentPtr.printkFmt->WriteTraceContent()) {
118 HILOG_INFO(LOG_CORE, "printkFmt WriteTraceContent failed.");
119 }
120 }
121 }
122
CreateTraceContentPtr(std::shared_ptr<ITraceSource> traceSource,const TraceDumpRequest & request,TraceContentPtr & contentPtr)123 bool ITraceDumpStrategy::CreateTraceContentPtr(std::shared_ptr<ITraceSource> traceSource,
124 const TraceDumpRequest& request, TraceContentPtr& contentPtr)
125 {
126 contentPtr.fileHdr = traceSource->GetTraceFileHeader();
127 if (contentPtr.fileHdr == nullptr) {
128 HILOG_ERROR(LOG_CORE, "CreateTraceContentPtr: GetTraceFileHeader failed.");
129 return false;
130 }
131 contentPtr.baseInfo = traceSource->GetTraceBaseInfo();
132 if (contentPtr.baseInfo == nullptr) {
133 HILOG_ERROR(LOG_CORE, "CreateTraceContentPtr: GetTraceBaseInfo failed.");
134 return false;
135 }
136 contentPtr.eventFmt = traceSource->GetTraceEventFmt();
137 if (contentPtr.eventFmt == nullptr) {
138 HILOG_ERROR(LOG_CORE, "CreateTraceContentPtr: GetTraceEventFmt failed.");
139 return false;
140 }
141 contentPtr.cpuRaw = traceSource->GetTraceCpuRaw(request);
142 if (contentPtr.cpuRaw == nullptr) {
143 HILOG_ERROR(LOG_CORE, "CreateTraceContentPtr: GetTraceCpuRaw failed.");
144 return false;
145 }
146 contentPtr.cmdLines = traceSource->GetTraceCmdLines();
147 if (contentPtr.cmdLines == nullptr) {
148 HILOG_ERROR(LOG_CORE, "CreateTraceContentPtr: GetTraceCmdLines failed.");
149 return false;
150 }
151 contentPtr.tgids = traceSource->GetTraceTgids();
152 if (contentPtr.tgids == nullptr) {
153 HILOG_ERROR(LOG_CORE, "CreateTraceContentPtr: GetTraceTgids failed.");
154 return false;
155 }
156 contentPtr.headerPage = traceSource->GetTraceHeaderPage();
157 if (contentPtr.headerPage == nullptr) {
158 HILOG_ERROR(LOG_CORE, "CreateTraceContentPtr: GetTraceHeaderPage failed.");
159 return false;
160 }
161 contentPtr.printkFmt = traceSource->GetTracePrintkFmt();
162 if (contentPtr.printkFmt == nullptr) {
163 HILOG_ERROR(LOG_CORE, "CreateTraceContentPtr: GetTracePrintkFmt failed.");
164 return false;
165 }
166 return true;
167 }
168
Execute(std::shared_ptr<ITraceSource> traceSource,const TraceDumpRequest & request)169 TraceDumpRet SnapshotTraceDumpStrategy::Execute(std::shared_ptr<ITraceSource> traceSource,
170 const TraceDumpRequest& request)
171 {
172 struct TraceContentPtr traceContentPtr;
173 if (!CreateTraceContentPtr(traceSource, request, traceContentPtr)) {
174 HILOG_ERROR(LOG_CORE, "SnapshotTraceDumpStrategy: CreateTraceContentPtr failed.");
175 return {TraceErrorCode::WRITE_TRACE_INFO_ERROR, "", 0, 0};
176 }
177 PreTraceContentDump(traceContentPtr);
178 if (!traceContentPtr.cpuRaw->WriteTraceContent()) {
179 return {traceContentPtr.cpuRaw->GetDumpStatus(), "", 0, 0};
180 }
181 AfterTraceContentDump(traceContentPtr);
182
183 auto traceFile = traceContentPtr.cpuRaw->GetTraceFilePath();
184 TraceDumpRet ret = {
185 .code = traceContentPtr.cpuRaw->GetDumpStatus(),
186 .traceStartTime = traceContentPtr.cpuRaw->GetFirstPageTimeStamp(),
187 .traceEndTime = traceContentPtr.cpuRaw->GetLastPageTimeStamp(),
188 };
189 if (strncpy_s(ret.outputFile, TRACE_FILE_LEN, traceFile.c_str(), TRACE_FILE_LEN - 1) != 0) {
190 HILOG_ERROR(LOG_CORE, "SnapshotTraceDumpStrategy: strncpy_s failed.");
191 }
192
193 return ret;
194 }
195
ProcessTraceContent(std::shared_ptr<ITraceSource> traceSource,const TraceDumpRequest & request,TraceContentPtr & traceContentPtr,TraceProcessResult & result)196 bool RecordTraceDumpStrategy::ProcessTraceContent(std::shared_ptr<ITraceSource> traceSource,
197 const TraceDumpRequest& request, TraceContentPtr& traceContentPtr, TraceProcessResult& result)
198 {
199 while (g_isDumpRunning.load()) {
200 sleep(1);
201 auto updatedRequest = request;
202 updatedRequest.traceEndTime = GetCurBootTime();
203 traceContentPtr.cpuRaw = traceSource->GetTraceCpuRaw(updatedRequest);
204 if (!traceContentPtr.cpuRaw->WriteTraceContent()) {
205 return false;
206 }
207 if (traceContentPtr.cpuRaw->IsOverFlow()) {
208 HILOG_INFO(LOG_CORE, "RecordTraceDumpStrategy: write trace content overflow.");
209 break;
210 }
211 result.code = traceContentPtr.cpuRaw->GetDumpStatus();
212 result.traceFile = traceContentPtr.cpuRaw->GetTraceFilePath();
213 result.traceStartTime = traceContentPtr.cpuRaw->GetFirstPageTimeStamp();
214 result.traceEndTime = traceContentPtr.cpuRaw->GetLastPageTimeStamp();
215 }
216 return true;
217 }
218
Execute(std::shared_ptr<ITraceSource> traceSource,const TraceDumpRequest & request)219 TraceDumpRet RecordTraceDumpStrategy::Execute(std::shared_ptr<ITraceSource> traceSource,
220 const TraceDumpRequest& request)
221 {
222 int newFileCount = 1;
223 TraceProcessResult result = {};
224 do {
225 struct TraceContentPtr traceContentPtr;
226 if (!CreateTraceContentPtr(traceSource, request, traceContentPtr)) {
227 HILOG_ERROR(LOG_CORE, "RecordTraceDumpStrategy: CreateTraceContentPtr failed.");
228 return {TraceErrorCode::WRITE_TRACE_INFO_ERROR, "", 0, 0};
229 }
230 PreTraceContentDump(traceContentPtr);
231 if (!ProcessTraceContent(traceSource, request, traceContentPtr, result)) {
232 if (!IsGenerateNewFile(traceSource, TraceDumpType::TRACE_RECORDING, newFileCount)) {
233 HILOG_ERROR(LOG_CORE, "RecordTraceDumpStrategy: write raw trace content failed.");
234 break;
235 }
236 continue;
237 }
238 AfterTraceContentDump(traceContentPtr);
239 } while (IsGenerateNewFile(traceSource, TraceDumpType::TRACE_RECORDING, newFileCount));
240
241 TraceDumpRet ret = {
242 .code = result.code,
243 .traceStartTime = result.traceStartTime,
244 .traceEndTime = result.traceEndTime,
245 };
246 if (strncpy_s(ret.outputFile, TRACE_FILE_LEN, result.traceFile.c_str(), TRACE_FILE_LEN - 1) != 0) {
247 HILOG_ERROR(LOG_CORE, "RecordTraceDumpStrategy: strncpy_s failed.");
248 }
249 return ret;
250 }
251
ProcessTraceContent(std::shared_ptr<ITraceSource> traceSource,const TraceDumpRequest & request,TraceContentPtr & traceContentPtr,TraceProcessResult & result,uint64_t & sliceDuration)252 bool CacheTraceDumpStrategy::ProcessTraceContent(std::shared_ptr<ITraceSource> traceSource,
253 const TraceDumpRequest& request, TraceContentPtr& traceContentPtr, TraceProcessResult& result,
254 uint64_t& sliceDuration)
255 {
256 while (g_isDumpRunning.load()) {
257 struct timespec bts = {0, 0};
258 clock_gettime(CLOCK_BOOTTIME, &bts);
259 uint64_t startTime = static_cast<uint64_t>(bts.tv_sec * S_TO_NS + bts.tv_nsec);
260 sleep(1);
261 if (!traceContentPtr.cpuRaw->WriteTraceContent()) {
262 return false;
263 }
264 clock_gettime(CLOCK_BOOTTIME, &bts);
265 uint64_t endTime = static_cast<uint64_t>(bts.tv_sec * S_TO_NS + bts.tv_nsec);
266 uint64_t timeDiff = (endTime - startTime) / S_TO_NS;
267 result.code = traceContentPtr.cpuRaw->GetDumpStatus();
268 result.traceFile = traceContentPtr.cpuRaw->GetTraceFilePath();
269 result.traceStartTime = traceContentPtr.cpuRaw->GetFirstPageTimeStamp();
270 result.traceEndTime = traceContentPtr.cpuRaw->GetLastPageTimeStamp();
271 sliceDuration += timeDiff;
272 if (sliceDuration >= g_sliceMaxDuration || g_interruptCache.load()) {
273 sliceDuration = 0;
274 break;
275 }
276 }
277 return true;
278 }
279
Execute(std::shared_ptr<ITraceSource> traceSource,const TraceDumpRequest & request)280 TraceDumpRet CacheTraceDumpStrategy::Execute(std::shared_ptr<ITraceSource> traceSource,
281 const TraceDumpRequest& request)
282 {
283 int newFileCount = 1;
284 TraceProcessResult result = {};
285 uint64_t sliceDuration = 0;
286 do {
287 struct TraceContentPtr traceContentPtr;
288 if (!CreateTraceContentPtr(traceSource, request, traceContentPtr)) {
289 return {TraceErrorCode::WRITE_TRACE_INFO_ERROR, "", 0, 0};
290 }
291 PreTraceContentDump(traceContentPtr);
292 if (!ProcessTraceContent(traceSource, request, traceContentPtr, result, sliceDuration)) {
293 if (!IsGenerateNewFile(traceSource, TraceDumpType::TRACE_CACHE, newFileCount)) {
294 HILOG_ERROR(LOG_CORE, "CacheTraceDumpStrategy: write raw trace content failed.");
295 break;
296 }
297 continue;
298 }
299 AfterTraceContentDump(traceContentPtr);
300 } while (IsGenerateNewFile(traceSource, TraceDumpType::TRACE_CACHE, newFileCount));
301
302 TraceDumpRet ret = {
303 .code = result.code,
304 .traceStartTime = result.traceStartTime,
305 .traceEndTime = result.traceEndTime,
306 };
307 if (strncpy_s(ret.outputFile, TRACE_FILE_LEN, result.traceFile.c_str(), TRACE_FILE_LEN - 1) != 0) {
308 HILOG_ERROR(LOG_CORE, "CacheTraceDumpStrategy: strncpy_s failed.");
309 }
310 return ret;
311 }
312
Execute(std::shared_ptr<ITraceSource> traceSource,const TraceDumpRequest & request)313 TraceDumpRet AsyncTraceReadStrategy::Execute(std::shared_ptr<ITraceSource> traceSource, const TraceDumpRequest& request)
314 {
315 auto cpuRawRead = traceSource->GetTraceCpuRawRead(request);
316 if (!cpuRawRead->WriteTraceContent()) {
317 return {cpuRawRead->GetDumpStatus(), "", 0, 0};
318 }
319
320 TraceDumpRet ret = {
321 .code = cpuRawRead->GetDumpStatus(),
322 .fileSize = static_cast<int64_t>(TraceBufferManager::GetInstance().GetTaskTotalUsedBytes(request.taskId)),
323 .traceStartTime = cpuRawRead->GetFirstPageTimeStamp(),
324 .traceEndTime = cpuRawRead->GetLastPageTimeStamp(),
325 };
326 auto tracefile = GenerateTraceFileNameByTraceTime(request.type, ret.traceStartTime, ret.traceEndTime);
327 if (strncpy_s(ret.outputFile, TRACE_FILE_LEN, tracefile.c_str(), TRACE_FILE_LEN - 1) != 0) {
328 HILOG_ERROR(LOG_CORE, "AsyncTraceReadStrategy: strncpy_s failed.");
329 }
330 HILOG_INFO(LOG_CORE, "AsyncTraceReadStrategy: trace file : %{public}s, file size : %{public}" PRId64,
331 ret.outputFile, ret.fileSize);
332 return ret;
333 }
334
Execute(std::shared_ptr<ITraceSource> traceSource,const TraceDumpRequest & request)335 TraceDumpRet AsyncTraceWriteStrategy::Execute(std::shared_ptr<ITraceSource> traceSource,
336 const TraceDumpRequest& request)
337 {
338 struct TraceContentPtr traceContentPtr;
339 if (!CreateTraceContentPtr(traceSource, request, traceContentPtr)) {
340 HILOG_ERROR(LOG_CORE, "AsyncTraceWriteStrategy: CreateTraceContentPtr failed.");
341 return {TraceErrorCode::WRITE_TRACE_INFO_ERROR, "", 0, 0};
342 }
343 PreTraceContentDump(traceContentPtr);
344 auto cpuRawWrite = traceSource->GetTraceCpuRawWrite(request.taskId);
345 if (!cpuRawWrite->WriteTraceContent()) {
346 return {TraceErrorCode::FILE_ERROR, "", 0, 0};
347 }
348 AfterTraceContentDump(traceContentPtr);
349 TraceDumpRet ret = {
350 .code = TraceErrorCode::SUCCESS
351 };
352 return ret;
353 }
354
TraceDumpExecutor()355 TraceDumpExecutor::TraceDumpExecutor()
356 {
357 if (!IsTraceMounted(tracefsDir_)) {
358 HILOG_ERROR(LOG_CORE, "TraceDumpExecutor: Trace is not mounted.");
359 }
360 }
361
PreCheckDumpTraceLoopStatus()362 bool TraceDumpExecutor::PreCheckDumpTraceLoopStatus()
363 {
364 if (!g_isDumpEnded.load()) {
365 return false;
366 }
367 g_isDumpRunning.store(true);
368 g_isDumpEnded.store(false);
369 return true;
370 }
371
StartDumpTraceLoop(const TraceDumpParam & param)372 bool TraceDumpExecutor::StartDumpTraceLoop(const TraceDumpParam& param)
373 {
374 {
375 std::lock_guard<std::mutex> lck(traceFileMutex_);
376 loopTraceFiles_.clear();
377 GetTraceFilesInDir(loopTraceFiles_, TraceDumpType::TRACE_RECORDING);
378 FileAgeingUtils::HandleAgeing(loopTraceFiles_, TraceDumpType::TRACE_RECORDING);
379 }
380
381 if (param.fileSize == 0 && g_isRootVer) {
382 std::string traceFile = param.outputFile.empty() ? GenerateTraceFileName(param.type) : param.outputFile;
383 if (DoDumpTraceLoop(param, traceFile, false)) {
384 std::lock_guard<std::mutex> lck(traceFileMutex_);
385 loopTraceFiles_.emplace_back(traceFile);
386 }
387 g_isDumpEnded.store(true);
388 g_isDumpRunning.store(false);
389 return true;
390 }
391
392 while (g_isDumpRunning.load()) {
393 FileAgeingUtils::HandleAgeing(loopTraceFiles_, param.type);
394 std::string traceFile = GenerateTraceFileName(param.type);
395 if (DoDumpTraceLoop(param, traceFile, true)) {
396 std::lock_guard<std::mutex> lck(traceFileMutex_);
397 loopTraceFiles_.emplace_back(traceFile);
398 } else {
399 break;
400 }
401 }
402 g_isDumpEnded.store(true);
403 g_isDumpRunning.store(false);
404 return true;
405 }
406
StopDumpTraceLoop()407 std::vector<std::string> TraceDumpExecutor::StopDumpTraceLoop()
408 {
409 g_isDumpRunning.store(false);
410 while (!g_isDumpEnded.load()) {
411 std::this_thread::sleep_for(std::chrono::milliseconds(100)); // 100 : 100ms
412 g_isDumpRunning.store(false);
413 }
414
415 std::lock_guard<std::mutex> lck(traceFileMutex_);
416 return FilterLoopTraceResult(loopTraceFiles_);
417 }
418
StartCacheTraceLoop(const TraceDumpParam & param,uint64_t totalFileSize,uint64_t sliceMaxDuration)419 bool TraceDumpExecutor::StartCacheTraceLoop(const TraceDumpParam& param,
420 uint64_t totalFileSize, uint64_t sliceMaxDuration)
421 {
422 g_sliceMaxDuration = sliceMaxDuration;
423 while (g_isDumpRunning.load()) {
424 auto traceFile = GenerateTraceFileName(param.type);
425 if (DoDumpTraceLoop(param, traceFile, true)) {
426 std::lock_guard<std::mutex> cacheLock(traceFileMutex_);
427 ClearCacheTraceFileBySize(cacheTraceFiles_, totalFileSize);
428 HILOG_INFO(LOG_CORE, "ProcessCacheTask: save cache file.");
429 } else {
430 break;
431 }
432 }
433 g_isDumpEnded.store(true);
434 return true;
435 }
436
StopCacheTraceLoop()437 void TraceDumpExecutor::StopCacheTraceLoop()
438 {
439 g_isDumpRunning.store(false);
440 while (!g_isDumpEnded.load()) {
441 std::this_thread::sleep_for(std::chrono::milliseconds(100)); // 100 : 100ms
442 g_isDumpRunning.store(false);
443 }
444 }
445
DumpTrace(const TraceDumpParam & param)446 TraceDumpRet TraceDumpExecutor::DumpTrace(const TraceDumpParam& param)
447 {
448 MarkClockSync(tracefsDir_);
449 constexpr int waitTime = 10000; // 10ms
450 usleep(waitTime);
451 const std::string traceFile = GenerateTraceFileName(param.type);
452 return DumpTraceInner(param, traceFile);
453 }
454
GetCacheTraceFiles()455 std::vector<TraceFileInfo> TraceDumpExecutor::GetCacheTraceFiles()
456 {
457 g_interruptCache.store(true);
458 std::vector<TraceFileInfo> cacheFiles;
459 {
460 std::lock_guard<std::mutex> lock(traceFileMutex_);
461 cacheFiles = cacheTraceFiles_;
462 g_interruptCache.store(false);
463 }
464 return cacheFiles;
465 }
466
ReadRawTraceLoop()467 void TraceDumpExecutor::ReadRawTraceLoop()
468 {
469 const std::string threadName = "ReadRawTraceLoop";
470 prctl(PR_SET_NAME, threadName.c_str());
471 HILOG_INFO(LOG_CORE, "ReadRawTraceLoop start.");
472 while (g_readFlag.load()) {
473 TraceDumpTask currentTask;
474 bool hasTask = false;
475 {
476 std::unique_lock<std::mutex> lck(taskQueueMutex_);
477 readCondVar_.wait(lck, [this]() {
478 return !g_readFlag.load() ||
479 std::any_of(traceDumpTaskVec_.begin(), traceDumpTaskVec_.end(), [](const TraceDumpTask& task) {
480 return task.status == TraceDumpStatus::START && task.code == TraceErrorCode::UNSET;
481 });
482 });
483 if (!g_readFlag.load()) {
484 break;
485 }
486 auto it = std::find_if(traceDumpTaskVec_.begin(), traceDumpTaskVec_.end(), [](const TraceDumpTask& task) {
487 return task.status == TraceDumpStatus::START && task.code == TraceErrorCode::UNSET;
488 });
489 if (it != traceDumpTaskVec_.end()) {
490 currentTask = *it;
491 hasTask = true;
492 }
493 }
494 if (hasTask) {
495 HILOG_INFO(LOG_CORE, "ReadRawTraceLoop : start read trace of taskid[%{public}" PRIu64 "]",
496 currentTask.time);
497 if (!DoReadRawTrace(currentTask)) {
498 HILOG_WARN(LOG_CORE, "ReadRawTraceLoop : do read raw trace failed, taskid[%{public}" PRIu64 "]",
499 currentTask.time);
500 } else {
501 HILOG_INFO(LOG_CORE, "ReadRawTraceLoop : read raw trace done, taskid[%{public}" PRIu64 "]",
502 currentTask.time);
503 writeCondVar_.notify_one();
504 }
505 }
506 }
507 HILOG_INFO(LOG_CORE, "ReadRawTraceLoop end.");
508 }
509
WriteTraceLoop()510 void TraceDumpExecutor::WriteTraceLoop()
511 {
512 const std::string threadName = "WriteTraceLoop";
513 prctl(PR_SET_NAME, threadName.c_str());
514 HILOG_INFO(LOG_CORE, "WriteTraceLoop start.");
515 while (g_writeFlag.load()) {
516 TraceDumpTask currentTask;
517 bool hasTask = false;
518 {
519 std::unique_lock<std::mutex> lck(taskQueueMutex_);
520 writeCondVar_.wait(lck, [this]() {
521 return !g_writeFlag.load() ||
522 std::any_of(traceDumpTaskVec_.begin(), traceDumpTaskVec_.end(), [](const TraceDumpTask& task) {
523 return task.status == TraceDumpStatus::READ_DONE || task.status == TraceDumpStatus::WAIT_WRITE;
524 });
525 });
526 if (!g_writeFlag.load()) {
527 break;
528 }
529 auto it = std::find_if(traceDumpTaskVec_.begin(), traceDumpTaskVec_.end(), [](const TraceDumpTask& task) {
530 return task.status == TraceDumpStatus::READ_DONE || task.status == TraceDumpStatus::WAIT_WRITE;
531 });
532 if (it != traceDumpTaskVec_.end()) {
533 currentTask = *it;
534 hasTask = true;
535 }
536 }
537 if (hasTask) {
538 HILOG_INFO(LOG_CORE, "WriteTraceLoop : start write trace of taskid[%{public}" PRIu64 "]", currentTask.time);
539 if (!DoWriteRawTrace(currentTask)) {
540 HILOG_WARN(LOG_CORE, "WriteTraceLoop : do write raw trace failed, taskid[%{public}" PRIu64 "]",
541 currentTask.time);
542 } else {
543 HILOG_INFO(LOG_CORE, "WriteTraceLoop : write raw trace done, taskid[%{public}" PRIu64 "]",
544 currentTask.time);
545 }
546 }
547 }
548 HILOG_INFO(LOG_CORE, "WriteTraceLoop end.");
549 }
550
ProcessNewTask(std::shared_ptr<HitraceDumpPipe> & dumpPipe,int & sleepCnt)551 void TraceDumpExecutor::ProcessNewTask(std::shared_ptr<HitraceDumpPipe>& dumpPipe, int& sleepCnt)
552 {
553 TraceDumpTask newTask;
554 if (dumpPipe->ReadTraceTask(200, newTask)) { // 200 : 200ms
555 std::lock_guard<std::mutex> lck(taskQueueMutex_);
556 traceDumpTaskVec_.push_back(newTask);
557 readCondVar_.notify_one();
558 sleepCnt = 0;
559 }
560 }
561
DoProcessTraceDumpTask(std::shared_ptr<HitraceDumpPipe> & dumpPipe,TraceDumpTask & task,std::vector<TraceDumpTask> & completedTasks)562 void TraceDumpExecutor::DoProcessTraceDumpTask(std::shared_ptr<HitraceDumpPipe>& dumpPipe, TraceDumpTask& task,
563 std::vector<TraceDumpTask>& completedTasks)
564 {
565 uint64_t curBootTime = GetCurBootTime();
566 if (task.status == TraceDumpStatus::WRITE_DONE) {
567 if (task.hasSyncReturn && dumpPipe->WriteAsyncReturn(task)) { // Async return
568 completedTasks.push_back(task);
569 } else if (!task.hasSyncReturn && dumpPipe->WriteSyncReturn(task)) { // Sync return
570 task.hasSyncReturn = true;
571 if (curBootTime - task.time <= SYNC_RETURN_TIMEOUT_NS) {
572 completedTasks.push_back(task);
573 }
574 }
575 } else if (task.status == TraceDumpStatus::READ_DONE) {
576 if (task.code != TraceErrorCode::SUCCESS) {
577 task.status = TraceDumpStatus::WRITE_DONE;
578 auto writeRet = false;
579 if (!task.hasSyncReturn) {
580 writeRet = dumpPipe->WriteSyncReturn(task);
581 } else {
582 writeRet = dumpPipe->WriteAsyncReturn(task);
583 }
584 if (writeRet) {
585 completedTasks.push_back(task);
586 }
587 } else if (!task.hasSyncReturn && curBootTime - task.time > SYNC_RETURN_TIMEOUT_NS) { // write trace timeout
588 if (dumpPipe->WriteSyncReturn(task)) {
589 task.hasSyncReturn = true;
590 task.status = TraceDumpStatus::WAIT_WRITE;
591 writeCondVar_.notify_one();
592 }
593 }
594 }
595 }
596
TraceDumpTaskMonitor()597 void TraceDumpExecutor::TraceDumpTaskMonitor()
598 {
599 auto dumpPipe = std::make_shared<HitraceDumpPipe>(false);
600 int sleepCnt = 0;
601 while (true) {
602 std::vector<TraceDumpTask> completedTasks;
603 {
604 std::lock_guard<std::mutex> lck(taskQueueMutex_);
605 for (auto& task : traceDumpTaskVec_) {
606 DoProcessTraceDumpTask(dumpPipe, task, completedTasks);
607 }
608 // Remove completed tasks
609 for (const auto& task : completedTasks) {
610 auto it = std::remove_if(traceDumpTaskVec_.begin(), traceDumpTaskVec_.end(),
611 [&task](const TraceDumpTask& t) { return t.time == task.time; });
612 traceDumpTaskVec_.erase(it, traceDumpTaskVec_.end());
613 }
614 if (!traceDumpTaskVec_.empty()) {
615 sleepCnt = 0;
616 }
617 }
618 // Check for new tasks
619 ProcessNewTask(dumpPipe, sleepCnt);
620 sleep(1);
621 sleepCnt++;
622 if (sleepCnt >= 15 && IsTraceDumpTaskEmpty()) { // 15 : similar to 15 seconds
623 HILOG_INFO(LOG_CORE, "TraceDumpTaskMonitor : no task, dump process exit.");
624 g_readFlag.store(false);
625 g_writeFlag.store(false);
626 readCondVar_.notify_all();
627 writeCondVar_.notify_all();
628 break;
629 }
630 }
631 }
632
RemoveTraceDumpTask(const uint64_t time)633 void TraceDumpExecutor::RemoveTraceDumpTask(const uint64_t time)
634 {
635 std::lock_guard<std::mutex> lck(taskQueueMutex_);
636 auto it = std::remove_if(traceDumpTaskVec_.begin(), traceDumpTaskVec_.end(),
637 [time](const TraceDumpTask& task) { return task.time == time; });
638 if (it != traceDumpTaskVec_.end()) {
639 traceDumpTaskVec_.erase(it, traceDumpTaskVec_.end());
640 HILOG_INFO(LOG_CORE, "EraseTraceDumpTask: task removed from task list.");
641 } else {
642 HILOG_WARN(LOG_CORE, "EraseTraceDumpTask: task not found in task list.");
643 }
644 }
645
UpdateTraceDumpTask(const TraceDumpTask & task)646 bool TraceDumpExecutor::UpdateTraceDumpTask(const TraceDumpTask& task)
647 {
648 std::lock_guard<std::mutex> lck(taskQueueMutex_);
649 for (auto& dumpTask : traceDumpTaskVec_) {
650 if (dumpTask.time == task.time) {
651 // attention: avoid updating hasSyncReturn field, it is only used in monitor thread.
652 dumpTask.code = task.code;
653 dumpTask.status = task.status;
654 dumpTask.fileSize = task.fileSize;
655 dumpTask.traceStartTime = task.traceStartTime;
656 dumpTask.traceEndTime = task.traceEndTime;
657 if (strcpy_s(dumpTask.outputFile, sizeof(dumpTask.outputFile), task.outputFile) != 0) {
658 HILOG_ERROR(LOG_CORE, "UpdateTraceDumpTask: strcpy_s failed.");
659 }
660 HILOG_INFO(LOG_CORE, "UpdateTraceDumpTask: task id: %{public}" PRIu64 ", status: %{public}hhu, "
661 "file: %{public}s, filesize: %{public}" PRId64 ".",
662 task.time, task.status, task.outputFile, task.fileSize);
663 return true;
664 }
665 }
666 HILOG_WARN(LOG_CORE, "UpdateTraceDumpTask: task[%{public}" PRIu64 "] not found in lists.", task.time);
667 return false;
668 }
669
AddTraceDumpTask(const TraceDumpTask & task)670 void TraceDumpExecutor::AddTraceDumpTask(const TraceDumpTask& task)
671 {
672 std::lock_guard<std::mutex> lck(taskQueueMutex_);
673 traceDumpTaskVec_.emplace_back(task);
674 readCondVar_.notify_one();
675 HILOG_INFO(LOG_CORE, "AddTraceDumpTask: task added to the list.");
676 }
677
ClearTraceDumpTask()678 void TraceDumpExecutor::ClearTraceDumpTask()
679 {
680 std::lock_guard<std::mutex> lck(taskQueueMutex_);
681 traceDumpTaskVec_.clear();
682 }
683
IsTraceDumpTaskEmpty()684 bool TraceDumpExecutor::IsTraceDumpTaskEmpty()
685 {
686 std::lock_guard<std::mutex> lck(taskQueueMutex_);
687 return traceDumpTaskVec_.empty();
688 }
689
GetTraceDumpTaskCount()690 size_t TraceDumpExecutor::GetTraceDumpTaskCount()
691 {
692 std::lock_guard<std::mutex> lck(taskQueueMutex_);
693 return traceDumpTaskVec_.size();
694 }
695
696 #ifdef HITRACE_UNITTEST
ClearCacheTraceFiles()697 void TraceDumpExecutor::ClearCacheTraceFiles()
698 {
699 std::lock_guard<std::mutex> lck(traceFileMutex_);
700 cacheTraceFiles_.clear();
701 }
702 #endif
703
SetTraceDumpStrategy(std::unique_ptr<ITraceDumpStrategy> strategy)704 void TraceDumpExecutor::SetTraceDumpStrategy(std::unique_ptr<ITraceDumpStrategy> strategy)
705 {
706 dumpStrategy_ = std::move(strategy);
707 }
708
ExecuteDumpTrace(std::shared_ptr<ITraceSource> traceSource,const TraceDumpRequest & request)709 TraceDumpRet TraceDumpExecutor::ExecuteDumpTrace(std::shared_ptr<ITraceSource> traceSource,
710 const TraceDumpRequest& request)
711 {
712 if (dumpStrategy_ == nullptr) {
713 HILOG_ERROR(LOG_CORE, "ExecuteDumpTrace : No write strategy set!");
714 return { TraceErrorCode::FILE_ERROR, "", 0, 0 }; // todo
715 }
716 return dumpStrategy_->Execute(traceSource, request);
717 }
718
DoDumpTraceLoop(const TraceDumpParam & param,std::string & traceFile,bool isLimited)719 bool TraceDumpExecutor::DoDumpTraceLoop(const TraceDumpParam& param, std::string& traceFile, bool isLimited)
720 {
721 if (tracefsDir_.empty()) {
722 HILOG_ERROR(LOG_CORE, "DumpTrace : Trace fs path is empty.");
723 return false;
724 }
725 MarkClockSync(tracefsDir_);
726 int fileSizeThreshold = param.type == TraceDumpType::TRACE_CACHE ?
727 DEFAULT_CACHE_FILE_SIZE * BYTE_PER_KB : DEFAULT_FILE_SIZE * BYTE_PER_KB;
728 if (param.fileSize != 0) {
729 fileSizeThreshold = param.fileSize * BYTE_PER_KB;
730 }
731 std::shared_ptr<ITraceSource> traceSource = nullptr;
732 if (IsHmKernel()) {
733 traceSource = std::make_shared<TraceSourceHM>(tracefsDir_, traceFile);
734 } else {
735 traceSource = std::make_shared<TraceSourceLinux>(tracefsDir_, traceFile);
736 }
737 if (param.type == TraceDumpType::TRACE_RECORDING) {
738 SetTraceDumpStrategy(std::make_unique<RecordTraceDumpStrategy>());
739 } else {
740 SetTraceDumpStrategy(std::make_unique<CacheTraceDumpStrategy>());
741 }
742
743 TraceDumpRequest request = { param.type, fileSizeThreshold, isLimited, param.traceStartTime, param.traceEndTime };
744
745 std::lock_guard<std::mutex> lck(traceFileMutex_);
746 auto dumpRet = ExecuteDumpTrace(traceSource, request);
747 HILOG_INFO(LOG_CORE, "DoDumpTraceLoop: ExecuteDumpTrace done, errorcode: %{public}d, tracefile: %{public}s",
748 static_cast<uint8_t>(dumpRet.code), dumpRet.outputFile);
749 if (dumpRet.code != TraceErrorCode::SUCCESS) {
750 HILOG_ERROR(LOG_CORE, "DoDumpTraceLoop : Execute loop trace dump failed.");
751 return false;
752 }
753 traceFile = traceSource->GetTraceFilePath();
754 if (param.type == TraceDumpType::TRACE_CACHE) {
755 TraceFileInfo traceFileInfo;
756 if (!SetFileInfo(true, traceFile, dumpRet.traceStartTime, dumpRet.traceEndTime, traceFileInfo)) {
757 RemoveFile(traceFile);
758 return false;
759 }
760 traceFile = traceFileInfo.filename;
761 cacheTraceFiles_.emplace_back(traceFileInfo);
762 }
763 if (access(traceFile.c_str(), F_OK) == -1) {
764 HILOG_ERROR(LOG_CORE, "DoDumpTraceLoop : Trace file (%{public}s) not found.", traceFile.c_str());
765 return false;
766 }
767 return true;
768 }
769
DumpTraceInner(const TraceDumpParam & param,const std::string & traceFile)770 TraceDumpRet TraceDumpExecutor::DumpTraceInner(const TraceDumpParam& param, const std::string& traceFile)
771 {
772 std::shared_ptr<ITraceSource> traceSource = nullptr;
773 if (tracefsDir_.empty()) {
774 HILOG_ERROR(LOG_CORE, "DumpTrace : Trace fs path is empty.");
775 return { TraceErrorCode::TRACE_NOT_SUPPORTED, "", 0, 0 }; // todo
776 }
777 if (IsHmKernel()) {
778 traceSource = std::make_shared<TraceSourceHM>(tracefsDir_, traceFile);
779 } else {
780 traceSource = std::make_shared<TraceSourceLinux>(tracefsDir_, traceFile);
781 }
782
783 TraceDumpRequest request = {
784 param.type,
785 0,
786 false,
787 param.traceStartTime,
788 param.traceEndTime
789 };
790
791 SetTraceDumpStrategy(std::make_unique<SnapshotTraceDumpStrategy>());
792 return ExecuteDumpTrace(traceSource, request);
793 }
794
DoReadRawTrace(TraceDumpTask & task)795 bool TraceDumpExecutor::DoReadRawTrace(TraceDumpTask& task)
796 {
797 std::shared_ptr<ITraceSource> traceSource = nullptr;
798 if (tracefsDir_.empty()) {
799 HILOG_ERROR(LOG_CORE, "DoReadRawTrace : Trace fs path is empty.");
800 task.code = TraceErrorCode::TRACE_NOT_SUPPORTED;
801 return false;
802 }
803 if (IsHmKernel()) {
804 traceSource = std::make_shared<TraceSourceHM>(tracefsDir_, "");
805 } else {
806 traceSource = std::make_shared<TraceSourceLinux>(tracefsDir_, "");
807 }
808
809 TraceDumpRequest request = {
810 TraceDumpType::TRACE_SNAPSHOT,
811 0,
812 false,
813 task.traceStartTime,
814 task.traceEndTime,
815 task.time
816 };
817 SetTraceDumpStrategy(std::make_unique<AsyncTraceReadStrategy>());
818 auto ret = ExecuteDumpTrace(traceSource, request);
819 task.code = ret.code;
820 task.fileSize = ret.fileSize + ASYNC_DUMP_FILE_SIZE_ADDITION;
821 if (strncpy_s(task.outputFile, TRACE_FILE_LEN, ret.outputFile, TRACE_FILE_LEN - 1) != 0) {
822 HILOG_ERROR(LOG_CORE, "DoReadRawTrace: strncpy_s failed.");
823 }
824 task.status = TraceDumpStatus::READ_DONE;
825 if (task.code == TraceErrorCode::SUCCESS && task.fileSize > task.fileSizeLimit) {
826 task.isFileSizeOverLimit = true;
827 }
828 #ifdef HITRACE_ASYNC_READ_TIMEOUT_TEST
829 sleep(10); // 10 : sleep 10 seconds to construct a timeout task
830 #endif
831 if (!UpdateTraceDumpTask(task)) {
832 HILOG_ERROR(LOG_CORE, "DoReadRawTrace: update trace dump task failed.");
833 return false;
834 }
835 return task.code == TraceErrorCode::SUCCESS;
836 }
837
DoWriteRawTrace(TraceDumpTask & task)838 bool TraceDumpExecutor::DoWriteRawTrace(TraceDumpTask& task)
839 {
840 std::shared_ptr<ITraceSource> traceSource = nullptr;
841 if (tracefsDir_.empty()) {
842 HILOG_ERROR(LOG_CORE, "DoWriteRawTrace : Trace fs path is empty.");
843 task.code = TraceErrorCode::TRACE_NOT_SUPPORTED;
844 return false;
845 }
846 if (IsHmKernel()) {
847 traceSource = std::make_shared<TraceSourceHM>(tracefsDir_, task.outputFile);
848 } else {
849 traceSource = std::make_shared<TraceSourceLinux>(tracefsDir_, task.outputFile);
850 }
851
852 TraceDumpRequest request = {
853 TraceDumpType::TRACE_SNAPSHOT,
854 0,
855 false,
856 task.traceStartTime,
857 task.traceEndTime,
858 task.time
859 };
860 SetTraceDumpStrategy(std::make_unique<AsyncTraceWriteStrategy>());
861 auto ret = ExecuteDumpTrace(traceSource, request);
862 task.code = ret.code;
863 task.fileSize = static_cast<int64_t>(GetFileSize(std::string(task.outputFile)));
864 task.status = TraceDumpStatus::WRITE_DONE;
865 if (task.code == TraceErrorCode::SUCCESS && task.fileSize > task.fileSizeLimit) {
866 task.isFileSizeOverLimit = true;
867 }
868 #ifdef HITRACE_ASYNC_WRITE_TIMEOUT_TEST
869 sleep(10); // 10 : sleep 10 seconds to construct a timeout task
870 #endif
871 if (!UpdateTraceDumpTask(task)) {
872 HILOG_ERROR(LOG_CORE, "DoWriteRawTrace: update trace dump task failed.");
873 return false;
874 }
875 return task.code == TraceErrorCode::SUCCESS;
876 }
877 } // namespace Hitrace
878 } // namespace HiviewDFX
879 } // namespace OHOS