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