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 "session/host/include/session_change_recorder.h"
17
18 #include <algorithm>
19 #include <charconv>
20 #include <chrono>
21
22 #include "window_helper.h"
23 #include "zlib.h"
24
25 namespace OHOS::Rosen {
26 namespace {
27 #define CHUNK 16384
28 #define COMPRESS_VERSION 9
29
30 constexpr uint32_t MAX_RECORD_TYPE_SIZE = 10;
31 constexpr uint32_t MAX_RECORD_LOG_SIZE = 102400;
32 constexpr uint32_t MAX_EVENT_DUMP_SIZE = 512 * 1024;
33 constexpr int32_t SCHEDULE_SECONDS = 5;
34
35 // LCOV_EXCL_START
GetFormattedTime()36 std::string GetFormattedTime()
37 {
38 auto now = std::chrono::system_clock::now();
39 std::time_t t = std::chrono::system_clock::to_time_t(now);
40 struct tm timeBuffer;
41 std::tm* tmPtr = localtime_r(&t, &timeBuffer);
42 // 1000:million record
43 auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) % 1000;
44
45 std::ostringstream oss;
46 const int formatThreeSpace = 3;
47 oss << std::put_time(tmPtr, "%m-%d %H:%M:%S") << "." << std::setw(formatThreeSpace) << ms.count();
48 return oss.str();
49 }
50 // LCOV_EXCL_STOP
51 } // namespace
52
WM_IMPLEMENT_SINGLE_INSTANCE(SessionChangeRecorder)53 WM_IMPLEMENT_SINGLE_INSTANCE(SessionChangeRecorder)
54
55 void SessionChangeRecorder::Init()
56 {
57 TLOGD(WmsLogTag::DEFAULT, "In");
58 if (isInitFlag_.load()) {
59 return;
60 }
61
62 isInitFlag_.store(true);
63 if (!HiLogIsLoggable(HILOG_DOMAIN_WINDOW, g_domainContents[static_cast<uint32_t>(WmsLogTag::DEFAULT)], LOG_DEBUG)) {
64 stopLogFlag_.store(true);
65 return;
66 }
67
68 // LCOV_EXCL_START
69 stopLogFlag_.store(false);
70 mThread = std::thread([this]() {
71 std::unordered_map<RecordType, std::queue<SceneSessionChangeInfo>> sceneSessionChangeNeedLogMapCopy;
72 while (!stopLogFlag_.load()) {
73 {
74 std::lock_guard<std::mutex> lock(sessionChangeRecorderMutex_);
75 if (!sceneSessionChangeNeedLogMap_.empty()) {
76 sceneSessionChangeNeedLogMapCopy = sceneSessionChangeNeedLogMap_;
77 sceneSessionChangeNeedLogMap_.clear();
78 currentLogSize_ = 0;
79 }
80 }
81 if (!sceneSessionChangeNeedLogMapCopy.empty()) {
82 PrintLog(sceneSessionChangeNeedLogMapCopy);
83 sceneSessionChangeNeedLogMapCopy.clear();
84 }
85 std::this_thread::sleep_for(std::chrono::seconds(SCHEDULE_SECONDS));
86 }
87 });
88 // LCOV_EXCL_STOP
89 }
90
~SessionChangeRecorder()91 SessionChangeRecorder::~SessionChangeRecorder()
92 {
93 TLOGD(WmsLogTag::DEFAULT, "In");
94 stopLogFlag_.store(true);
95 if (mThread.joinable()) {
96 mThread.join();
97 }
98 }
99
100 // LCOV_EXCL_START
RecordSceneSessionChange(RecordType recordType,SceneSessionChangeInfo & changeInfo)101 WSError SessionChangeRecorder::RecordSceneSessionChange(RecordType recordType, SceneSessionChangeInfo& changeInfo)
102 {
103 TLOGD(WmsLogTag::DEFAULT, "In");
104 if (changeInfo.logTag_ == WmsLogTag::DEFAULT || changeInfo.logTag_ >= WmsLogTag::END ||
105 changeInfo.changeInfo_ == "") {
106 TLOGD(WmsLogTag::DEFAULT, "Invalid log tag");
107 return WSError::WS_ERROR_INVALID_PARAM;
108 }
109 if (changeInfo.time_ == "") {
110 changeInfo.time_ = GetFormattedTime();
111 }
112 RecordLog(recordType, changeInfo);
113 RecordDump(recordType, changeInfo);
114 return WSError::WS_OK;
115 }
116
SetRecordSize(RecordType recordType,uint32_t recordSize)117 WSError SessionChangeRecorder::SetRecordSize(RecordType recordType, uint32_t recordSize)
118 {
119 TLOGD(WmsLogTag::DEFAULT, "recordType: %{public}" PRIu32 ", size: %{public}d", recordType, recordSize);
120 if (recordSize <= 0 || recordSize > MAX_RECORD_TYPE_SIZE) {
121 return WSError::WS_ERROR_INVALID_PARAM;
122 }
123 {
124 std::lock_guard<std::mutex> lock(sessionChangeRecorderMutex_);
125 recordSizeMap_[recordType] = recordSize;
126 }
127 return WSError::WS_OK;
128 }
129
GetSceneSessionNeedDumpInfo(const std::vector<std::string> & dumpParams,std::string & dumpInfo)130 void SessionChangeRecorder::GetSceneSessionNeedDumpInfo(
131 const std::vector<std::string>& dumpParams, std::string& dumpInfo)
132 {
133 std::ostringstream oss;
134 oss << "Record session change: " << std::endl;
135 bool simplifyFlag = false;
136 std::vector<std::string> params = dumpParams;
137 auto it = std::find(params.begin(), params.end(), "-simplify");
138 if (it != params.end()) {
139 simplifyFlag = true;
140 params.erase(it);
141 }
142 int32_t specifiedWindowId = INVALID_SESSION_ID;
143 if (params.size() >= 1 && params[0] == "all") {
144 specifiedWindowId = INVALID_SESSION_ID;
145 } else if (params.size() >= 1 && WindowHelper::IsNumber(params[0])) {
146 const std::string& str = params[0];
147 int32_t value;
148 auto res = std::from_chars(str.data(), str.data() + str.size(), value);
149 if (res.ec == std::errc()) {
150 specifiedWindowId = value;
151 } else {
152 oss << "Available args: '-v all/[specified window id]'" << std::endl;
153 dumpInfo.append(oss.str());
154 return;
155 }
156 } else {
157 oss << "Available args: '-v all/[specified window id]'" << std::endl;
158 dumpInfo.append(oss.str());
159 return;
160 }
161 uint32_t specifiedRecordType = static_cast<uint32_t>(RecordType::RECORD_TYPE_BEGIN);
162 if (params.size() >= 2 && WindowHelper::IsNumber(params[1])) { // 2: params num
163 const std::string& str = params[1];
164 uint32_t value;
165 auto res = std::from_chars(str.data(), str.data() + str.size(), value);
166 if (res.ec == std::errc()) {
167 specifiedRecordType = value;
168 }
169 }
170 std::unordered_map<RecordType, std::queue<SceneSessionChangeInfo>> sceneSessionChangeNeedDumpMapCopy;
171 {
172 std::lock_guard<std::mutex> lock(sessionChangeRecorderMutex_);
173 sceneSessionChangeNeedDumpMapCopy = sceneSessionChangeNeedDumpMap_;
174 }
175 std::string dumpInfoJsonString = FormatDumpInfoToJsonString(specifiedRecordType, specifiedWindowId,
176 sceneSessionChangeNeedDumpMapCopy);
177 oss << dumpInfoJsonString;
178 if (simplifyFlag && oss.str().size() > MAX_EVENT_DUMP_SIZE) {
179 dumpInfo.append("wmsDumpSimplify\n");
180 SimplifyDumpInfo(dumpInfo, oss.str());
181 } else {
182 dumpInfo.append(oss.str());
183 }
184 }
185
FormatDumpInfoToJsonString(uint32_t specifiedRecordType,int32_t specifiedWindowId,std::unordered_map<RecordType,std::queue<SceneSessionChangeInfo>> & dumpMap)186 std::string SessionChangeRecorder::FormatDumpInfoToJsonString (uint32_t specifiedRecordType, int32_t specifiedWindowId,
187 std::unordered_map<RecordType, std::queue<SceneSessionChangeInfo>>& dumpMap)
188 {
189 nlohmann::json jsonArrayDump = nlohmann::json::array();
190 for (const auto& elem : dumpMap) {
191 if (specifiedRecordType && static_cast<uint32_t>(elem.first) != specifiedRecordType) {
192 continue;
193 }
194 std::queue<SceneSessionChangeInfo> tempDumpQueue = elem.second;
195 while (!tempDumpQueue.empty()) {
196 auto tempQueue = tempDumpQueue.front();
197 if (specifiedWindowId && tempQueue.persistentId_ != specifiedWindowId) {
198 tempDumpQueue.pop();
199 continue;
200 }
201 jsonArrayDump.push_back({{"winId", tempQueue.persistentId_},
202 {"changeInfo", tempQueue.changeInfo_}, {"time", tempQueue.time_}});
203 tempDumpQueue.pop();
204 }
205 }
206 return jsonArrayDump.dump();
207 }
208
SimplifyDumpInfo(std::string & dumpInfo,std::string preCompressInfo)209 void SessionChangeRecorder::SimplifyDumpInfo(std::string& dumpInfo, std::string preCompressInfo)
210 {
211 std::unique_ptr<std::ostringstream> compressOstream = std::make_unique<std::ostringstream>();
212 std::string compressInfo;
213 if (CompressString(preCompressInfo.c_str(), preCompressInfo.size(), compressInfo, COMPRESS_VERSION) == Z_OK) {
214 compressOstream->write(compressInfo.c_str(), compressInfo.length());
215 } else {
216 compressOstream->write(preCompressInfo.c_str(), preCompressInfo.length());
217 }
218 preCompressInfo.clear();
219 compressOstream->flush();
220
221 dumpInfo.append(compressOstream->str());
222 }
CompressString(const char * inStr,size_t inLen,std::string & outStr,int level)223 int SessionChangeRecorder::CompressString (const char* inStr, size_t inLen, std::string& outStr, int level)
224 {
225 if (!inStr) {
226 return Z_DATA_ERROR;
227 }
228
229 int ret;
230 int flush;
231 unsigned have;
232 z_stream strm;
233
234 unsigned char out[CHUNK];
235
236 /* allocate deflate state */
237 strm.zalloc = Z_NULL;
238 strm.zfree = Z_NULL;
239 strm.opaque = Z_NULL;
240 ret = deflateInit(&strm, level);
241 if (ret != Z_OK)
242 return ret;
243
244 std::shared_ptr<z_stream> sp_strm(&strm, [](z_stream* strm) { (void)deflateEnd(strm); });
245 const char* end = inStr + inLen;
246
247 size_t distance = 0;
248 /* compress until end of file */
249 do {
250 distance = end - inStr;
251 strm.avail_in = (distance >= CHUNK) ? CHUNK : distance;
252 strm.next_in = (Bytef*)inStr;
253 inStr += strm.avail_in;
254 flush = (inStr == end) ? Z_FINISH : Z_NO_FLUSH;
255 do {
256 strm.avail_out = CHUNK;
257 strm.next_out = out;
258 ret = deflate(&strm, flush);
259 if (ret == Z_STREAM_ERROR)
260 break;
261 have = CHUNK - strm.avail_out;
262 outStr.append((const char*)out, have);
263 } while (strm.avail_out == 0);
264 } while (flush != Z_FINISH || strm.avail_in != 0);
265 if (ret != Z_STREAM_END) {
266 return Z_STREAM_ERROR;
267 }
268 return Z_OK;
269 }
270
RecordDump(RecordType recordType,SceneSessionChangeInfo & changeInfo)271 void SessionChangeRecorder::RecordDump(RecordType recordType, SceneSessionChangeInfo& changeInfo)
272 {
273 TLOGD(WmsLogTag::DEFAULT, "In");
274 uint32_t maxRecordTypeSize = MAX_RECORD_TYPE_SIZE;
275 uint32_t currentRecordTypeSize = MAX_RECORD_TYPE_SIZE;
276 {
277 std::lock_guard<std::mutex> lock(sessionChangeRecorderMutex_);
278 if (sceneSessionChangeNeedDumpMap_.find(recordType) == sceneSessionChangeNeedDumpMap_.end()) {
279 std::queue<SceneSessionChangeInfo> dumpQueue;
280 dumpQueue.push(changeInfo);
281 sceneSessionChangeNeedDumpMap_[recordType] = dumpQueue;
282 } else {
283 sceneSessionChangeNeedDumpMap_[recordType].push(changeInfo);
284 }
285 maxRecordTypeSize = recordSizeMap_.find(recordType) != recordSizeMap_.end() ?
286 recordSizeMap_[recordType] : MAX_RECORD_TYPE_SIZE;
287 currentRecordTypeSize = sceneSessionChangeNeedDumpMap_[recordType].size();
288 }
289
290 if (currentRecordTypeSize > maxRecordTypeSize) {
291 std::lock_guard<std::mutex> lock(sessionChangeRecorderMutex_);
292 uint32_t diff = sceneSessionChangeNeedDumpMap_[recordType].size() - maxRecordTypeSize;
293 while (diff > 0) {
294 sceneSessionChangeNeedDumpMap_[recordType].pop();
295 diff--;
296 }
297 }
298 }
299
RecordLog(RecordType recordType,SceneSessionChangeInfo & changeInfo)300 void SessionChangeRecorder::RecordLog(RecordType recordType, SceneSessionChangeInfo& changeInfo)
301 {
302 TLOGD(WmsLogTag::DEFAULT, "In");
303 uint32_t maxRecordTypeSize = MAX_RECORD_TYPE_SIZE;
304 uint32_t currentRecordTypeSize = MAX_RECORD_TYPE_SIZE;
305 {
306 std::lock_guard<std::mutex> lock(sessionChangeRecorderMutex_);
307 if (sceneSessionChangeNeedLogMap_.find(recordType) == sceneSessionChangeNeedLogMap_.end()) {
308 std::queue<SceneSessionChangeInfo> logQueue;
309 logQueue.push(changeInfo);
310 sceneSessionChangeNeedLogMap_[recordType] = logQueue;
311 } else {
312 sceneSessionChangeNeedLogMap_[recordType].push(changeInfo);
313 }
314 currentLogSize_ += changeInfo.changeInfo_.size();
315 maxRecordTypeSize = recordSizeMap_.find(recordType) != recordSizeMap_.end() ?
316 recordSizeMap_[recordType] : MAX_RECORD_TYPE_SIZE;
317 currentRecordTypeSize = sceneSessionChangeNeedLogMap_[recordType].size();
318 }
319 if (currentLogSize_ >= MAX_RECORD_LOG_SIZE || currentRecordTypeSize > maxRecordTypeSize) {
320 TLOGD(WmsLogTag::DEFAULT, "currentLogSize: %{public}d", currentLogSize_);
321 std::unordered_map<RecordType, std::queue<SceneSessionChangeInfo>> sceneSessionChangeNeedLogMapCopy;
322 {
323 std::lock_guard<std::mutex> lock(sessionChangeRecorderMutex_);
324 sceneSessionChangeNeedLogMapCopy = sceneSessionChangeNeedLogMap_;
325 sceneSessionChangeNeedLogMap_.clear();
326 currentLogSize_ = 0;
327 }
328 PrintLog(sceneSessionChangeNeedLogMapCopy);
329 }
330 }
331
PrintLog(std::unordered_map<RecordType,std::queue<SceneSessionChangeInfo>> sceneSessionChangeNeedLogMap)332 void SessionChangeRecorder::PrintLog(
333 std::unordered_map<RecordType, std::queue<SceneSessionChangeInfo>> sceneSessionChangeNeedLogMap)
334 {
335 TLOGD(WmsLogTag::DEFAULT, "In");
336 for (const auto& item : sceneSessionChangeNeedLogMap) {
337 std::queue<SceneSessionChangeInfo> logInfoQueue = item.second;
338 while (!logInfoQueue.empty()) {
339 SceneSessionChangeInfo curChange = logInfoQueue.front();
340 TLOGD(curChange.logTag_, "winId: %{public}d, changeInfo: %{public}s, time: %{public}s",
341 curChange.persistentId_, curChange.changeInfo_.c_str(), curChange.time_.c_str());
342 logInfoQueue.pop();
343 }
344 }
345 }
346 // LCOV_EXCL_STOP
347 } // namespace OHOS::Rosen