1 /*
2 * Copyright (c) 2025 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 "faultlog_hilog_helper.h"
16
17 #include <fcntl.h>
18 #include <poll.h>
19 #include <queue>
20 #include <unistd.h>
21
22 #include <sys/syscall.h>
23 #include <sys/wait.h>
24
25 #include "constants.h"
26 #include "hiview_logger.h"
27 #include "parameter_ex.h"
28
29 // define Fdsan Domain
30 #ifndef FDSAN_DOMAIN
31 #undef FDSAN_DOMAIN
32 #endif
33 #define FDSAN_DOMAIN 0xD002D11
34
35 namespace OHOS {
36 namespace HiviewDFX {
37 DEFINE_LOG_LABEL(0xD002D11, "Faultlogger");
38 using namespace FaultLogger;
39 namespace {
40 constexpr int READ_HILOG_BUFFER_SIZE = 1024;
41 constexpr int NUMBER_ONE_THOUSAND = 1000;
42 constexpr int NUMBER_ONE_MILLION = 1000 * 1000;
43 constexpr uint64_t MAX_HILOG_TIMEOUT = 15 * 1000;
44
GetTimeMilliseconds(void)45 uint64_t GetTimeMilliseconds(void)
46 {
47 struct timespec ts;
48 (void)clock_gettime(CLOCK_REALTIME, &ts);
49 return (static_cast<uint64_t>(ts.tv_sec) * NUMBER_ONE_THOUSAND) +
50 (static_cast<uint64_t>(ts.tv_nsec) / NUMBER_ONE_MILLION);
51 }
52
ReadDataFromPipe(int fd,std::string & log)53 void ReadDataFromPipe(int fd, std::string &log)
54 {
55 char buffer[READ_HILOG_BUFFER_SIZE];
56 ssize_t nread {0};
57 do {
58 nread = TEMP_FAILURE_RETRY(read(fd, buffer, sizeof(buffer) - 1));
59 if (nread > 0) {
60 log.append(buffer, nread);
61 }
62 } while (nread > 0);
63 }
64 } // namespace
65
ReadHilogTimeout(int fd,uint64_t timeout)66 std::string FaultlogHilogHelper::ReadHilogTimeout(int fd, uint64_t timeout)
67 {
68 if (fd < 0 || timeout > MAX_HILOG_TIMEOUT) {
69 HIVIEW_LOGE("Invalid fd or timeout");
70 return "";
71 }
72 uint64_t startTime = GetTimeMilliseconds();
73 uint64_t endTime = startTime + timeout;
74
75 struct pollfd pfds[1];
76 pfds[0].fd = fd;
77 pfds[0].events = POLLIN | POLLHUP;
78
79 std::string log;
80 int pollRet = -1;
81 do {
82 uint64_t now = GetTimeMilliseconds();
83 if (now >= endTime || now < startTime) {
84 HIVIEW_LOGI("read hilog timeout.");
85 break;
86 } else {
87 timeout = endTime - now;
88 }
89
90 pollRet = poll(pfds, 1, timeout);
91 if (pollRet < 0) {
92 continue;
93 } else if (pollRet == 0) {
94 HIVIEW_LOGI("poll timeout");
95 break;
96 } else {
97 if (pfds[0].revents & POLLHUP) {
98 ReadDataFromPipe(fd, log);
99 break;
100 }
101
102 if ((pfds[0].revents & POLLIN)) {
103 ReadDataFromPipe(fd, log);
104 }
105 }
106 } while (pollRet > 0 || errno == EINTR);
107 return log;
108 }
109
GetHilogByPid(int32_t pid)110 std::string FaultlogHilogHelper::GetHilogByPid(int32_t pid)
111 {
112 if (Parameter::IsOversea() && !Parameter::IsBetaVersion()) {
113 HIVIEW_LOGI("Do not get hilog in oversea commercial version.");
114 return "";
115 }
116 int fds[2] = {-1, -1}; // 2: one read pipe, one write pipe
117 if (pipe2(fds, O_NONBLOCK) != 0) {
118 HIVIEW_LOGE("Failed to create pipe for get log.");
119 return "";
120 }
121 uint64_t ownerTag = fdsan_create_owner_tag(FDSAN_OWNER_TYPE_FILE, FDSAN_DOMAIN);
122 fdsan_exchange_owner_tag(fds[0], 0, ownerTag);
123 fdsan_exchange_owner_tag(fds[1], 0, ownerTag);
124
125 int childPid = fork();
126 if (childPid < 0) {
127 HIVIEW_LOGE("fork fail");
128 return "";
129 } else if (childPid == 0) {
130 syscall(SYS_close, fds[0]);
131 int rc = DoGetHilogProcess(pid, fds[1]);
132 fdsan_close_with_tag(fds[1], ownerTag);
133 _exit(rc);
134 } else {
135 fdsan_close_with_tag(fds[1], ownerTag);
136 HIVIEW_LOGI("read hilog start");
137 std::string log = ReadHilogTimeout(fds[0]);
138 fdsan_close_with_tag(fds[0], ownerTag);
139
140 if (TEMP_FAILURE_RETRY(waitpid(childPid, nullptr, 0)) != childPid) {
141 HIVIEW_LOGE("waitpid fail, pid: %{public}d, errno: %{public}d", childPid, errno);
142 return "";
143 }
144 HIVIEW_LOGI("get hilog waitpid %{public}d success", childPid);
145 return log;
146 }
147 return "";
148 }
149
DoGetHilogProcess(int32_t pid,int writeFd)150 int FaultlogHilogHelper::DoGetHilogProcess(int32_t pid, int writeFd)
151 {
152 HIVIEW_LOGD("Start do get hilog process, pid:%{public}d", pid);
153 if (writeFd < 0 || dup2(writeFd, STDOUT_FILENO) == -1 ||
154 dup2(writeFd, STDERR_FILENO) == -1) {
155 HIVIEW_LOGE("dup2 writeFd fail");
156 return -1;
157 }
158
159 int ret = -1;
160 ret = execl("/system/bin/hilog", "hilog", "-z", "1000", "-P", std::to_string(pid).c_str(), nullptr);
161 if (ret < 0) {
162 HIVIEW_LOGE("execl %{public}d, errno: %{public}d", ret, errno);
163 return ret;
164 }
165 return 0;
166 }
167
ParseHilogToJson(const std::string & hilogStr)168 Json::Value FaultlogHilogHelper::ParseHilogToJson(const std::string &hilogStr)
169 {
170 Json::Value hilog(Json::arrayValue);
171 if (hilogStr.empty()) {
172 HIVIEW_LOGE("Get hilog is empty");
173 return hilog;
174 }
175 std::stringstream logStream(hilogStr);
176 std::string oneLine;
177 std::queue<std::string> hilogContent;
178 while (getline(logStream, oneLine)) {
179 if (hilogContent.size() == REPORT_HILOG_LINE) {
180 hilogContent.pop();
181 }
182 hilogContent.push(std::move(oneLine));
183 }
184 while (!hilogContent.empty()) {
185 hilog.append(std::move(hilogContent.front()));
186 hilogContent.pop();
187 }
188 return hilog;
189 }
190 } // namespace HiviewDFX
191 } // namespace OHOS
192