• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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