• 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     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