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