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 std::string 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