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 #include "io_message_listener.h"
16
17 #include "utils_log.h"
18
19 using namespace std;
20 using namespace chrono;
21
22 namespace OHOS {
23 namespace FileManagement {
24 namespace CloudDisk {
25
26 const string DESK_BUNDLE_NAME = "com.ohos.sceneboard";
27 const int32_t GET_FREQUENCY = 5;
28 const int32_t READ_THRESHOLD = 1000;
29 const int32_t OPEN_THRESHOLD = 1000;
30 const int32_t STAT_THRESHOLD = 1000;
31 const string IO_DATA_FILE_PATH = "/data/service/el1/public/cloudfile/rdb/io_message.csv";
32 const int32_t TYPE_FRONT = 2;
33 const int32_t TYPE_BACKGROUND = 4;
34
GetInstance()35 IoMessageManager &IoMessageManager::GetInstance()
36 {
37 static IoMessageManager instance;
38 return instance;
39 }
40
ReadIoDataFromFile(const std::string & path)41 bool IoMessageManager::ReadIoDataFromFile(const std::string &path)
42 {
43 ifstream inFile(path);
44 if (!inFile.is_open()) {
45 LOGE("Failed to open io, err: %{public}d", errno);
46 return false;
47 }
48
49 string line;
50 while (getline(inFile, line)) {
51 size_t colon = line.find(':');
52 if (colon == string::npos) {
53 continue;
54 }
55
56 string key = line.substr(0, colon);
57 string value = line.substr(colon + 1);
58
59 try {
60 if (key == "rchar") currentData.rchar = stol(value);
61 if (key == "syscr") currentData.syscr = stol(value);
62 if (key == "read_bytes") currentData.readBytes = stol(value);
63 if (key == "syscopen") currentData.syscopen = stol(value);
64 if (key == "syscstat") currentData.syscstat = stol(value);
65 } catch (const invalid_argument& e) {
66 LOGE("Invalid argument: %{public}s", e.what());
67 currentData = preData;
68 return false;
69 } catch (const out_of_range& e) {
70 LOGE("Out of range: %{public}s", e.what());
71 currentData = preData;
72 return false;
73 }
74 }
75 return true;
76 }
77
IsFirstLineHeader(const string & path)78 bool IoMessageManager::IsFirstLineHeader(const string &path)
79 {
80 ifstream inFile(path);
81 if (!inFile.is_open()) {
82 return false;
83 }
84
85 string firstLine;
86 getline(inFile, firstLine);
87 return firstLine.find("time") != string::npos;
88 }
89
RecordDataToFile(const string & path)90 void IoMessageManager::RecordDataToFile(const string &path)
91 {
92 ofstream outFile(path, ios::app);
93 if (!outFile) {
94 LOGE("Failed to open io file to write, err: %{public}d", errno);
95 return;
96 }
97
98 if (!IsFirstLineHeader(path)) {
99 outFile << "time, bundle_name, rchar_diff, syscr_diff, read_bytes_diff, syscopen_diff, syscstat_diff, result\n";
100 }
101
102 outFile << bundleTimeMap[dataToWrite.bundleName] << ","
103 << dataToWrite.bundleName << ","
104 << dataToWrite.rcharDiff << ","
105 << dataToWrite.syscrDiff << ","
106 << dataToWrite.readBytesDiff << ","
107 << dataToWrite.syscopenDiff << ","
108 << dataToWrite.syscstatDiff << ","
109 << dataToWrite.result << "\n";
110 LOGI("Write io data success");
111 }
112
ProcessIoData(const string & path)113 void IoMessageManager::ProcessIoData(const string &path)
114 {
115 if (currentBundleName != lastestAppStateData.bundleName) {
116 bundleTimeMap[lastestAppStateData.bundleName] = 0;
117 }
118
119 if (!ReadIoDataFromFile(path)) {
120 return;
121 }
122
123 if (preData.rchar == 0) {
124 preData = currentData;
125 return;
126 }
127
128 currentBundleName = lastestAppStateData.bundleName;
129 dataToWrite.bundleName = lastestAppStateData.bundleName;
130 dataToWrite.rcharDiff = currentData.rchar - preData.rchar;
131 dataToWrite.syscrDiff = currentData.syscr - preData.syscr;
132 dataToWrite.readBytesDiff = currentData.readBytes - preData.readBytes;
133 dataToWrite.syscopenDiff = currentData.syscopen - preData.syscopen;
134 dataToWrite.syscstatDiff = currentData.syscstat - preData.syscstat;
135
136 /* Prevent division by zero */
137 if (dataToWrite.rcharDiff + 1 != 0) {
138 dataToWrite.result = (dataToWrite.readBytesDiff) / (dataToWrite.rcharDiff + 1) * dataToWrite.syscrDiff;
139 }
140
141 if (dataToWrite.result >= READ_THRESHOLD ||
142 dataToWrite.syscopenDiff >= OPEN_THRESHOLD ||
143 dataToWrite.syscstatDiff >= STAT_THRESHOLD) {
144 RecordDataToFile(IO_DATA_FILE_PATH);
145 }
146
147 preData = currentData;
148 bundleTimeMap[lastestAppStateData.bundleName] += GET_FREQUENCY;
149 }
150
RecordIoData()151 void IoMessageManager::RecordIoData()
152 {
153 while (isThreadRunning.load()) {
154 if (!lastestAppStateData.bundleName.empty()) {
155 string path = "/proc/" + to_string(lastestAppStateData.pid) + "/sys_count";
156 ProcessIoData(path);
157 }
158 unique_lock<mutex> lock(sleepMutex);
159 sleepCv.wait_for(lock, chrono::seconds(GET_FREQUENCY), [&] {
160 return !isThreadRunning.load();
161 });
162 }
163 }
164
OnReceiveEvent(const AppExecFwk::AppStateData & appStateData)165 void IoMessageManager::OnReceiveEvent(const AppExecFwk::AppStateData &appStateData)
166 {
167 if (appStateData.bundleName != DESK_BUNDLE_NAME) {
168 if (appStateData.state == TYPE_FRONT) {
169 lastestAppStateData = appStateData;
170 if (!ioThread.joinable()) {
171 isThreadRunning.store(true);
172 ioThread = thread(&IoMessageManager::RecordIoData, this);
173 }
174 return;
175 }
176 if (appStateData.state == TYPE_BACKGROUND) {
177 if (ioThread.joinable()) {
178 lock_guard<mutex> lock(cvMute);
179 isThreadRunning.store(false);
180 sleepCv.notify_all();
181 ioThread.join();
182 ioThread = thread();
183 currentData = {};
184 preData = {};
185 }
186 }
187 }
188 }
189 } // namespace CloudDisk
190 } // namespace FileManagement
191 } // namespace OHOS