1 /*
2 * Copyright (C) 2021-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 "peer_binder_catcher.h"
16
17 #include <ctime>
18 #include <cstdio>
19 #include <cstdlib>
20 #include <list>
21 #include <securec.h>
22 #include <sys/wait.h>
23
24 #include "common_utils.h"
25 #include "file_util.h"
26 #include "ffrt.h"
27 #include "freeze_json_util.h"
28 #include "hiview_logger.h"
29 #include "log_catcher_utils.h"
30 #include "open_stacktrace_catcher.h"
31 #include "parameter_ex.h"
32 #include "perf_collector.h"
33 #include "string_util.h"
34
35 namespace OHOS {
36 namespace HiviewDFX {
37 #ifdef BINDER_CATCHER_ENABLE
38 namespace {
39 static constexpr uint8_t ARR_SIZE = 7;
40 static constexpr uint8_t DECIMAL = 10;
41 static constexpr uint8_t FREE_ASYNC_INDEX = 6;
42 static constexpr uint16_t FREE_ASYNC_MAX = 1000;
43 static constexpr const char* const LOGGER_EVENT_PEERBINDER = "PeerBinder";
44 enum {
45 LOGGER_BINDER_STACK_ONE = 0,
46 LOGGER_BINDER_STACK_ALL = 1,
47 };
48 }
49 DEFINE_LOG_LABEL(0xD002D01, "EventLogger-PeerBinderCatcher");
50 #ifdef HAS_HIPERF
51 using namespace OHOS::HiviewDFX::UCollectUtil;
52 constexpr char EVENT_LOG_PATH[] = "/data/log/eventlog";
53 #endif
PeerBinderCatcher()54 PeerBinderCatcher::PeerBinderCatcher() : EventLogCatcher()
55 {
56 name_ = "PeerBinderCatcher";
57 }
58
Initialize(const std::string & perfCmd,int layer,int pid)59 bool PeerBinderCatcher::Initialize(const std::string& perfCmd, int layer, int pid)
60 {
61 pid_ = pid;
62 layer_ = layer;
63 perfCmd_ = perfCmd;
64 char buf[BUF_SIZE_512] = {0};
65 int ret = snprintf_s(buf, BUF_SIZE_512, BUF_SIZE_512 - 1,
66 "PeerBinderCatcher -- pid==%d layer_ == %d ", pid_, layer_);
67 if (ret > 0) {
68 description_ = buf;
69 }
70 return true;
71 }
72
Init(std::shared_ptr<SysEvent> event,const std::string & filePath,std::set<int> & catchedPids)73 void PeerBinderCatcher::Init(std::shared_ptr<SysEvent> event, const std::string& filePath, std::set<int>& catchedPids)
74 {
75 event_ = event;
76 catchedPids_ = catchedPids;
77 if ((filePath != "") && FileUtil::FileExists(filePath)) {
78 binderPath_ = filePath;
79 }
80 }
81
Catch(int fd,int jsonFd)82 int PeerBinderCatcher::Catch(int fd, int jsonFd)
83 {
84 if (pid_ <= 0) {
85 return -1;
86 }
87
88 auto originSize = GetFdSize(fd);
89
90 if (!FileUtil::FileExists(binderPath_)) {
91 std::string content = binderPath_ + " : file isn't exists\r\n";
92 FileUtil::SaveStringToFd(fd, content);
93 return -1;
94 }
95 std::set<int> asyncPids;
96 std::set<int> syncPids = GetBinderPeerPids(fd, jsonFd, asyncPids);
97 if (syncPids.empty()) {
98 std::string content = "PeerBinder pids is empty\r\n";
99 FileUtil::SaveStringToFd(fd, content);
100 }
101 #ifdef HAS_HIPERF
102 if (Parameter::IsBetaVersion()) {
103 int processId = pid_;
104 std::string perfCmd = perfCmd_;
105 ffrt::submit([this, syncPids, processId, perfCmd] { this->DumpHiperf(syncPids, processId, perfCmd); }, {}, {},
106 ffrt::task_attr().name("dump_hiperf").qos(ffrt::qos_default));
107 }
108 #endif
109 std::string pidStr = "";
110 for (auto pidTemp : syncPids) {
111 if (pidTemp == pid_ || IsAncoProc(pidTemp)) {
112 HIVIEW_LOGI("Stack of PeerBinder pid %{public}d is catched.", pidTemp);
113 continue;
114 }
115
116 if (catchedPids_.count(pidTemp) == 0) {
117 CatcherStacktrace(fd, pidTemp);
118 pidStr += "," + std::to_string(pidTemp);
119 }
120 CatcherFfrtStack(fd, pidTemp);
121 }
122 for (auto pidTemp : asyncPids) {
123 if (pidTemp == pid_ || IsAncoProc(pidTemp) || syncPids.find(pidTemp) != syncPids.end() ||
124 catchedPids_.count(pidTemp) != 0) {
125 HIVIEW_LOGI("Stack of AsyncBinder pid %{public}d is catched.", pidTemp);
126 continue;
127 }
128 CatcherStacktrace(fd, pidTemp, false);
129 }
130
131 if (event_ != nullptr) {
132 event_->SetValue(LOGGER_EVENT_PEERBINDER, StringUtil::TrimStr(pidStr, ','));
133 }
134
135 logSize_ = GetFdSize(fd) - originSize;
136 return logSize_;
137 }
138
AddBinderJsonInfo(std::list<OutputBinderInfo> outputBinderInfoList,int jsonFd) const139 void PeerBinderCatcher::AddBinderJsonInfo(std::list<OutputBinderInfo> outputBinderInfoList, int jsonFd) const
140 {
141 if (jsonFd < 0) {
142 return;
143 }
144 std::map<int, std::string> processNameMap;
145 for (OutputBinderInfo outputBinderInfo : outputBinderInfoList) {
146 int pid = outputBinderInfo.pid;
147 if (processNameMap[pid] != "") {
148 continue;
149 }
150 std::string filePath = "/proc/" + std::to_string(pid) + "/cmdline";
151 std::string realPath;
152 if (!FileUtil::PathToRealPath(filePath, realPath)) {
153 continue;
154 }
155 std::ifstream cmdLineFile(realPath);
156 std::string processName;
157 if (cmdLineFile) {
158 std::getline(cmdLineFile, processName);
159 cmdLineFile.close();
160 StringUtil::FormatProcessName(processName);
161 processNameMap[pid] = processName;
162 } else {
163 HIVIEW_LOGE("Fail to open /proc/%{public}d/cmdline", pid);
164 }
165 }
166 std::list<std::string> infoList;
167 for (auto it = outputBinderInfoList.begin(); it != outputBinderInfoList.end(); it++) {
168 int pid = (*it).pid;
169 std::string info = (*it).info;
170 std::string lineStr = info + " " + std::to_string(pid)
171 + FreezeJsonUtil::WrapByParenthesis(processNameMap[pid]);
172 infoList.push_back(lineStr);
173 }
174 std::string binderInfoJsonStr = FreezeJsonUtil::GetStrByList(infoList);
175 HIVIEW_LOGI("Get FreezeJson PeerBinder jsonStr: %{public}s.", binderInfoJsonStr.c_str());
176 FreezeJsonUtil::WriteKeyValue(jsonFd, "peer_binder", binderInfoJsonStr);
177 }
178
BinderInfoParser(std::ifstream & fin,int fd,int jsonFd,std::set<int> & asyncPids) const179 std::map<int, std::list<PeerBinderCatcher::BinderInfo>> PeerBinderCatcher::BinderInfoParser(
180 std::ifstream& fin, int fd, int jsonFd, std::set<int>& asyncPids) const
181 {
182 std::map<int, std::list<BinderInfo>> manager;
183 FileUtil::SaveStringToFd(fd, "\nBinderCatcher --\n\n");
184 std::list<OutputBinderInfo> outputBinderInfoList;
185
186 BinderInfoParser(fin, fd, manager, outputBinderInfoList, asyncPids);
187 AddBinderJsonInfo(outputBinderInfoList, jsonFd);
188
189 FileUtil::SaveStringToFd(fd, "\n\nPeerBinder Stacktrace --\n\n");
190 HIVIEW_LOGI("manager size: %{public}zu", manager.size());
191 return manager;
192 }
193
GetFileToList(std::string line) const194 std::vector<std::string> PeerBinderCatcher::GetFileToList(std::string line) const
195 {
196 std::vector<std::string> strList;
197 std::istringstream lineStream(line);
198 std::string tmpstr;
199 while (lineStream >> tmpstr) {
200 strList.push_back(tmpstr);
201 }
202 HIVIEW_LOGD("strList size: %{public}zu", strList.size());
203 return strList;
204 }
205
BinderInfoParser(std::ifstream & fin,int fd,std::map<int,std::list<PeerBinderCatcher::BinderInfo>> & manager,std::list<PeerBinderCatcher::OutputBinderInfo> & outputBinderInfoList,std::set<int> & asyncPids) const206 void PeerBinderCatcher::BinderInfoParser(std::ifstream& fin, int fd,
207 std::map<int, std::list<PeerBinderCatcher::BinderInfo>>& manager,
208 std::list<PeerBinderCatcher::OutputBinderInfo>& outputBinderInfoList, std::set<int>& asyncPids) const
209 {
210 std::map<uint32_t, uint32_t> asyncBinderMap;
211 std::vector<std::pair<uint32_t, uint64_t>> freeAsyncSpacePairs;
212 BinderInfoLineParser(fin, fd, manager, outputBinderInfoList, asyncBinderMap, freeAsyncSpacePairs);
213
214 std::sort(freeAsyncSpacePairs.begin(), freeAsyncSpacePairs.end(),
215 [] (const auto& pairOne, const auto& pairTwo) { return pairOne.second < pairTwo.second; });
216 std::vector<std::pair<uint32_t, uint32_t>> asyncBinderPairs(asyncBinderMap.begin(), asyncBinderMap.end());
217 std::sort(asyncBinderPairs.begin(), asyncBinderPairs.end(),
218 [] (const auto& pairOne, const auto& pairTwo) { return pairOne.second > pairTwo.second; });
219
220 size_t freeAsyncSpaceSize = freeAsyncSpacePairs.size();
221 size_t asyncBinderSize = asyncBinderPairs.size();
222 size_t individualMaxSize = 2;
223 for (size_t i = 0; i < individualMaxSize; i++) {
224 if (i < freeAsyncSpaceSize) {
225 asyncPids.insert(freeAsyncSpacePairs[i].first);
226 }
227 if (i < asyncBinderSize) {
228 asyncPids.insert(asyncBinderPairs[i].first);
229 }
230 }
231 }
232
BinderInfoLineParser(std::ifstream & fin,int fd,std::map<int,std::list<PeerBinderCatcher::BinderInfo>> & manager,std::list<PeerBinderCatcher::OutputBinderInfo> & outputBinderInfoList,std::map<uint32_t,uint32_t> & asyncBinderMap,std::vector<std::pair<uint32_t,uint64_t>> & freeAsyncSpacePairs) const233 void PeerBinderCatcher::BinderInfoLineParser(std::ifstream& fin, int fd,
234 std::map<int, std::list<PeerBinderCatcher::BinderInfo>>& manager,
235 std::list<PeerBinderCatcher::OutputBinderInfo>& outputBinderInfoList,
236 std::map<uint32_t, uint32_t>& asyncBinderMap,
237 std::vector<std::pair<uint32_t, uint64_t>>& freeAsyncSpacePairs) const
238 {
239 std::string line;
240 bool isBinderMatchup = false;
241 while (getline(fin, line)) {
242 FileUtil::SaveStringToFd(fd, line + "\n");
243 isBinderMatchup = (!isBinderMatchup && line.find("free_async_space") != line.npos) ? true : isBinderMatchup;
244 std::vector<std::string> strList = GetFileToList(line);
245 auto strSplit = [](const std::string& str, uint16_t index) -> std::string {
246 std::vector<std::string> strings;
247 StringUtil::SplitStr(str, ":", strings);
248 return index < strings.size() ? strings[index] : "";
249 };
250
251 if (isBinderMatchup) {
252 if (line.find("free_async_space") == line.npos && strList.size() == ARR_SIZE &&
253 std::atoll(strList[FREE_ASYNC_INDEX].c_str()) < FREE_ASYNC_MAX) {
254 freeAsyncSpacePairs.emplace_back(
255 std::atoi(strList[0].c_str()),
256 std::atoll(strList[FREE_ASYNC_INDEX].c_str()));
257 }
258 } else if (line.find("async\t") != std::string::npos && strList.size() > ARR_SIZE) {
259 std::string serverPid = strSplit(strList[3], 0);
260 std::string serverTid = strSplit(strList[3], 1);
261 if (serverPid != "" && serverTid != "" && std::atoi(serverTid.c_str()) == 0) {
262 asyncBinderMap[std::atoi(serverPid.c_str())]++;
263 }
264 } else if (strList.size() >= ARR_SIZE) { // 7: valid array size
265 // 0: binder local id,
266 std::string clientPid = strSplit(strList[0], 0);
267 std::string clientTid = strSplit(strList[0], 1);
268 // 2: binder peer id,
269 std::string serverPid = strSplit(strList[2], 0);
270 std::string serverTid = strSplit(strList[2], 1);
271 std::string wait = strSplit(strList[5], 1);
272 if (clientPid == "" || clientTid == "" || serverPid == "" || serverTid == "" || wait == "") {
273 HIVIEW_LOGI("server:%{public}s, client:%{public}s, wait:%{public}s",
274 serverPid.c_str(), clientPid.c_str(), wait.c_str());
275 continue;
276 }
277 BinderInfo info = {
278 std::strtol(clientPid.c_str(), nullptr, DECIMAL), std::strtol(clientTid.c_str(), nullptr, DECIMAL),
279 std::strtol(serverPid.c_str(), nullptr, DECIMAL), std::strtol(serverTid.c_str(), nullptr, DECIMAL),
280 std::strtol(wait.c_str(), nullptr, DECIMAL)};
281 HIVIEW_LOGD("server:%{public}d, client:%{public}d, wait:%{public}d", info.serverPid, info.clientPid,
282 info.wait);
283 manager[info.clientPid].push_back(info);
284 OutputBinderInfo outputInfo = {StringUtil::TrimStr(line), info.serverPid};
285 outputBinderInfoList.push_back(outputInfo);
286 } else {
287 HIVIEW_LOGI("strList size: %{public}zu, line: %{public}s", strList.size(), line.c_str());
288 }
289 }
290 }
291
GetBinderPeerPids(int fd,int jsonFd,std::set<int> & asyncPids)292 std::set<int> PeerBinderCatcher::GetBinderPeerPids(int fd, int jsonFd, std::set<int>& asyncPids)
293 {
294 std::set<int> pids;
295 std::ifstream fin;
296 std::string path = binderPath_;
297 fin.open(path.c_str());
298 if (!fin.is_open()) {
299 HIVIEW_LOGE("open binder file failed, %{public}s.", path.c_str());
300 std::string content = "open binder file failed :" + path + "\r\n";
301 FileUtil::SaveStringToFd(fd, content);
302 return pids;
303 }
304
305 std::map<int, std::list<PeerBinderCatcher::BinderInfo>> manager = BinderInfoParser(fin, fd, jsonFd, asyncPids);
306 fin.close();
307
308 if (manager.size() == 0 || manager.find(pid_) == manager.end()) {
309 return pids;
310 }
311
312 if (layer_ == LOGGER_BINDER_STACK_ONE) {
313 for (auto each : manager[pid_]) {
314 if (!firstLayerInit_) {
315 terminalBinder_.pid = each.serverPid;
316 terminalBinder_.tid = each.serverTid;
317 firstLayerInit_ = true;
318 }
319 pids.insert(each.serverPid);
320 }
321 } else if (layer_ == LOGGER_BINDER_STACK_ALL) {
322 ParseBinderCallChain(manager, pids, pid_);
323 }
324 return pids;
325 }
326
ParseBinderCallChain(std::map<int,std::list<PeerBinderCatcher::BinderInfo>> & manager,std::set<int> & pids,int pid)327 void PeerBinderCatcher::ParseBinderCallChain(std::map<int, std::list<PeerBinderCatcher::BinderInfo>>& manager,
328 std::set<int>& pids, int pid)
329 {
330 bool isGetLayerBinder = false;
331 for (auto& each : manager[pid]) {
332 if (pids.find(each.serverPid) != pids.end()) {
333 continue;
334 }
335 pids.insert(each.serverPid);
336 if (!firstLayerInit_ || (!isGetLayerBinder && terminalBinder_.pid == each.clientPid &&
337 terminalBinder_.tid == each.clientTid)) {
338 terminalBinder_.pid = each.serverPid;
339 terminalBinder_.tid = each.serverTid;
340 firstLayerInit_ = true;
341 isGetLayerBinder = true;
342 }
343 ParseBinderCallChain(manager, pids, each.serverPid);
344 }
345 }
346
IsAncoProc(int pid) const347 bool PeerBinderCatcher::IsAncoProc(int pid) const
348 {
349 std::string cgroupPath = "/proc/" + std::to_string(pid) + "/cgroup";
350 std::string firstLine = FileUtil::GetFirstLine(cgroupPath);
351 return firstLine.find("isulad") != std::string::npos;
352 }
353
CatcherFfrtStack(int fd,int pid) const354 void PeerBinderCatcher::CatcherFfrtStack(int fd, int pid) const
355 {
356 std::string content = "PeerBinder catcher ffrt stacktrace for pid : " + std::to_string(pid) + "\r\n";
357 FileUtil::SaveStringToFd(fd, content);
358
359 LogCatcherUtils::DumpStackFfrt(fd, std::to_string(pid));
360 }
361
CatcherStacktrace(int fd,int pid,bool sync)362 void PeerBinderCatcher::CatcherStacktrace(int fd, int pid, bool sync)
363 {
364 std::string type = sync ? "peer" : "async";
365 std::string content = "Binder catcher stacktrace, type is " + type + ", pid : " + std::to_string(pid) + "\r\n";
366 FileUtil::SaveStringToFd(fd, content);
367
368 LogCatcherUtils::DumpStacktrace(fd, pid, terminalBinder_.threadStack, terminalBinder_.pid, terminalBinder_.tid);
369 }
370
371 #ifdef HAS_HIPERF
DoExecHiperf(const std::string & fileName,const std::set<int> & pids,int processId,const std::string & perfCmd)372 void PeerBinderCatcher::DoExecHiperf(const std::string& fileName, const std::set<int>& pids, int processId,
373 const std::string& perfCmd)
374 {
375 std::shared_ptr<PerfCollector> perfCollector = PerfCollector::Create(PerfCaller::EVENTLOGGER);
376 perfCollector->SetOutputFilename(fileName);
377 constexpr int collectTime = 1;
378 perfCollector->SetTimeStopSec(collectTime);
379 if (perfCmd.find("a") == std::string::npos) {
380 std::vector<pid_t> selectPids;
381 selectPids.push_back(processId);
382 for (const auto& pid : pids) {
383 if (pid > 0) {
384 selectPids.push_back(pid);
385 }
386 }
387 perfCollector->SetSelectPids(selectPids);
388 perfCollector->SetReport(true);
389 } else {
390 perfCollector->SetTargetSystemWide(true);
391 }
392 perfCollector->SetFrequency(1000); // 1000 : 1kHz
393 CollectResult<bool> ret = perfCollector->StartPerf(EVENT_LOG_PATH);
394 if (ret.retCode == UCollect::UcError::SUCCESS) {
395 HIVIEW_LOGI("Success to call perf result : %{public}d", ret.data);
396 } else {
397 HIVIEW_LOGI("Failed to call perf result : %{public}d", ret.data);
398 }
399 }
400
DumpHiperf(const std::set<int> & pids,int processId,const std::string & perfCmd)401 void PeerBinderCatcher::DumpHiperf(const std::set<int>& pids, int processId, const std::string& perfCmd)
402 {
403 #if defined(__aarch64__)
404 if (perfCmd.empty()) {
405 HIVIEW_LOGI("BinderPeer perf is not configured.");
406 return;
407 }
408
409 HIVIEW_LOGI("dump hiperf start, pid:%{public}d", processId);
410 static ffrt::mutex lock;
411 std::unique_lock<ffrt::mutex> mlock(lock);
412 std::string fileName = "hiperf-" + std::to_string(processId) + ".data";
413 std::string fullPath = std::string(EVENT_LOG_PATH) + "/" + fileName;
414 constexpr int perfLogExPireTime = 60;
415 if (access(fullPath.c_str(), F_OK) == 0) {
416 struct stat statBuf;
417 auto now = time(nullptr);
418 if (stat(fullPath.c_str(), &statBuf) == -1) {
419 HIVIEW_LOGI("Failed to stat file, error:%{public}d.", errno);
420 FileUtil::RemoveFile(fullPath);
421 } else if (now - statBuf.st_mtime < perfLogExPireTime) {
422 HIVIEW_LOGI("Target log has exist, reuse it.");
423 return;
424 } else {
425 FileUtil::RemoveFile(fullPath);
426 }
427 }
428 DoExecHiperf(fileName, pids, processId, perfCmd);
429 HIVIEW_LOGI("dump hiperf end, pid:%{public}d", processId);
430 #endif
431 }
432 #endif
433 #endif // BINDER_CATCHER_ENABLE
434 } // namespace HiviewDFX
435 } // namespace OHOS