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