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