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