1 /*
2 * Copyright (C) 2023 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 "process_status.h"
16
17 #include <unordered_map>
18
19 #include "file_util.h"
20 #include "logger.h"
21 #include "time_util.h"
22 #include "unified_collection_data.h"
23
24 namespace OHOS {
25 namespace HiviewDFX {
26 namespace UCollectUtil {
27 DEFINE_LOG_TAG("UCollectUtil-ProcessStatus");
28 namespace {
29 constexpr uint64_t INVALID_LAST_FOREGROUND_TIME = 0;
30
IsValidProcessId(int32_t pid)31 bool IsValidProcessId(int32_t pid)
32 {
33 std::string procDir = "/proc/" + std::to_string(pid);
34 return FileUtil::IsDirectory(procDir);
35 }
36
GetProcessNameFromProcCmdline(int32_t pid)37 std::string GetProcessNameFromProcCmdline(int32_t pid)
38 {
39 std::string procCmdlinePath = "/proc/" + std::to_string(pid) + "/cmdline";
40 std::string procCmdlineContent = FileUtil::GetFirstLine(procCmdlinePath);
41 if (procCmdlineContent.empty()) {
42 return "";
43 }
44
45 size_t procNameStartPos = 0;
46 size_t procNameEndPos = procCmdlineContent.size();
47 for (size_t i = 0; i < procCmdlineContent.size(); i++) {
48 if (procCmdlineContent[i] == '/') {
49 // for the format '/system/bin/hiview' of the cmdline file
50 procNameStartPos = i + 1; // 1 for next char
51 } else if (procCmdlineContent[i] == '\0') {
52 // for the format 'hiview \0 3 \0 hiview' of the cmdline file
53 procNameEndPos = i;
54 break;
55 }
56 }
57 return procCmdlineContent.substr(procNameStartPos, procNameEndPos - procNameStartPos);
58 }
59
GetProcessNameFromProcStat(int32_t pid)60 std::string GetProcessNameFromProcStat(int32_t pid)
61 {
62 std::string procStatFilePath = "/proc/" + std::to_string(pid) + "/stat";
63 std::string procStatFileContent = FileUtil::GetFirstLine(procStatFilePath);
64 if (procStatFileContent.empty()) {
65 return "";
66 }
67 // for the format '40 (hiview) I ...'
68 auto procNameStartPos = procStatFileContent.find('(') + 1; // 1: for '(' next char
69 if (procNameStartPos == std::string::npos) {
70 return "";
71 }
72 auto procNameEndPos = procStatFileContent.find(')');
73 if (procNameEndPos == std::string::npos) {
74 return "";
75 }
76 if (procNameEndPos <= procNameStartPos) {
77 return "";
78 }
79 return procStatFileContent.substr(procNameStartPos, procNameEndPos - procNameStartPos);
80 }
81 }
82
GetProcessName(int32_t pid)83 std::string ProcessStatus::GetProcessName(int32_t pid)
84 {
85 std::unique_lock<std::mutex> lock(mutex_);
86 // the cleanup judgment is triggered each time
87 if (NeedClearProcessInfos()) {
88 ClearProcessInfos();
89 }
90
91 if (processInfos_.find(pid) != processInfos_.end() && !processInfos_[pid].name.empty()) {
92 return processInfos_[pid].name;
93 }
94 std::string procName = GetProcessNameFromProcCmdline(pid);
95 if (UpdateProcessName(pid, procName)) {
96 return procName;
97 }
98 procName = GetProcessNameFromProcStat(pid);
99 if (UpdateProcessName(pid, procName)) {
100 return procName;
101 }
102 HIVIEW_LOGW("failed to get proc name from pid=%{public}d", pid);
103 return "";
104 }
105
NeedClearProcessInfos()106 bool ProcessStatus::NeedClearProcessInfos()
107 {
108 return processInfos_.size() > PROCESS_TOTAL_COUNT;
109 }
110
ClearProcessInfos()111 void ProcessStatus::ClearProcessInfos()
112 {
113 HIVIEW_LOGI("start to clear process cache, size=%{public}zu", processInfos_.size());
114 for (auto it = processInfos_.begin(); it != processInfos_.end();) {
115 if (!IsValidProcessId(it->first)) {
116 it = processInfos_.erase(it);
117 } else {
118 it++;
119 }
120 }
121 HIVIEW_LOGI("end to clear process cache, size=%{public}zu", processInfos_.size());
122 }
123
UpdateProcessName(int32_t pid,const std::string & procName)124 bool ProcessStatus::UpdateProcessName(int32_t pid, const std::string& procName)
125 {
126 if (procName.empty()) {
127 return false;
128 }
129
130 if (processInfos_.find(pid) != processInfos_.end()) {
131 processInfos_[pid].name = procName;
132 return true;
133 }
134 processInfos_[pid] = {
135 .name = procName,
136 .state = BACKGROUND,
137 .lastForegroundTime = INVALID_LAST_FOREGROUND_TIME,
138 };
139 return true;
140 }
141
GetProcessState(int32_t pid)142 ProcessState ProcessStatus::GetProcessState(int32_t pid)
143 {
144 std::unique_lock<std::mutex> lock(mutex_);
145 return (processInfos_.find(pid) != processInfos_.end())
146 ? processInfos_[pid].state
147 : BACKGROUND;
148 }
149
GetProcessLastForegroundTime(int32_t pid)150 uint64_t ProcessStatus::GetProcessLastForegroundTime(int32_t pid)
151 {
152 std::unique_lock<std::mutex> lock(mutex_);
153 return (processInfos_.find(pid) != processInfos_.end())
154 ? processInfos_[pid].lastForegroundTime
155 : INVALID_LAST_FOREGROUND_TIME;
156 }
157
NotifyProcessState(int32_t pid,ProcessState procState)158 void ProcessStatus::NotifyProcessState(int32_t pid, ProcessState procState)
159 {
160 std::unique_lock<std::mutex> lock(mutex_);
161 UpdateProcessState(pid, procState);
162 }
163
UpdateProcessState(int32_t pid,ProcessState procState)164 void ProcessStatus::UpdateProcessState(int32_t pid, ProcessState procState)
165 {
166 HIVIEW_LOGI("update process=%{public}d state=%{public}d", pid, procState);
167 switch (procState) {
168 case FOREGROUND:
169 UpdateProcessForegroundState(pid);
170 break;
171 case BACKGROUND:
172 UpdateProcessBackgroundState(pid);
173 break;
174 default:
175 HIVIEW_LOGW("invalid process=%{public}d state=%{public}d", pid, procState);
176 }
177 }
178
UpdateProcessForegroundState(int32_t pid)179 void ProcessStatus::UpdateProcessForegroundState(int32_t pid)
180 {
181 uint64_t nowTime = TimeUtil::GetMilliseconds();
182 if (processInfos_.find(pid) != processInfos_.end()) {
183 processInfos_[pid].state = FOREGROUND;
184 processInfos_[pid].lastForegroundTime = nowTime;
185 return;
186 }
187 processInfos_[pid] = {
188 .name = "",
189 .state = FOREGROUND,
190 .lastForegroundTime = nowTime,
191 };
192 }
193
UpdateProcessBackgroundState(int32_t pid)194 void ProcessStatus::UpdateProcessBackgroundState(int32_t pid)
195 {
196 if (processInfos_.find(pid) != processInfos_.end()) {
197 // last foreground time needs to be updated when the foreground status is switched to the background
198 if (processInfos_[pid].state == FOREGROUND) {
199 processInfos_[pid].lastForegroundTime = TimeUtil::GetMilliseconds();
200 }
201 processInfos_[pid].state = BACKGROUND;
202 return;
203 }
204 processInfos_[pid] = {
205 .name = "",
206 .state = BACKGROUND,
207 .lastForegroundTime = INVALID_LAST_FOREGROUND_TIME,
208 };
209 }
210 } // UCollectUtil
211 } // namespace HiviewDFX
212 } // namespace OHOS
213