1 /*
2 * Copyright (C) 2023 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 "dmesg_catcher.h"
16
17 #include <string>
18 #include <sys/klog.h>
19 #include <fcntl.h>
20 #include <sys/stat.h>
21 #include <sys/types.h>
22
23 #ifdef KERNELSTACK_CATCHER_ENABLE
24 #include "dfx_kernel_stack.h"
25 #endif
26
27 #include "hiview_logger.h"
28 #include "log_catcher_utils.h"
29 #include "common_utils.h"
30 #include "ffrt.h"
31 #include "file_util.h"
32 #include "time_util.h"
33 #include "securec.h"
34
35 namespace OHOS {
36 namespace HiviewDFX {
37 #ifdef DMESG_CATCHER_ENABLE
38 using namespace std::chrono_literals;
39 DEFINE_LOG_LABEL(0xD002D01, "EventLogger-DmesgCatcher");
40 namespace {
41 constexpr int SYSLOG_ACTION_READ_ALL = 3;
42 constexpr int SYSLOG_ACTION_SIZE_BUFFER = 10;
43 constexpr mode_t DEFAULT_LOG_FILE_MODE = 0644;
44 static constexpr const char* const FULL_DIR = "/data/log/eventlog/";
45 static constexpr int DECIMEL = 10;
46 static constexpr int DIR_BUFFER = 256;
47 }
DmesgCatcher()48 DmesgCatcher::DmesgCatcher() : EventLogCatcher()
49 {
50 event_ = nullptr;
51 name_ = "DmesgCatcher";
52 }
53
Initialize(const std::string & packageNam __UNUSED,int writeNewFile __UNUSED,int writeType)54 bool DmesgCatcher::Initialize(const std::string& packageNam __UNUSED,
55 int writeNewFile __UNUSED, int writeType)
56 {
57 writeNewFile_ = writeNewFile;
58 writeType_ = writeType;
59 return true;
60 }
61
Init(std::shared_ptr<SysEvent> event)62 bool DmesgCatcher::Init(std::shared_ptr<SysEvent> event)
63 {
64 event_ = event;
65 return true;
66 }
67
DumpToFile(int fd,const std::string & dataStr)68 bool DmesgCatcher::DumpToFile(int fd, const std::string& dataStr)
69 {
70 bool res = false;
71 size_t lineStart = 0;
72 size_t lineEnd = dataStr.size();
73 if (writeType_ == SYS_RQ) {
74 size_t sysRqStart = dataStr.find("sysrq start:");
75 if (sysRqStart == std::string::npos) {
76 return false;
77 }
78 size_t sysRqEnd = dataStr.find("sysrq end:", sysRqStart);
79 if (sysRqEnd == std::string::npos) {
80 return false;
81 }
82 lineStart = dataStr.rfind('\n', sysRqStart);
83 lineStart = (lineStart == std::string::npos) ? 0 : lineStart + 1;
84 lineEnd = dataStr.find('\n', sysRqEnd);
85 lineEnd = (lineEnd == std::string::npos) ? dataStr.length() : lineEnd;
86 res = FileUtil::SaveStringToFd(fd, dataStr.substr(lineStart, lineEnd - lineStart + 1));
87 } else if (writeType_ == HUNG_TASK) {
88 std::string hungtaskStr;
89 while (lineStart < lineEnd) {
90 size_t seekPos = dataStr.find("hguard-worker", lineStart);
91 if (seekPos == std::string::npos) {
92 seekPos = dataStr.find("sys-lmk-debug-t", lineStart);
93 }
94 if (seekPos == std::string::npos) {
95 break;
96 }
97 lineStart = dataStr.rfind("\n", seekPos);
98 lineStart = (lineStart == std::string::npos) ? 0 : lineStart + 1;
99 lineEnd = dataStr.find("\n", seekPos);
100 lineEnd = (lineEnd == std::string::npos) ? dataStr.size() : lineEnd;
101
102 hungtaskStr.append(dataStr, lineStart, lineEnd - lineStart + 1);
103 lineStart = lineEnd + 1;
104 }
105 res = FileUtil::SaveStringToFd(fd, hungtaskStr);
106 }
107 return res;
108 }
109
DumpDmesgLog(int fd)110 bool DmesgCatcher::DumpDmesgLog(int fd)
111 {
112 if (fd < 0) {
113 return false;
114 }
115 int size = klogctl(SYSLOG_ACTION_SIZE_BUFFER, 0, 0);
116 if (size <= 0) {
117 return false;
118 }
119 char *data = (char *)malloc(size + 1);
120 if (data == nullptr) {
121 return false;
122 }
123
124 memset_s(data, size + 1, 0, size + 1);
125 int readSize = klogctl(SYSLOG_ACTION_READ_ALL, data, size);
126 if (readSize < 0) {
127 free(data);
128 return false;
129 }
130 std::string dataStr = std::string(data, size);
131 free(data);
132 bool res = (writeType_ == DMESG) ? FileUtil::SaveStringToFd(fd, dataStr) : DumpToFile(fd, dataStr);
133 return res;
134 }
135
WriteSysrqTrigger()136 bool DmesgCatcher::WriteSysrqTrigger()
137 {
138 static std::atomic<bool> isWriting(false);
139 bool expected = false;
140 if (!isWriting.compare_exchange_strong(expected, true)) {
141 HIVIEW_LOGE("other is writing sysrq trigger!");
142 ffrt::this_task::sleep_for(1s);
143 return true;
144 }
145
146 FILE* file = fopen("/proc/sysrq-trigger", "w");
147 if (file == nullptr) {
148 isWriting.store(false);
149 HIVIEW_LOGE("Can't read sysrq,errno: %{public}d", errno);
150 return false;
151 }
152
153 fwrite("w", 1, 1, file);
154 fflush(file);
155
156 fwrite("l", 1, 1, file);
157 ffrt::this_task::sleep_for(1s);
158 fclose(file);
159 isWriting.store(false);
160 return true;
161 }
162
Catch(int fd,int jsonFd)163 int DmesgCatcher::Catch(int fd, int jsonFd)
164 {
165 if (writeType_ && !WriteSysrqTrigger()) {
166 return 0;
167 }
168 description_ = (writeType_ == SYS_RQ) ? "\nSysrqCatcher -- \n" :
169 (writeType_ == HUNG_TASK) ? "\nHungTaskCatcher -- \n" : "\nDmesgCatcher -- \n";;
170 auto originSize = GetFdSize(fd);
171 FileUtil::SaveStringToFd(fd, description_);
172 DumpDmesgLog(fd);
173 logSize_ = GetFdSize(fd) - originSize;
174 return logSize_;
175 }
176 #ifdef KERNELSTACK_CATCHER_ENABLE
GetTidsByPid(int pid,std::vector<pid_t> & tids)177 void DmesgCatcher::GetTidsByPid(int pid, std::vector<pid_t>& tids)
178 {
179 char taskDir[DIR_BUFFER];
180 if (pid < 0 || sprintf_s(taskDir, sizeof(taskDir), "/proc/%d/task", pid) < 0) {
181 HIVIEW_LOGW("get tids failed, pid: %{public}d", pid);
182 return;
183 }
184
185 DIR* dir = opendir(taskDir);
186 if (dir != nullptr) {
187 struct dirent* dent;
188 while ((dent = readdir(dir)) != nullptr) {
189 char* endptr;
190 unsigned long tid = strtoul(dent->d_name, &endptr, DECIMEL);
191 if (tid == ULONG_MAX || *endptr) {
192 continue;
193 }
194 tids.push_back(tid);
195 }
196 closedir(dir);
197 }
198 }
199
DumpKernelStacktrace(int fd,int pid)200 int DmesgCatcher::DumpKernelStacktrace(int fd, int pid)
201 {
202 if (fd < 0 || pid < 0) {
203 return -1;
204 }
205 std::string msg = "";
206 std::vector<pid_t> tids;
207 GetTidsByPid(pid, tids);
208 for (auto tid : tids) {
209 std::string temp = "";
210 if (DfxGetKernelStack(tid, temp) != 0) {
211 msg = "Failed to format kernel stack for " + std::to_string(tid) + temp + "\n";
212 continue;
213 }
214 msg += temp + "\n";
215 }
216 if (msg == "") {
217 msg = "dumpCatch return empty stack!!!!";
218 }
219 FileUtil::SaveStringToFd(fd, msg);
220 return 0;
221 }
222 #endif
223
WriteNewFile(int pid)224 void DmesgCatcher::WriteNewFile(int pid)
225 {
226 if (writeType_ && !WriteSysrqTrigger()) {
227 return;
228 }
229 std::string fileType = (writeType_ == SYS_RQ) ? "sysrq" : "hungtask";
230 std::string fileTime = (writeType_ == SYS_RQ) ? event_->GetEventValue("SYSRQ_TIME") :
231 event_->GetEventValue("HUNGTASK_TIME");
232 std::string fileName = fileType + "-" + fileTime + ".log";
233 std::string fullPath = std::string(FULL_DIR) + fileName;
234 HIVIEW_LOGI("write file %{public}s start", fileName.c_str());
235 if (FileUtil::FileExists(fullPath)) {
236 HIVIEW_LOGW("filename: %{public}s is existed, direct use.", fileName.c_str());
237 return;
238 }
239 FILE* fp = fopen(fullPath.c_str(), "w");
240 chmod(fullPath.c_str(), DEFAULT_LOG_FILE_MODE);
241 if (fp == nullptr) {
242 HIVIEW_LOGI("Fail to create %{public}s, errno: %{public}d.", fileName.c_str(), errno);
243 return;
244 }
245 auto fd = fileno(fp);
246 DumpDmesgLog(fd);
247 #ifdef KERNELSTACK_CATCHER_ENABLE
248 if (writeType_ == SYS_RQ) {
249 DumpKernelStacktrace(fd, pid);
250 }
251 #endif // KERNELSTACK_CATCHER_ENABLE
252 if (fclose(fp)) {
253 HIVIEW_LOGE("fclose is failed");
254 }
255 fp = nullptr;
256 HIVIEW_LOGI("write file %{public}s end", fileName.c_str());
257 }
258 #endif // DMESG_CATCHER_ENABLE
259 } // namespace HiviewDFX
260 } // namespace OHOS
261