1 /*
2 * Copyright (C) 2021 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 "peer_binder_catcher.h"
16
17 #include <ctime>
18 #include <cstdio>
19 #include <cstdlib>
20
21 #include <sys/wait.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24
25 #include <securec.h>
26
27 #include "common_utils.h"
28 #include "file_util.h"
29 #include "log_catcher_utils.h"
30 #include "logger.h"
31 #include "parameter_ex.h"
32 #include "string_util.h"
33
34 #include "open_stacktrace_catcher.h"
35 namespace OHOS {
36 namespace HiviewDFX {
37 DEFINE_LOG_LABEL(0xD002D01, "EventLogger-PeerBinderCatcher");
38 using namespace Developtools::HiPerf;
39 constexpr char EVENT_LOG_PATH[] = "/data/log/eventlog";
PeerBinderCatcher()40 PeerBinderCatcher::PeerBinderCatcher() : EventLogCatcher()
41 {
42 name_ = "PeerBinderCatcher";
43 }
44
Initialize(const std::string & perfCmd,int layer,int pid)45 bool PeerBinderCatcher::Initialize(const std::string& perfCmd, int layer, int pid)
46 {
47 pid_ = pid;
48 layer_ = layer;
49 perfCmd_ = perfCmd;
50 char buf[BUF_SIZE_512] = {0};
51 int ret = snprintf_s(buf, BUF_SIZE_512, BUF_SIZE_512 - 1,
52 "PeerBinderCatcher -- pid==%d layer_ == %d\n", pid_, layer_);
53 if (ret > 0) {
54 description_ = buf;
55 }
56 return true;
57 }
58
Init(std::shared_ptr<SysEvent> event,const std::string & filePath)59 void PeerBinderCatcher::Init(std::shared_ptr<SysEvent> event, const std::string& filePath)
60 {
61 event_ = event;
62 if ((filePath != "") && FileUtil::FileExists(filePath)) {
63 binderPath_ = filePath;
64 }
65 }
66
Catch(int fd)67 int PeerBinderCatcher::Catch(int fd)
68 {
69 if (pid_ <= 0) {
70 return -1;
71 }
72
73 auto originSize = GetFdSize(fd);
74
75 if (!FileUtil::FileExists(binderPath_)) {
76 std::string content = binderPath_ + " : file isn't exists\r\n";
77 FileUtil::SaveStringToFd(fd, content);
78 return -1;
79 }
80
81 std::set<int> pids = GetBinderPeerPids(fd);
82 if (pids.empty()) {
83 std::string content = "PeerBinder pids is empty\r\n";
84 FileUtil::SaveStringToFd(fd, content);
85 }
86
87 ForkToDumpHiperf(pids);
88 std::string pidStr = "";
89 for (auto pidTemp : pids) {
90 if (pidTemp != pid_) {
91 CatcherStacktrace(fd, pidTemp);
92 pidStr += "," + std::to_string(pidTemp);
93 }
94 }
95
96 if (event_ != nullptr) {
97 event_->SetValue(LOGGER_EVENT_PEERBINDER, StringUtil::TrimStr(pidStr, ','));
98 }
99
100 logSize_ = GetFdSize(fd) - originSize;
101 return logSize_;
102 }
103
BinderInfoParser(std::ifstream & fin,int fd) const104 std::map<int, std::list<PeerBinderCatcher::BinderInfo>> PeerBinderCatcher::BinderInfoParser(
105 std::ifstream& fin, int fd) const
106 {
107 std::map<int, std::list<BinderInfo>> manager;
108 const int DECIMAL = 10;
109 std::string line;
110 bool findBinderHeader = false;
111 FileUtil::SaveStringToFd(fd, "\nBinderCatcher --\n\n");
112 while (getline(fin, line)) {
113 FileUtil::SaveStringToFd(fd, line + "\n");
114 if (findBinderHeader) {
115 continue;
116 }
117
118 if (line.find("async") != std::string::npos) {
119 continue;
120 }
121
122 std::istringstream lineStream(line);
123 std::vector<std::string> strList;
124 std::string tmpstr;
125 while (lineStream >> tmpstr) {
126 strList.push_back(tmpstr);
127 }
128
129 auto stringSplit = [](const std::string& str, uint16_t index) -> std::string {
130 std::vector<std::string> strings;
131 StringUtil::SplitStr(str, ":", strings);
132 if (index < strings.size()) {
133 return strings[index];
134 }
135 return "";
136 };
137
138 if (strList.size() == 7) { // 7: valid array size
139 BinderInfo info = {0};
140 // 2: binder peer id,
141 std::string server = stringSplit(strList[2], 0);
142 // 0: binder local id,
143 std::string client = stringSplit(strList[0], 0);
144 // 5: binder wait time, s
145 std::string wait = stringSplit(strList[5], 1);
146 if (server == "" || client == "" || wait == "") {
147 continue;
148 }
149 info.server = std::strtol(server.c_str(), nullptr, DECIMAL);
150 info.client = std::strtol(client.c_str(), nullptr, DECIMAL);
151 info.wait = std::strtol(wait.c_str(), nullptr, DECIMAL);
152 HIVIEW_LOGI("server:%{public}d, client:%{public}d, wait:%{public}d", info.server, info.client, info.wait);
153 manager[info.client].push_back(info);
154 }
155 if (line.find("context") != line.npos) {
156 findBinderHeader = true;
157 }
158 }
159 FileUtil::SaveStringToFd(fd, "\n\nPeerBinder Stacktrace --\n\n");
160 HIVIEW_LOGI("manager size: %{public}zu", manager.size());
161 return manager;
162 }
163
GetBinderPeerPids(int fd) const164 std::set<int> PeerBinderCatcher::GetBinderPeerPids(int fd) const
165 {
166 std::set<int> pids;
167 std::ifstream fin;
168 std::string path = binderPath_;
169 fin.open(path.c_str());
170 if (!fin.is_open()) {
171 HIVIEW_LOGE("open binder file failed, %{public}s.", path.c_str());
172 std::string content = "open binder file failed :" + path + "\r\n";
173 FileUtil::SaveStringToFd(fd, content);
174 return pids;
175 }
176
177 std::map<int, std::list<PeerBinderCatcher::BinderInfo>> manager = BinderInfoParser(fin, fd);
178 fin.close();
179
180 if (manager.size() == 0 || manager.find(pid_) == manager.end()) {
181 return pids;
182 }
183
184 if (layer_ == LOGGER_BINDER_STACK_ONE) {
185 for (auto each : manager[pid_]) {
186 pids.insert(each.server);
187 }
188 } else if (layer_ == LOGGER_BINDER_STACK_ALL) {
189 ParseBinderCallChain(manager, pids, pid_);
190 }
191 return pids;
192 }
193
ParseBinderCallChain(std::map<int,std::list<PeerBinderCatcher::BinderInfo>> & manager,std::set<int> & pids,int pid) const194 void PeerBinderCatcher::ParseBinderCallChain(std::map<int, std::list<PeerBinderCatcher::BinderInfo>>& manager,
195 std::set<int>& pids, int pid) const
196 {
197 for (auto& each : manager[pid]) {
198 if (pids.find(each.server) != pids.end()) {
199 continue;
200 }
201 pids.insert(each.server);
202 ParseBinderCallChain(manager, pids, each.server);
203 }
204 }
205
CatcherStacktrace(int fd,int pid) const206 void PeerBinderCatcher::CatcherStacktrace(int fd, int pid) const
207 {
208 std::string content = "PeerBinderCatcher start catcher stacktrace for pid : " + std::to_string(pid) + "\r\n";
209 FileUtil::SaveStringToFd(fd, content);
210
211 LogCatcherUtils::DumpStacktrace(fd, pid);
212 }
213
DoExecHiperf(const std::string & fileName,const std::set<int> & pids)214 void PeerBinderCatcher::DoExecHiperf(const std::string& fileName, const std::set<int>& pids)
215 {
216 HiperfClient::RecordOption opt;
217 opt.SetOutputFilename(fileName);
218 constexpr int collectTime = 1;
219 opt.SetTimeStopSec(collectTime);
220 opt.SetFrequency(1000); // 1000 : 1kHz
221 opt.SetCallGraph("fp");
222 opt.SetOffCPU(true);
223 if (perfCmd_.find("a") == std::string::npos) {
224 std::vector<pid_t> selectPids;
225 selectPids.push_back(pid_);
226 for (const auto& pid : pids) {
227 if (pid > 0) {
228 selectPids.push_back(pid);
229 }
230 }
231 opt.SetSelectPids(selectPids);
232 } else {
233 opt.SetTargetSystemWide(true);
234 }
235
236 if (perfClient_ == nullptr) {
237 perfClient_ = std::make_unique<HiperfClient::Client>(EVENT_LOG_PATH);
238 }
239 perfClient_->Start(opt);
240 }
241
ForkToDumpHiperf(const std::set<int> & pids)242 void PeerBinderCatcher::ForkToDumpHiperf(const std::set<int>& pids)
243 {
244 #if defined(__aarch64__)
245 if (perfCmd_.empty()) {
246 HIVIEW_LOGI("BinderPeer perf is not configured.");
247 return;
248 }
249
250 if (!Parameter::IsBetaVersion()) {
251 HIVIEW_LOGI("BinderPeer perf is only enabled in beta version.");
252 return;
253 }
254
255 static std::mutex lock;
256 std::unique_lock<std::mutex> mlock(lock);
257 std::string fileName = "hiperf-" + std::to_string(pid_) + ".data";
258 std::string fullPath = std::string(EVENT_LOG_PATH) + "/" + fileName;
259 if (access(fullPath.c_str(), F_OK) == 0) {
260 struct stat statBuf;
261 auto now = time(nullptr);
262 if (stat(fullPath.c_str(), &statBuf) == -1) {
263 HIVIEW_LOGI("Failed to stat file, error:%{public}d.", errno);
264 FileUtil::RemoveFile(fullPath);
265 } else if (now - statBuf.st_mtime < PERF_LOG_EXPIRE_TIME) {
266 HIVIEW_LOGI("Target log has exist, reuse it.");
267 return;
268 } else {
269 FileUtil::RemoveFile(fullPath);
270 }
271 }
272
273 pid_t child = fork();
274 if (child < 0) {
275 // failed to fork child
276 return;
277 } else if (child == 0) {
278 pid_t grandChild = fork();
279 if (grandChild == 0) {
280 DoExecHiperf(fileName, pids);
281 }
282 _exit(0);
283 } else {
284 // do not left a zombie
285 waitpid(child, nullptr, 0);
286 }
287 #endif
288 }
289 } // namespace HiviewDFX
290 } // namespace OHOS