• 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 
21 #include <sys/wait.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 
25 #include <securec.h>
26 
27 #include "common_utils.h"
28 #include "file_util.h"
29 #include "log_catcher_utils.h"
30 #include "logger.h"
31 #include "parameter_ex.h"
32 #include "string_util.h"
33 
34 #include "open_stacktrace_catcher.h"
35 namespace OHOS {
36 namespace HiviewDFX {
37 DEFINE_LOG_LABEL(0xD002D01, "EventLogger-PeerBinderCatcher");
38 using namespace Developtools::HiPerf;
39 constexpr char EVENT_LOG_PATH[] = "/data/log/eventlog";
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)59 void PeerBinderCatcher::Init(std::shared_ptr<SysEvent> event, const std::string& filePath)
60 {
61     event_ = event;
62     if ((filePath != "") && FileUtil::FileExists(filePath)) {
63         binderPath_ = filePath;
64     }
65 }
66 
Catch(int fd)67 int PeerBinderCatcher::Catch(int fd)
68 {
69     if (pid_ <= 0) {
70         return -1;
71     }
72 
73     auto originSize = GetFdSize(fd);
74 
75     if (!FileUtil::FileExists(binderPath_)) {
76         std::string content = binderPath_ + " : file isn't exists\r\n";
77         FileUtil::SaveStringToFd(fd, content);
78         return -1;
79     }
80 
81     std::set<int> pids = GetBinderPeerPids(fd);
82     if (pids.empty()) {
83         std::string content = "PeerBinder pids is empty\r\n";
84         FileUtil::SaveStringToFd(fd, content);
85     }
86 
87     ForkToDumpHiperf(pids);
88     std::string pidStr = "";
89     for (auto pidTemp : pids) {
90         if (pidTemp != pid_) {
91             CatcherStacktrace(fd, pidTemp);
92             pidStr += "," + std::to_string(pidTemp);
93         }
94     }
95 
96     if (event_ != nullptr) {
97         event_->SetValue(LOGGER_EVENT_PEERBINDER, StringUtil::TrimStr(pidStr, ','));
98     }
99 
100     logSize_ = GetFdSize(fd) - originSize;
101     return logSize_;
102 }
103 
BinderInfoParser(std::ifstream & fin,int fd) const104 std::map<int, std::list<PeerBinderCatcher::BinderInfo>> PeerBinderCatcher::BinderInfoParser(
105     std::ifstream& fin, int fd) const
106 {
107     std::map<int, std::list<BinderInfo>> manager;
108     const int DECIMAL = 10;
109     std::string line;
110     bool findBinderHeader = false;
111     FileUtil::SaveStringToFd(fd, "\nBinderCatcher --\n\n");
112     while (getline(fin, line)) {
113         FileUtil::SaveStringToFd(fd, line + "\n");
114         if (findBinderHeader) {
115             continue;
116         }
117 
118         if (line.find("async") != std::string::npos) {
119             continue;
120         }
121 
122         std::istringstream lineStream(line);
123         std::vector<std::string> strList;
124         std::string tmpstr;
125         while (lineStream >> tmpstr) {
126             strList.push_back(tmpstr);
127         }
128 
129         auto stringSplit = [](const std::string& str, uint16_t index) -> std::string {
130             std::vector<std::string> strings;
131             StringUtil::SplitStr(str, ":", strings);
132             if (index < strings.size()) {
133                 return strings[index];
134             }
135             return "";
136         };
137 
138         if (strList.size() == 7) { // 7: valid array size
139             BinderInfo info = {0};
140             // 2: binder peer id,
141             std::string server = stringSplit(strList[2], 0);
142             // 0: binder local id,
143             std::string client = stringSplit(strList[0], 0);
144             // 5: binder wait time, s
145             std::string wait = stringSplit(strList[5], 1);
146             if (server == "" || client == "" || wait == "") {
147                 continue;
148             }
149             info.server = std::strtol(server.c_str(), nullptr, DECIMAL);
150             info.client = std::strtol(client.c_str(), nullptr, DECIMAL);
151             info.wait = std::strtol(wait.c_str(), nullptr, DECIMAL);
152             HIVIEW_LOGI("server:%{public}d, client:%{public}d, wait:%{public}d", info.server, info.client, info.wait);
153             manager[info.client].push_back(info);
154         }
155         if (line.find("context") != line.npos) {
156             findBinderHeader = true;
157         }
158     }
159     FileUtil::SaveStringToFd(fd, "\n\nPeerBinder Stacktrace --\n\n");
160     HIVIEW_LOGI("manager size: %{public}zu", manager.size());
161     return manager;
162 }
163 
GetBinderPeerPids(int fd) const164 std::set<int> PeerBinderCatcher::GetBinderPeerPids(int fd) const
165 {
166     std::set<int> pids;
167     std::ifstream fin;
168     std::string path = binderPath_;
169     fin.open(path.c_str());
170     if (!fin.is_open()) {
171         HIVIEW_LOGE("open binder file failed, %{public}s.", path.c_str());
172         std::string content = "open binder file failed :" + path + "\r\n";
173         FileUtil::SaveStringToFd(fd, content);
174         return pids;
175     }
176 
177     std::map<int, std::list<PeerBinderCatcher::BinderInfo>> manager = BinderInfoParser(fin, fd);
178     fin.close();
179 
180     if (manager.size() == 0 || manager.find(pid_) == manager.end()) {
181         return pids;
182     }
183 
184     if (layer_ == LOGGER_BINDER_STACK_ONE) {
185         for (auto each : manager[pid_]) {
186             pids.insert(each.server);
187         }
188     } else if (layer_ == LOGGER_BINDER_STACK_ALL) {
189         ParseBinderCallChain(manager, pids, pid_);
190     }
191     return pids;
192 }
193 
ParseBinderCallChain(std::map<int,std::list<PeerBinderCatcher::BinderInfo>> & manager,std::set<int> & pids,int pid) const194 void PeerBinderCatcher::ParseBinderCallChain(std::map<int, std::list<PeerBinderCatcher::BinderInfo>>& manager,
195     std::set<int>& pids, int pid) const
196 {
197     for (auto& each : manager[pid]) {
198         if (pids.find(each.server) != pids.end()) {
199             continue;
200         }
201         pids.insert(each.server);
202         ParseBinderCallChain(manager, pids, each.server);
203     }
204 }
205 
CatcherStacktrace(int fd,int pid) const206 void PeerBinderCatcher::CatcherStacktrace(int fd, int pid) const
207 {
208     std::string content =  "PeerBinderCatcher start catcher stacktrace for pid : " + std::to_string(pid) + "\r\n";
209     FileUtil::SaveStringToFd(fd, content);
210 
211     LogCatcherUtils::DumpStacktrace(fd, pid);
212 }
213 
DoExecHiperf(const std::string & fileName,const std::set<int> & pids)214 void PeerBinderCatcher::DoExecHiperf(const std::string& fileName, const std::set<int>& pids)
215 {
216     HiperfClient::RecordOption opt;
217     opt.SetOutputFilename(fileName);
218     constexpr int collectTime = 1;
219     opt.SetTimeStopSec(collectTime);
220     opt.SetFrequency(1000); // 1000 : 1kHz
221     opt.SetCallGraph("fp");
222     opt.SetOffCPU(true);
223     if (perfCmd_.find("a") == std::string::npos) {
224         std::vector<pid_t> selectPids;
225         selectPids.push_back(pid_);
226         for (const auto& pid : pids) {
227             if (pid > 0) {
228                 selectPids.push_back(pid);
229             }
230         }
231         opt.SetSelectPids(selectPids);
232     } else {
233         opt.SetTargetSystemWide(true);
234     }
235 
236     if (perfClient_ == nullptr) {
237         perfClient_ = std::make_unique<HiperfClient::Client>(EVENT_LOG_PATH);
238     }
239     perfClient_->Start(opt);
240 }
241 
ForkToDumpHiperf(const std::set<int> & pids)242 void PeerBinderCatcher::ForkToDumpHiperf(const std::set<int>& pids)
243 {
244 #if defined(__aarch64__)
245     if (perfCmd_.empty()) {
246         HIVIEW_LOGI("BinderPeer perf is not configured.");
247         return;
248     }
249 
250     if (!Parameter::IsBetaVersion()) {
251         HIVIEW_LOGI("BinderPeer perf is only enabled in beta version.");
252         return;
253     }
254 
255     static std::mutex lock;
256     std::unique_lock<std::mutex> mlock(lock);
257     std::string fileName = "hiperf-" + std::to_string(pid_) + ".data";
258     std::string fullPath = std::string(EVENT_LOG_PATH) + "/" + fileName;
259     if (access(fullPath.c_str(), F_OK) == 0) {
260         struct stat statBuf;
261         auto now = time(nullptr);
262         if (stat(fullPath.c_str(), &statBuf) == -1) {
263             HIVIEW_LOGI("Failed to stat file, error:%{public}d.", errno);
264             FileUtil::RemoveFile(fullPath);
265         } else if (now - statBuf.st_mtime < PERF_LOG_EXPIRE_TIME) {
266             HIVIEW_LOGI("Target log has exist, reuse it.");
267             return;
268         } else {
269             FileUtil::RemoveFile(fullPath);
270         }
271     }
272 
273     pid_t child = fork();
274     if (child < 0) {
275         // failed to fork child
276         return;
277     } else if (child == 0) {
278         pid_t grandChild = fork();
279         if (grandChild == 0) {
280             DoExecHiperf(fileName, pids);
281         }
282         _exit(0);
283     } else {
284         // do not left a zombie
285         waitpid(child, nullptr, 0);
286     }
287 #endif
288 }
289 } // namespace HiviewDFX
290 } // namespace OHOS