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