• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "base/log/dump_recorder.h"
17 
18 #include <fstream>
19 
20 #include "base/log/dump_log.h"
21 #include "core/common/ace_application_info.h"
22 #include "core/common/container.h"
23 
24 namespace OHOS::Ace {
25 
26 namespace {
27 constexpr uint32_t MAX_FILE_SIZE = 100 * 1024 * 1024;
28 constexpr char REC_FILE_NAME[] = "/arkui_dump.rec";
29 const std::vector<std::string> SKIP_COMPARE_PARAMS = { "time", "children" };
30 } // namespace
31 
32 DumpRecorder::DumpRecorder() = default;
33 DumpRecorder::~DumpRecorder() = default;
34 
Init()35 void DumpRecorder::Init()
36 {
37     Clear();
38     recordTree_ = JsonUtil::Create(true);
39     auto jsonNodeArray = JsonUtil::CreateArray(true);
40     recordTree_->PutRef("infos", std::move(jsonNodeArray));
41 }
42 
Clear()43 void DumpRecorder::Clear()
44 {
45     fileSize_ = 0;
46     records_.clear();
47     recordTree_.reset();
48 }
49 
Start(std::function<bool ()> && func)50 void DumpRecorder::Start(std::function<bool()>&& func)
51 {
52     if (frameDumpFunc_) {
53         Stop();
54     }
55     Init();
56     frameDumpFunc_ = func;
57 }
58 
Stop()59 void DumpRecorder::Stop()
60 {
61     if (!frameDumpFunc_) {
62         return;
63     }
64     frameDumpFunc_ = nullptr;
65     auto taskExecutor = Container::CurrentTaskExecutorSafelyWithCheck();
66     CHECK_NULL_VOID(taskExecutor);
67     taskExecutor->PostTask(
68         []() -> void { DumpRecorder::GetInstance().StopInner(); }, TaskExecutor::TaskType::BACKGROUND, "ArkUIDumpStop");
69 }
70 
StopInner()71 void DumpRecorder::StopInner()
72 {
73     CHECK_NULL_VOID(recordTree_);
74     Output(recordTree_->ToString());
75     Clear();
76 }
77 
Record(int64_t timestamp,std::unique_ptr<JsonValue> && json)78 void DumpRecorder::Record(int64_t timestamp, std::unique_ptr<JsonValue>&& json)
79 {
80     records_[timestamp] = std::move(json);
81     if (static_cast<int32_t>(records_.size()) > 1) {
82         auto taskExecutor = Container::CurrentTaskExecutorSafelyWithCheck();
83         CHECK_NULL_VOID(taskExecutor);
84         taskExecutor->PostTask([timestamp]() -> void { DumpRecorder::GetInstance().Diff(timestamp); },
85             TaskExecutor::TaskType::BACKGROUND, "ArkUIDumpDiff");
86     } else {
87         auto info = JsonUtil::Create();
88         info->Put("startTime", timestamp);
89         auto& infoJson = records_.at(timestamp);
90         info->Put("info", infoJson);
91         std::string infoContent = info->ToString();
92         fileSize_ += static_cast<uint32_t>(infoContent.size());
93         auto infos = recordTree_->GetValue("infos");
94         infos->PutRef(std::move(info));
95     }
96 }
97 
Diff(int64_t timestamp)98 void DumpRecorder::Diff(int64_t timestamp)
99 {
100     if (timestamp <= records_.begin()->first) {
101         return;
102     }
103     auto iter = records_.find(timestamp);
104     if (iter == records_.end()) {
105         return;
106     }
107     auto& curNode = iter->second;
108     auto& prevNode = records_.begin()->second;
109 
110     std::string diff;
111     Compare(curNode, prevNode, diff);
112     auto info = JsonUtil::Create();
113     info->Put("startTime", timestamp);
114     if (diff.empty()) {
115         info->Put("info", "");
116     } else {
117         auto infoJson = JsonUtil::ParseJsonString(diff);
118         info->PutRef("info", std::move(infoJson));
119     }
120     auto infoContent = info->ToString();
121     fileSize_ += static_cast<uint32_t>(infoContent.size());
122     auto infos = recordTree_->GetValue("infos");
123     infos->PutRef(std::move(info));
124     if (fileSize_ > MAX_FILE_SIZE) {
125         Stop();
126     }
127     records_.erase(timestamp);
128 }
129 
CompareDumpParam(std::unique_ptr<JsonValue> & curParams,std::unique_ptr<JsonValue> & prevParams)130 bool DumpRecorder::CompareDumpParam(std::unique_ptr<JsonValue>& curParams, std::unique_ptr<JsonValue>& prevParams)
131 {
132     auto curParam = curParams->GetChild();
133     while (!curParam->IsNull()) {
134         auto curParamKey = curParam->GetKey();
135         if (curParamKey.empty()) {
136             return false;
137         }
138         if (std::find(SKIP_COMPARE_PARAMS.begin(), SKIP_COMPARE_PARAMS.end(), curParamKey) !=
139             SKIP_COMPARE_PARAMS.end()) {
140             curParam = curParam->GetNext();
141             continue;
142         }
143         auto prevParam = prevParams->GetValue(curParamKey);
144         if (prevParam->IsNull()) {
145             return false;
146         }
147         if (curParam->ToString() != prevParam->ToString()) {
148             return false;
149         }
150         curParam = curParam->GetNext();
151     }
152     return true;
153 }
154 
Compare(std::unique_ptr<JsonValue> & curNode,std::unique_ptr<JsonValue> & prevNode,std::string & diff)155 bool DumpRecorder::Compare(std::unique_ptr<JsonValue>& curNode, std::unique_ptr<JsonValue>& prevNode, std::string& diff)
156 {
157     CHECK_NULL_RETURN(curNode, false);
158     CHECK_NULL_RETURN(prevNode, false);
159     auto curNodeInfo = curNode->GetChild();
160     auto prevNodeInfo = prevNode->GetChild();
161     std::string curNodeName = curNodeInfo->GetKey();
162     std::string prevNodeName = prevNodeInfo->GetKey();
163     if (curNodeName != prevNodeName) {
164         diff = curNode->ToString();
165         return false;
166     }
167     auto curNodeChildren = curNodeInfo->GetValue("children");
168     auto prevNodeChildren = prevNodeInfo->GetValue("children");
169     if (curNodeChildren->GetArraySize() != prevNodeChildren->GetArraySize()) {
170         diff = curNode->ToString();
171         return false;
172     }
173     if (!CompareDumpParam(curNodeInfo, prevNodeInfo)) {
174         return false;
175     }
176     for (int32_t i = 0; i < curNodeChildren->GetArraySize(); i++) {
177         auto curChild = curNodeChildren->GetArrayItem(i);
178         auto prevChild = prevNodeChildren->GetArrayItem(i);
179         if (!Compare(curChild, prevChild, diff)) {
180             if (diff.empty()) {
181                 diff = curNode->ToString().c_str();
182             }
183             return false;
184         }
185     }
186     return true;
187 }
188 
Output(const std::string & content)189 void DumpRecorder::Output(const std::string& content)
190 {
191     auto dumpFilePath = AceApplicationInfo::GetInstance().GetDataFileDirPath() + REC_FILE_NAME;
192     std::unique_ptr<std::ostream> ostream = std::make_unique<std::ofstream>(dumpFilePath);
193     ostream->write(content.c_str(), content.length());
194     ostream->flush();
195 }
196 } // namespace OHOS::Ace
197