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 const char* const LOGGER_EVENT_PEERBINDER = "PeerBinder";
38     enum {
39         LOGGER_BINDER_STACK_ONE = 0,
40         LOGGER_BINDER_STACK_ALL = 1,
41     };
42 }
43 DEFINE_LOG_LABEL(0xD002D01, "EventLogger-PeerBinderCatcher");
44 #ifdef HAS_HIPERF
45 using namespace OHOS::HiviewDFX::UCollectUtil;
46 constexpr char EVENT_LOG_PATH[] = "/data/log/eventlog";
47 #endif
PeerBinderCatcher()48 PeerBinderCatcher::PeerBinderCatcher() : EventLogCatcher()
49 {
50     name_ = "PeerBinderCatcher";
51 }
52 
Initialize(const std::string& perfCmd, int layer, int pid)53 bool PeerBinderCatcher::Initialize(const std::string& perfCmd, int layer, int pid)
54 {
55     pid_ = pid;
56     layer_ = layer;
57     perfCmd_ = perfCmd;
58     char buf[BUF_SIZE_512] = {0};
59     int ret = snprintf_s(buf, BUF_SIZE_512, BUF_SIZE_512 - 1,
60         "PeerBinderCatcher -- pid==%d layer_ == %d\n", pid_, layer_);
61     if (ret > 0) {
62         description_ = buf;
63     }
64     return true;
65 }
66 
Init(std::shared_ptr<SysEvent> event, const std::string& filePath, std::set<int>& catchedPids)67 void PeerBinderCatcher::Init(std::shared_ptr<SysEvent> event, const std::string& filePath, std::set<int>& catchedPids)
68 {
69     event_ = event;
70     catchedPids_ = catchedPids;
71     if ((filePath != "") && FileUtil::FileExists(filePath)) {
72         binderPath_ = filePath;
73     }
74 }
75 
Catch(int fd, int jsonFd)76 int PeerBinderCatcher::Catch(int fd, int jsonFd)
77 {
78     if (pid_ <= 0) {
79         return -1;
80     }
81 
82     auto originSize = GetFdSize(fd);
83 
84     if (!FileUtil::FileExists(binderPath_)) {
85         std::string content = binderPath_ + " : file isn't exists\r\n";
86         FileUtil::SaveStringToFd(fd, content);
87         return -1;
88     }
89 
90     std::set<int> pids = GetBinderPeerPids(fd, jsonFd);
91     if (pids.empty()) {
92         std::string content = "PeerBinder pids is empty\r\n";
93         FileUtil::SaveStringToFd(fd, content);
94     }
95 #ifdef HAS_HIPERF
96     ForkToDumpHiperf(pids);
97 #endif
98     std::string pidStr = "";
99     for (auto pidTemp : pids) {
100         if (pidTemp == pid_ || IsAncoProc(pidTemp)) {
101             HIVIEW_LOGI("Stack of pid %{public}d is catched.", pidTemp);
102             continue;
103         }
104 
105         if (catchedPids_.count(pidTemp) == 0) {
106             CatcherStacktrace(fd, pidTemp);
107             pidStr += "," + std::to_string(pidTemp);
108         }
109         CatcherFfrtStack(fd, pidTemp);
110     }
111 
112     if (event_ != nullptr) {
113         event_->SetValue(LOGGER_EVENT_PEERBINDER, StringUtil::TrimStr(pidStr, ','));
114     }
115 
116     logSize_ = GetFdSize(fd) - originSize;
117     return logSize_;
118 }
119 
AddBinderJsonInfo(std::list<OutputBinderInfo> outputBinderInfoList, int jsonFd) const120 void PeerBinderCatcher::AddBinderJsonInfo(std::list<OutputBinderInfo> outputBinderInfoList, int jsonFd) const
121 {
122     if (jsonFd < 0) {
123         return;
124     }
125     std::map<int, std::string> processNameMap;
126     for (OutputBinderInfo outputBinderInfo : outputBinderInfoList) {
127         int pid = outputBinderInfo.pid;
128         if (processNameMap[pid] != "") {
129             continue;
130         }
131         std::string filePath = "/proc/" + std::to_string(pid) + "/cmdline";
132         std::string realPath;
133         if (!FileUtil::PathToRealPath(filePath, realPath)) {
134             continue;
135         }
136         std::ifstream cmdLineFile(realPath);
137         std::string processName;
138         if (cmdLineFile) {
139             std::getline(cmdLineFile, processName);
140             cmdLineFile.close();
141             processName = StringUtil::FormatCmdLine(processName);
142             processNameMap[pid] = processName;
143         } else {
144             HIVIEW_LOGE("Fail to open /proc/%{public}d/cmdline", pid);
145         }
146     }
147     std::list<std::string> infoList;
148     for (auto it = outputBinderInfoList.begin(); it != outputBinderInfoList.end(); it++) {
149         int pid = (*it).pid;
150         std::string info = (*it).info;
151         std::string lineStr = info + "    " + std::to_string(pid)
152             + FreezeJsonUtil::WrapByParenthesis(processNameMap[pid]);
153         infoList.push_back(lineStr);
154     }
155     std::string binderInfoJsonStr = FreezeJsonUtil::GetStrByList(infoList);
156     HIVIEW_LOGI("Get FreezeJson PeerBinder jsonStr: %{public}s.", binderInfoJsonStr.c_str());
157     FreezeJsonUtil::WriteKeyValue(jsonFd, "peer_binder", binderInfoJsonStr);
158 }
159 
BinderInfoParser( std::ifstream& fin, int fd, int jsonFd) const160 std::map<int, std::list<PeerBinderCatcher::BinderInfo>> PeerBinderCatcher::BinderInfoParser(
161     std::ifstream& fin, int fd, int jsonFd) const
162 {
163     std::map<int, std::list<BinderInfo>> manager;
164     FileUtil::SaveStringToFd(fd, "\nBinderCatcher --\n\n");
165     std::list<OutputBinderInfo> outputBinderInfoList;
166 
167     BinderInfoParser(fin, fd, manager, outputBinderInfoList);
168     AddBinderJsonInfo(outputBinderInfoList, jsonFd);
169 
170     FileUtil::SaveStringToFd(fd, "\n\nPeerBinder Stacktrace --\n\n");
171     HIVIEW_LOGI("manager size: %{public}zu", manager.size());
172     return manager;
173 }
174 
GetFileToList(std::string line, std::vector<std::string>& strList) const175 void PeerBinderCatcher::GetFileToList(std::string line,
176     std::vector<std::string>& strList) const
177 {
178     std::istringstream lineStream(line);
179     std::string tmpstr;
180     while (lineStream >> tmpstr) {
181         strList.push_back(tmpstr);
182     }
183     HIVIEW_LOGD("strList size: %{public}zu", strList.size());
184 }
185 
BinderInfoParser(std::ifstream& fin, int fd, std::map<int, std::list<PeerBinderCatcher::BinderInfo>>& manager, std::list<PeerBinderCatcher::OutputBinderInfo>& outputBinderInfoList) const186 void PeerBinderCatcher::BinderInfoParser(std::ifstream& fin, int fd,
187     std::map<int, std::list<PeerBinderCatcher::BinderInfo>>& manager,
188     std::list<PeerBinderCatcher::OutputBinderInfo>& outputBinderInfoList) const
189 {
190     const int DECIMAL = 10;
191     std::string line;
192     bool findBinderHeader = false;
193     while (getline(fin, line)) {
194         FileUtil::SaveStringToFd(fd, line + "\n");
195         if (findBinderHeader) {
196             continue;
197         }
198 
199         if (line.find("async\t") != std::string::npos) {
200             continue;
201         }
202 
203         std::vector<std::string> strList;
204         GetFileToList(line, strList);
205         auto stringSplit = [](const std::string& str, uint16_t index) -> std::string {
206             std::vector<std::string> strings;
207             StringUtil::SplitStr(str, ":", strings);
208             if (index < strings.size()) {
209                 return strings[index];
210             }
211             return "";
212         };
213 
214         if (strList.size() >= 7) { // 7: valid array size
215             BinderInfo info = {0};
216             OutputBinderInfo outputInfo;
217             // 2: binder peer id,
218             std::string server = stringSplit(strList[2], 0);
219             // 0: binder local id,
220             std::string client = stringSplit(strList[0], 0);
221             // 5: binder wait time, s
222             std::string wait = stringSplit(strList[5], 1);
223             if (server == "" || client == "" || wait == "") {
224                 HIVIEW_LOGI("server:%{public}s, client:%{public}s, wait:%{public}s",
225                     server.c_str(), client.c_str(), wait.c_str());
226                 continue;
227             }
228             info.server = std::strtol(server.c_str(), nullptr, DECIMAL);
229             info.client = std::strtol(client.c_str(), nullptr, DECIMAL);
230             info.wait = std::strtol(wait.c_str(), nullptr, DECIMAL);
231             HIVIEW_LOGD("server:%{public}d, client:%{public}d, wait:%{public}d", info.server, info.client, info.wait);
232             manager[info.client].push_back(info);
233             outputInfo.info = StringUtil::TrimStr(line);
234             outputInfo.pid = info.server;
235             outputBinderInfoList.push_back(outputInfo);
236         } else {
237             HIVIEW_LOGI("strList size: %{public}zu, line: %{public}s", strList.size(), line.c_str());
238         }
239         if (line.find("context") != line.npos) {
240             findBinderHeader = true;
241         }
242     }
243 }
244 
GetBinderPeerPids(int fd, int jsonFd) const245 std::set<int> PeerBinderCatcher::GetBinderPeerPids(int fd, int jsonFd) const
246 {
247     std::set<int> pids;
248     std::ifstream fin;
249     std::string path = binderPath_;
250     fin.open(path.c_str());
251     if (!fin.is_open()) {
252         HIVIEW_LOGE("open binder file failed, %{public}s.", path.c_str());
253         std::string content = "open binder file failed :" + path + "\r\n";
254         FileUtil::SaveStringToFd(fd, content);
255         return pids;
256     }
257 
258     std::map<int, std::list<PeerBinderCatcher::BinderInfo>> manager = BinderInfoParser(fin, fd, jsonFd);
259     fin.close();
260 
261     if (manager.size() == 0 || manager.find(pid_) == manager.end()) {
262         return pids;
263     }
264 
265     if (layer_ == LOGGER_BINDER_STACK_ONE) {
266         for (auto each : manager[pid_]) {
267             pids.insert(each.server);
268         }
269     } else if (layer_ == LOGGER_BINDER_STACK_ALL) {
270         ParseBinderCallChain(manager, pids, pid_);
271     }
272     return pids;
273 }
274 
ParseBinderCallChain(std::map<int, std::list<PeerBinderCatcher::BinderInfo>>& manager, std::set<int>& pids, int pid) const275 void PeerBinderCatcher::ParseBinderCallChain(std::map<int, std::list<PeerBinderCatcher::BinderInfo>>& manager,
276     std::set<int>& pids, int pid) const
277 {
278     for (auto& each : manager[pid]) {
279         if (pids.find(each.server) != pids.end()) {
280             continue;
281         }
282         pids.insert(each.server);
283         ParseBinderCallChain(manager, pids, each.server);
284     }
285 }
286 
IsAncoProc(int pid) const287 bool PeerBinderCatcher::IsAncoProc(int pid) const
288 {
289     std::string cgroupPath = "/proc/" + std::to_string(pid) + "/cgroup";
290     std::string firstLine = FileUtil::GetFirstLine(cgroupPath);
291     return firstLine.find("isulad") != std::string::npos;
292 }
293 
CatcherFfrtStack(int fd, int pid) const294 void PeerBinderCatcher::CatcherFfrtStack(int fd, int pid) const
295 {
296     std::string content =  "PeerBinderCatcher start catcher ffrt stack for pid : " + std::to_string(pid) + "\r\n";
297     FileUtil::SaveStringToFd(fd, content);
298 
299     LogCatcherUtils::DumpStackFfrt(fd, std::to_string(pid));
300 }
301 
CatcherStacktrace(int fd, int pid) const302 void PeerBinderCatcher::CatcherStacktrace(int fd, int pid) const
303 {
304     std::string content =  "PeerBinderCatcher start catcher stacktrace for pid : " + std::to_string(pid) + "\r\n";
305     FileUtil::SaveStringToFd(fd, content);
306 
307     LogCatcherUtils::DumpStacktrace(fd, pid);
308 }
309 
310 #ifdef HAS_HIPERF
DoExecHiperf(const std::string& fileName, const std::set<int>& pids)311 void PeerBinderCatcher::DoExecHiperf(const std::string& fileName, const std::set<int>& pids)
312 {
313     std::shared_ptr<PerfCollector> perfCollector = PerfCollector::Create();
314     perfCollector->SetOutputFilename(fileName);
315     constexpr int collectTime = 1;
316     perfCollector->SetTimeStopSec(collectTime);
317     if (perfCmd_.find("a") == std::string::npos) {
318         std::vector<pid_t> selectPids;
319         selectPids.push_back(pid_);
320         for (const auto& pid : pids) {
321             if (pid > 0) {
322                 selectPids.push_back(pid);
323             }
324         }
325         perfCollector->SetSelectPids(selectPids);
326         perfCollector->SetReport(true);
327     } else {
328         perfCollector->SetTargetSystemWide(true);
329     }
330     perfCollector->SetFrequency(1000); // 1000 : 1kHz
331     CollectResult<bool> ret = perfCollector->StartPerf(EVENT_LOG_PATH);
332     if (ret.retCode == UCollect::UcError::SUCCESS) {
333         HIVIEW_LOGI("Success to call perf result : %{public}d", ret.data);
334     } else {
335         HIVIEW_LOGI("Failed to  call perf result : %{public}d", ret.data);
336     }
337 }
338 
ForkToDumpHiperf(const std::set<int>& pids)339 void PeerBinderCatcher::ForkToDumpHiperf(const std::set<int>& pids)
340 {
341 #if defined(__aarch64__)
342     if (perfCmd_.empty()) {
343         HIVIEW_LOGI("BinderPeer perf is not configured.");
344         return;
345     }
346 
347     if (!Parameter::IsBetaVersion()) {
348         HIVIEW_LOGI("BinderPeer perf is only enabled in beta version.");
349         return;
350     }
351 
352     static std::mutex lock;
353     std::unique_lock<std::mutex> mlock(lock);
354     std::string fileName = "hiperf-" + std::to_string(pid_) + ".data";
355     std::string fullPath = std::string(EVENT_LOG_PATH) + "/" + fileName;
356     constexpr int perfLogExPireTime = 60;
357     if (access(fullPath.c_str(), F_OK) == 0) {
358         struct stat statBuf;
359         auto now = time(nullptr);
360         if (stat(fullPath.c_str(), &statBuf) == -1) {
361             HIVIEW_LOGI("Failed to stat file, error:%{public}d.", errno);
362             FileUtil::RemoveFile(fullPath);
363         } else if (now - statBuf.st_mtime < perfLogExPireTime) {
364             HIVIEW_LOGI("Target log has exist, reuse it.");
365             return;
366         } else {
367             FileUtil::RemoveFile(fullPath);
368         }
369     }
370 
371     pid_t child = fork();
372     if (child < 0) {
373         // failed to fork child
374         return;
375     } else if (child == 0) {
376         pid_t grandChild = fork();
377         if (grandChild == 0) {
378             DoExecHiperf(fileName, pids);
379         }
380         _exit(0);
381     } else {
382         // do not left a zombie
383         waitpid(child, nullptr, 0);
384     }
385 #endif
386 }
387 #endif
388 } // namespace HiviewDFX
389 } // namespace OHOS