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