• 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 "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