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 <thread>
17
18 #include "rs_profiler.h"
19 #include "rs_profiler_archive.h"
20 #include "rs_profiler_file.h"
21 #include "rs_profiler_network.h"
22 #include "rs_profiler_packet.h"
23 #include "rs_profiler_telemetry.h"
24 #include "rs_profiler_utils.h"
25
26 namespace OHOS::Rosen {
27
28 static constexpr uint32_t INACTIVITY_THRESHOLD_SECONDS = 5u;
29 static DeviceInfo g_deviceInfo;
30 static std::mutex g_deviceInfoMutex;
31 static std::mutex g_fileSavingMutex;
32 static bool g_started = false;
33 static double g_inactiveTimestamp = 0.0;
34 static std::vector<std::string> g_records;
35 static double g_recordsTimestamp = 0.0;
36 static double g_currentFrameDirtyRegion = 0.0;
37
38 // implemented in rs_profiler.cpp
39 void DeviceInfoToCaptureData(double time, const DeviceInfo& in, RSCaptureData& out);
40
HasInitializationFinished()41 static bool HasInitializationFinished()
42 {
43 constexpr uint32_t maxAttempts = 600u;
44 static uint32_t attempt = 0u;
45 if (attempt < maxAttempts) {
46 attempt++;
47 }
48 return attempt == maxAttempts;
49 }
50
IsBetaRecordInactive()51 bool RSProfiler::IsBetaRecordInactive()
52 {
53 return (Now() - g_inactiveTimestamp) > INACTIVITY_THRESHOLD_SECONDS;
54 }
55
RequestVSyncOnBetaRecordInactivity()56 void RSProfiler::RequestVSyncOnBetaRecordInactivity()
57 {
58 if (IsBetaRecordInactive()) {
59 RequestNextVSync();
60 }
61 }
62
LaunchBetaRecordNotificationThread()63 void RSProfiler::LaunchBetaRecordNotificationThread()
64 {
65 std::thread thread([]() {
66 while (IsBetaRecordStarted()) {
67 RequestVSyncOnBetaRecordInactivity();
68 std::this_thread::sleep_for(std::chrono::seconds(INACTIVITY_THRESHOLD_SECONDS));
69 }
70 });
71 thread.detach();
72 }
73
LaunchBetaRecordMetricsUpdateThread()74 void RSProfiler::LaunchBetaRecordMetricsUpdateThread()
75 {
76 if (!IsBetaRecordEnabledWithMetrics()) {
77 return;
78 }
79 std::thread thread([]() {
80 while (IsBetaRecordStarted()) {
81 const DeviceInfo deviceInfo = RSTelemetry::GetDeviceInfo();
82
83 g_deviceInfoMutex.lock();
84 g_deviceInfo = deviceInfo;
85 g_deviceInfoMutex.unlock();
86
87 constexpr int32_t sendInterval = 8;
88 std::this_thread::sleep_for(std::chrono::milliseconds(sendInterval));
89 }
90 });
91 thread.detach();
92 }
93
WriteBetaRecordFileThread(RSFile & file,const std::string path)94 void RSProfiler::WriteBetaRecordFileThread(RSFile& file, const std::string path)
95 {
96 std::vector<uint8_t> fileData;
97 if (!file.GetDataCopy(fileData)) {
98 return;
99 }
100
101 std::thread thread([fileDataCopy{std::move(fileData)}, path]() {
102 const std::lock_guard<std::mutex> fileSavingMutex(g_fileSavingMutex);
103
104 FILE* fileCopy = Utils::FileOpen(path, "wbe");
105 if (!Utils::IsFileValid(fileCopy)) {
106 return;
107 }
108 Utils::FileWrite(fileCopy, fileDataCopy.data(), fileDataCopy.size());
109 Utils::FileClose(fileCopy);
110 });
111 thread.detach();
112 }
113
RenameAndSendFilenameThread()114 void RSProfiler::RenameAndSendFilenameThread()
115 {
116 std::thread thread([]() {
117 const std::lock_guard<std::mutex> fileSavingMutex(g_fileSavingMutex);
118 SendBetaRecordPath();
119 });
120 thread.detach();
121 }
122
StartBetaRecord()123 void RSProfiler::StartBetaRecord()
124 {
125 if (HasInitializationFinished() && !IsBetaRecordStarted() && IsBetaRecordEnabled()) {
126 g_started = true;
127 g_inactiveTimestamp = Now();
128
129 LaunchBetaRecordNotificationThread();
130 LaunchBetaRecordMetricsUpdateThread();
131
132 // Start recording for the first file
133 RecordStart(ArgList());
134 }
135 }
136
IsBetaRecordStarted()137 bool RSProfiler::IsBetaRecordStarted()
138 {
139 return g_started;
140 }
141
SendBetaRecordPath()142 void RSProfiler::SendBetaRecordPath()
143 {
144 if (g_records.empty()) {
145 return;
146 }
147
148 const std::string directory = Utils::GetDirectory(g_records[0]);
149
150 std::string path;
151 for (size_t i = 0; i < g_records.size(); i++) {
152 const auto& original = g_records[i];
153 const auto renamed = Utils::MakePath(directory, "rec_" + std::to_string(g_records.size() - i - 1) + ".ohr");
154 const auto message = "BetaRecord: Rename: " + original + " -> " + renamed;
155 if (std::rename(original.data(), renamed.data())) {
156 Network::SendMessage(message + "failed");
157 path.clear();
158 break;
159 }
160
161 Network::SendMessage(message);
162 path += renamed + ";";
163 }
164 g_records.clear();
165
166 Network::SendBetaRecordPath(path);
167 }
168
SaveBetaRecord()169 bool RSProfiler::SaveBetaRecord()
170 {
171 if (!IsBetaRecordSavingTriggered()) {
172 return false;
173 }
174
175 RecordStop(ArgList());
176 RenameAndSendFilenameThread();
177 EnableBetaRecord();
178 RecordStart(ArgList());
179 return true;
180 }
181
UpdateBetaRecord()182 void RSProfiler::UpdateBetaRecord()
183 {
184 if (!IsBetaRecordStarted()) {
185 return;
186 }
187
188 if (!IsBetaRecordEnabled()) {
189 RecordStop(ArgList());
190 g_started = false;
191 }
192
193 if (!IsRecording()) {
194 return;
195 }
196
197 if (!SaveBetaRecord()) {
198 constexpr uint32_t recordMaxLengthSeconds = 30u; // 30sec length of each recording
199 const double recordLength = Now() - g_recordsTimestamp;
200 if (recordLength > recordMaxLengthSeconds) {
201 RecordStop(ArgList());
202 RecordStart(ArgList());
203 }
204 }
205
206 // the last time any rendering is done
207 if (g_currentFrameDirtyRegion > 0) {
208 g_inactiveTimestamp = Now();
209 }
210 }
211
OpenBetaRecordFile(RSFile & file)212 bool RSProfiler::OpenBetaRecordFile(RSFile& file)
213 {
214 if (!IsBetaRecordStarted()) {
215 return false;
216 }
217
218 const auto path = "RECORD_IN_MEMORY";
219 file.Create(path);
220
221 g_recordsTimestamp = Now();
222 return true;
223 }
224
SaveBetaRecordFile(RSFile & file)225 bool RSProfiler::SaveBetaRecordFile(RSFile& file)
226 {
227 if (!IsBetaRecordStarted()) {
228 return false;
229 }
230
231 const std::string cacheFile("data/service/el0/render_service/file0");
232 constexpr uint32_t maxCacheFiles = 5u; // 5 recordings in a "ring buffer" way
233
234 static uint32_t index = 0u;
235 const auto path = cacheFile + std::to_string(index++) + ".ohr";
236 if (index >= maxCacheFiles) {
237 index = 0u;
238 }
239
240 WriteBetaRecordFileThread(file, path);
241
242 constexpr uint32_t maxRecords = 4u;
243 if (g_records.size() < maxRecords) {
244 g_records.push_back(path);
245 } else {
246 std::rotate(g_records.begin(), g_records.begin() + 1, g_records.end());
247 g_records[g_records.size() - 1] = path;
248 }
249
250 return true;
251 }
252
WriteBetaRecordMetrics(RSFile & file,double time)253 void RSProfiler::WriteBetaRecordMetrics(RSFile& file, double time)
254 {
255 if (!IsBetaRecordStarted() || (time < 0.0) || !IsBetaRecordEnabledWithMetrics()) {
256 return;
257 }
258
259 g_deviceInfoMutex.lock();
260 const DeviceInfo deviceInfo = g_deviceInfo;
261 g_deviceInfoMutex.unlock();
262
263 RSCaptureData captureData;
264 DeviceInfoToCaptureData(time, deviceInfo, captureData);
265
266 std::vector<char> out;
267 DataWriter archive(out);
268 char headerType = static_cast<char>(PackageID::RS_PROFILER_GFX_METRICS);
269 archive.Serialize(headerType);
270 captureData.Serialize(archive);
271
272 file.WriteGFXMetrics(0, time, 0, out.data(), out.size());
273 }
274
UpdateDirtyRegionBetaRecord(double currentFrameDirtyRegion)275 void RSProfiler::UpdateDirtyRegionBetaRecord(double currentFrameDirtyRegion)
276 {
277 g_currentFrameDirtyRegion = currentFrameDirtyRegion;
278 }
279
280 } // namespace OHOS::Rosen