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