1 /*
2 * Copyright (c) 2024 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 <atomic>
17 #include <thread>
18
19 #include "rs_profiler.h"
20 #include "rs_profiler_archive.h"
21 #include "rs_profiler_command.h"
22 #include "rs_profiler_file.h"
23 #include "rs_profiler_network.h"
24 #include "rs_profiler_packet.h"
25 #include "rs_profiler_telemetry.h"
26 #include "pipeline/main_thread/rs_main_thread.h"
27
28 namespace OHOS::Rosen {
29
30 static constexpr uint32_t INACTIVITY_THRESHOLD_SECONDS = 5u;
31 static DeviceInfo g_deviceInfo;
32 static std::mutex g_deviceInfoMutex;
33 static std::atomic_bool g_started = false;
34 static double g_inactiveTimestamp = 0.0;
35 static double g_recordsTimestamp = 0.0;
36 static double g_currentFrameDirtyRegion = 0.0;
37 static uint64_t g_lastParcelTime = 0;
38 static int g_animationCount = 0;
39 static std::mutex g_mutexBetaRecording;
40 static bool g_mutexBetaRecordingLocked = false;
41
42 // implemented in rs_profiler.cpp
43 void DeviceInfoToCaptureData(double time, const DeviceInfo& in, RSCaptureData& out);
44
HasInitializationFinished()45 static bool HasInitializationFinished()
46 {
47 constexpr uint32_t maxAttempts = 600u;
48 static std::atomic_uint32_t attempt = 0u;
49 if (attempt < maxAttempts) {
50 attempt++;
51 }
52 return attempt >= maxAttempts;
53 }
54
GetBetaRecordFileName(uint32_t index)55 static std::string GetBetaRecordFileName(uint32_t index)
56 {
57 constexpr uint32_t ten = 10u;
58 const std::string cacheFile("/data/service/el0/render_service/file");
59 return cacheFile + ((index < ten) ? "0" : "") + std::to_string(index) + ".ohr";
60 }
61
IsBetaRecordInactive()62 bool RSProfiler::IsBetaRecordInactive()
63 {
64 return (Now() - g_inactiveTimestamp) > INACTIVITY_THRESHOLD_SECONDS;
65 }
66
RequestVSyncOnBetaRecordInactivity()67 void RSProfiler::RequestVSyncOnBetaRecordInactivity()
68 {
69 if (IsBetaRecordInactive()) {
70 RequestNextVSync();
71 }
72 }
73
LaunchBetaRecordNotificationThread()74 void RSProfiler::LaunchBetaRecordNotificationThread()
75 {
76 std::thread thread([]() {
77 while (IsBetaRecordStarted()) {
78 RequestVSyncOnBetaRecordInactivity();
79 std::this_thread::sleep_for(std::chrono::seconds(INACTIVITY_THRESHOLD_SECONDS));
80 }
81 });
82 thread.detach();
83 }
84
LaunchBetaRecordFileSplitThread()85 void RSProfiler::LaunchBetaRecordFileSplitThread()
86 {
87 std::thread thread([]() {
88 while (IsBetaRecordStarted() && IsBetaRecordEnabled()) {
89 SaveBetaRecord();
90
91 constexpr int32_t splitCheckTime = 100;
92 std::this_thread::sleep_for(std::chrono::milliseconds(splitCheckTime));
93 }
94 });
95 thread.detach();
96 }
97
LaunchBetaRecordMetricsUpdateThread()98 void RSProfiler::LaunchBetaRecordMetricsUpdateThread()
99 {
100 if (!IsBetaRecordEnabledWithMetrics()) {
101 return;
102 }
103 std::thread thread([]() {
104 while (IsBetaRecordStarted()) {
105 const DeviceInfo deviceInfo = RSTelemetry::GetDeviceInfo();
106
107 g_deviceInfoMutex.lock();
108 g_deviceInfo = deviceInfo;
109 g_deviceInfoMutex.unlock();
110
111 constexpr int32_t sendInterval = 8;
112 std::this_thread::sleep_for(std::chrono::milliseconds(sendInterval));
113 }
114 });
115 thread.detach();
116 }
117
BetaRecordOnFrameBegin()118 void RSProfiler::BetaRecordOnFrameBegin()
119 {
120 if (IsBetaRecordStarted() && IsBetaRecordEnabled()) {
121 g_mutexBetaRecording.lock();
122 g_mutexBetaRecordingLocked = true;
123 if (!IsNoneMode() && IsSecureScreen()) {
124 // don't record secure screens
125 std::vector<std::string> argList;
126 argList.push_back("REMOVELAST");
127 RecordStop(ArgList(argList));
128 EnableBetaRecord();
129 }
130 } else {
131 g_mutexBetaRecordingLocked = false;
132 }
133 }
134
BetaRecordOnFrameEnd()135 void RSProfiler::BetaRecordOnFrameEnd()
136 {
137 if (g_mutexBetaRecordingLocked) {
138 g_mutexBetaRecording.unlock();
139 g_mutexBetaRecordingLocked = false;
140 }
141 }
142
StartBetaRecord()143 void RSProfiler::StartBetaRecord()
144 {
145 if (HasInitializationFinished() && !IsBetaRecordStarted() && IsBetaRecordEnabled()) {
146 g_inactiveTimestamp = Now();
147 g_recordsTimestamp = Now();
148
149 LaunchBetaRecordNotificationThread();
150 LaunchBetaRecordMetricsUpdateThread();
151
152 if (!IsSecureScreen()) {
153 // Start recording for the first file
154 RecordStart(ArgList());
155 }
156
157 g_started = true;
158
159 LaunchBetaRecordFileSplitThread();
160 }
161 }
162
StopBetaRecord()163 void RSProfiler::StopBetaRecord()
164 {
165 if (IsBetaRecordStarted()) {
166 RecordStop(ArgList());
167 g_started = false;
168 g_inactiveTimestamp = 0;
169 }
170 }
171
IsBetaRecordStarted()172 bool RSProfiler::IsBetaRecordStarted()
173 {
174 return g_started;
175 }
176
BetaRecordSetLastParcelTime()177 void RSProfiler::BetaRecordSetLastParcelTime()
178 {
179 g_lastParcelTime = Utils::Now();
180 }
181
SaveBetaRecord()182 void RSProfiler::SaveBetaRecord()
183 {
184 if (!RSUniRenderThread::Instance().IsTaskQueueEmpty()) {
185 // rendering thread works
186 return;
187 }
188
189 constexpr double recordMinLengthSeconds = 30.0;
190 constexpr double recordMaxLengthSeconds = 50.0;
191 const auto recordLength = Now() - g_recordsTimestamp;
192 bool saveShouldBeDone = recordLength > recordMinLengthSeconds;
193 bool saveMustBeDone = recordLength > recordMaxLengthSeconds;
194
195 if (g_animationCount && !saveMustBeDone) {
196 // avoid start beta recording during animations
197 return;
198 }
199
200 if (IsNoneMode()) {
201 std::unique_lock<std::mutex> lock(g_mutexBetaRecording);
202 if (!IsSecureScreen()) {
203 if (RSUniRenderThread::Instance().IsTaskQueueEmpty()) {
204 // rendering thread doesn't work
205 RecordStart(ArgList());
206 }
207 }
208 return;
209 }
210
211 if (!IsBetaRecordSavingTriggered() && !saveShouldBeDone) {
212 // start new beta-record file every recordMinLengthSeconds
213 return;
214 }
215
216 std::unique_lock<std::mutex> lock(g_mutexBetaRecording);
217 RecordStop(ArgList());
218 EnableBetaRecord();
219 }
220
UpdateBetaRecord(const RSContext & context)221 void RSProfiler::UpdateBetaRecord(const RSContext& context)
222 {
223 // the last time any rendering is done
224 if (g_currentFrameDirtyRegion > 0) {
225 g_inactiveTimestamp = Now();
226 }
227 g_animationCount = static_cast<int>(context.animatingNodeList_.size());
228 }
229
OpenBetaRecordFile(RSFile & file)230 bool RSProfiler::OpenBetaRecordFile(RSFile& file)
231 {
232 if (!IsBetaRecordStarted()) {
233 return false;
234 }
235
236 const auto path = "RECORD_IN_MEMORY";
237 Utils::FileDelete(path);
238 file.SetVersion(RSFILE_VERSION_LATEST);
239 file.Create(path);
240
241 g_recordsTimestamp = Now();
242 return true;
243 }
244
SaveBetaRecordFile(RSFile & file)245 bool RSProfiler::SaveBetaRecordFile(RSFile& file)
246 {
247 if (!IsBetaRecordStarted()) {
248 return false;
249 }
250
251 constexpr uint32_t maxCacheFiles = 10u;
252 static uint32_t index = 0u;
253
254 std::vector<uint8_t> data;
255 if (!file.GetDataCopy(data)) {
256 return false;
257 }
258
259 auto out = Utils::FileOpen(GetBetaRecordFileName(index), "wbe");
260 if (Utils::IsFileValid(out)) {
261 Utils::FileWrite(out, data.data(), data.size());
262 Utils::FileClose(out);
263 }
264
265 index++;
266 if (index >= maxCacheFiles) {
267 index = 0u;
268 }
269
270 return true;
271 }
272
WriteBetaRecordMetrics(RSFile & file,double time)273 void RSProfiler::WriteBetaRecordMetrics(RSFile& file, double time)
274 {
275 if (!IsBetaRecordStarted() || (time < 0.0) || !IsBetaRecordEnabledWithMetrics()) {
276 return;
277 }
278
279 g_deviceInfoMutex.lock();
280 const DeviceInfo deviceInfo = g_deviceInfo;
281 g_deviceInfoMutex.unlock();
282
283 RSCaptureData captureData;
284 DeviceInfoToCaptureData(time, deviceInfo, captureData);
285
286 std::vector<char> out;
287 DataWriter archive(out);
288 char headerType = static_cast<char>(PackageID::RS_PROFILER_GFX_METRICS);
289 archive.Serialize(headerType);
290 captureData.Serialize(archive);
291
292 file.WriteGFXMetrics(0, time, 0, out.data(), out.size());
293 }
294
UpdateDirtyRegionBetaRecord(double currentFrameDirtyRegion)295 void RSProfiler::UpdateDirtyRegionBetaRecord(double currentFrameDirtyRegion)
296 {
297 g_currentFrameDirtyRegion = currentFrameDirtyRegion;
298 }
299
300 } // namespace OHOS::Rosen