1 /*
2  * Copyright (C) 2023-2024 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 "common_utils.h"
20 #include "file_util.h"
21 #include "hiview_logger.h"
22 #include "time_util.h"
23 #include "unified_collection_data.h"
24 
25 namespace OHOS {
26 namespace HiviewDFX {
27 namespace UCollectUtil {
28 DEFINE_LOG_TAG("ProcessStatus");
29 namespace {
30 constexpr uint64_t INVALID_LAST_FOREGROUND_TIME = 0;
31 }
32 
GetProcessName(int32_t pid)33 std::string ProcessStatus::GetProcessName(int32_t pid)
34 {
35     std::unique_lock<std::mutex> lock(mutex_);
36     // the cleanup judgment is triggered each time
37     if (NeedClearProcessInfos()) {
38         ClearProcessInfos();
39     }
40 
41     if (processInfos_.find(pid) != processInfos_.end() && !processInfos_[pid].name.empty()) {
42         return processInfos_[pid].name;
43     }
44     std::string procName = CommonUtils::GetProcFullNameByPid(pid);
45     if (UpdateProcessName(pid, procName)) {
46         return procName;
47     }
48     HIVIEW_LOGD("failed to get proc name from pid=%{public}d", pid);
49     return "";
50 }
51 
NeedClearProcessInfos()52 bool ProcessStatus::NeedClearProcessInfos()
53 {
54     if (processInfos_.size() <= capacity_) {
55         return false;
56     }
57     static uint64_t lastClearTime = 0;
58     uint64_t now = TimeUtil::GetSteadyClockTimeMs();
59     uint64_t interval = now > lastClearTime ? (now - lastClearTime) : 0;
60     constexpr uint32_t clearInterval = 600 * 1000; // 10min
61     if (interval <= clearInterval) {
62         return false;
63     }
64     lastClearTime = now;
65     return true;
66 }
67 
ClearProcessInfos()68 void ProcessStatus::ClearProcessInfos()
69 {
70     HIVIEW_LOGI("start to clear process cache, size=%{public}zu, capacity=%{public}u", processInfos_.size(), capacity_);
71     for (auto it = processInfos_.begin(); it != processInfos_.end();) {
72         if (!CommonUtils::IsPidExist(it->first) || CommonUtils::GetProcFullNameByPid(it->first) != it->second.name) {
73             it = processInfos_.erase(it);
74             continue;
75         }
76         it++;
77     }
78     constexpr uint32_t reservedNum = 100;
79     capacity_ = processInfos_.size() + reservedNum;
80     HIVIEW_LOGI("end to clear process cache, size=%{public}zu, capacity=%{public}u", processInfos_.size(), capacity_);
81 }
82 
UpdateProcessName(int32_t pid,const std::string & procName)83 bool ProcessStatus::UpdateProcessName(int32_t pid, const std::string& procName)
84 {
85     if (procName.empty()) {
86         return false;
87     }
88 
89     if (processInfos_.find(pid) != processInfos_.end()) {
90         processInfos_[pid].name = procName;
91         return true;
92     }
93     processInfos_[pid] = {
94         .name = procName,
95         .state = BACKGROUND,
96         .lastForegroundTime = INVALID_LAST_FOREGROUND_TIME,
97     };
98     return true;
99 }
100 
GetProcessState(int32_t pid)101 ProcessState ProcessStatus::GetProcessState(int32_t pid)
102 {
103     std::unique_lock<std::mutex> lock(mutex_);
104     return (processInfos_.find(pid) != processInfos_.end())
105         ? processInfos_[pid].state
106         : BACKGROUND;
107 }
108 
GetProcessLastForegroundTime(int32_t pid)109 uint64_t ProcessStatus::GetProcessLastForegroundTime(int32_t pid)
110 {
111     std::unique_lock<std::mutex> lock(mutex_);
112     return (processInfos_.find(pid) != processInfos_.end())
113         ? processInfos_[pid].lastForegroundTime
114         : INVALID_LAST_FOREGROUND_TIME;
115 }
116 
NotifyProcessState(int32_t pid,ProcessState procState)117 void ProcessStatus::NotifyProcessState(int32_t pid, ProcessState procState)
118 {
119     std::unique_lock<std::mutex> lock(mutex_);
120     UpdateProcessState(pid, procState);
121 }
122 
UpdateProcessState(int32_t pid,ProcessState procState)123 void ProcessStatus::UpdateProcessState(int32_t pid, ProcessState procState)
124 {
125     HIVIEW_LOGD("pid=%{public}d state=%{public}d", pid, procState);
126     switch (procState) {
127         case FOREGROUND:
128             UpdateProcessForegroundState(pid);
129             break;
130         case BACKGROUND:
131             UpdateProcessBackgroundState(pid);
132             break;
133         case CREATED:
134             ClearProcessInfo(pid);
135             break;
136         case DIED:
137             ClearProcessInfo(pid);
138             break;
139         default:
140             HIVIEW_LOGW("invalid process=%{public}d state=%{public}d", pid, procState);
141     }
142 }
143 
UpdateProcessForegroundState(int32_t pid)144 void ProcessStatus::UpdateProcessForegroundState(int32_t pid)
145 {
146     HIVIEW_LOGI("pid=%{public}d state=FOREGROUND", pid);
147     uint64_t nowTime = TimeUtil::GetMilliseconds();
148     if (processInfos_.find(pid) != processInfos_.end()) {
149         processInfos_[pid].state = FOREGROUND;
150         processInfos_[pid].lastForegroundTime = nowTime;
151         return;
152     }
153     processInfos_[pid] = {
154         .name = CommonUtils::GetProcFullNameByPid(pid),
155         .state = FOREGROUND,
156         .lastForegroundTime = nowTime,
157     };
158 }
159 
UpdateProcessBackgroundState(int32_t pid)160 void ProcessStatus::UpdateProcessBackgroundState(int32_t pid)
161 {
162     HIVIEW_LOGI("pid=%{public}d state=BACKGROUND", pid);
163     if (processInfos_.find(pid) != processInfos_.end()) {
164         // last foreground time needs to be updated when the foreground status is switched to the background
165         if (processInfos_[pid].state == FOREGROUND) {
166             processInfos_[pid].lastForegroundTime = TimeUtil::GetMilliseconds();
167         }
168         processInfos_[pid].state = BACKGROUND;
169         return;
170     }
171     processInfos_[pid] = {
172         .name = CommonUtils::GetProcFullNameByPid(pid),
173         .state = BACKGROUND,
174         .lastForegroundTime = INVALID_LAST_FOREGROUND_TIME,
175     };
176 }
177 
ClearProcessInfo(int32_t pid)178 void ProcessStatus::ClearProcessInfo(int32_t pid)
179 {
180     if (processInfos_.find(pid) != processInfos_.end()) {
181         processInfos_.erase(pid);
182         HIVIEW_LOGD("end to clear process cache, pid=%{public}d", pid);
183     }
184 }
185 } // UCollectUtil
186 }  // namespace HiviewDFX
187 }  // namespace OHOS
188