1 /*
2 * Copyright (C) 2018-2022 Intel Corporation.
3 * SPDX-License-Identifier: BSD-3-Clause
4 */
5
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <signal.h>
9 #include <string.h>
10 #include <unistd.h>
11 #include <stdarg.h>
12 #include "log_sys.h"
13 #include "crash_dump.h"
14 #include "cmdutils.h"
15 #include "fsutils.h"
16 #include "strutils.h"
17
18 #define GET_TID "/proc/%d/status"
19 #define GET_COMM "/proc/%d/exe"
20 #define GET_K_STACK "/proc/%d/stack"
21 #define GET_MAPS "/proc/%d/maps"
22 #define GET_OPEN_FILES "/proc/%d/fd"
23 #define DEBUGGER_SIGNAL (__SIGRTMIN + 3)
24 #define DUMP_FILE "/tmp/core"
25 #define BUFFER_SIZE 8196
26 #define LINK_LEN 512
27 #define TID "Tgid:"
28 /* 128 means the length of the DUMP_FILE */
29 #define FORMAT_LENGTH (LINK_LEN + 128)
30
loginfo(int fd,const char * fmt,...)31 static void loginfo(int fd, const char *fmt, ...)
32 {
33 char *buf;
34 va_list ap;
35 int len, ret;
36
37 va_start(ap, fmt);
38 len = vasprintf(&buf, fmt, ap);
39 va_end(ap);
40 if (len == -1) {
41 LOGE("write to buf failed\n");
42 return;
43 }
44
45 ret = write(fd, buf, len);
46 if (ret != len) {
47 LOGE("write in loginfo failed\n");
48 }
49 free(buf);
50 }
51
get_signame(int sig)52 static const char *get_signame(int sig)
53 {
54 switch (sig) {
55 case SIGABRT:
56 return "SIGABRT";
57 case SIGBUS:
58 return "SIGBUS";
59 case SIGFPE:
60 return "SIGFPE";
61 case SIGILL:
62 return "SIGILL";
63 case SIGSEGV:
64 return "SIGSEGV";
65 #if defined(SIGSTKFLT)
66 case SIGSTKFLT:
67 return "SIGSTKFLT";
68 #endif
69 case SIGSTOP:
70 return "SIGSTOP";
71 case SIGSYS:
72 return "SIGSYS";
73 case SIGTRAP:
74 return "SIGTRAP";
75 case DEBUGGER_SIGNAL:
76 return "<debuggerd signal>";
77 default:
78 return "?";
79 }
80 }
81
82 /**
83 * Save core dump to file.
84 * @filename: core dump file name
85 * return 0 on success, or -1 on error
86 */
save_coredump(const char * filename)87 static int save_coredump(const char *filename)
88 {
89 size_t read_len;
90 size_t write_len;
91 char input_buffer[BUFFER_SIZE];
92 FILE *dump_file;
93
94 /* open dump file in write and binary mode */
95 dump_file = fopen(filename, "wb");
96 if (dump_file == NULL) {
97 LOGE("fopen core file failed\n");
98 return -1;
99 }
100 /* Read from STDIN and write to dump file */
101 while (1) {
102 read_len = fread(input_buffer, 1, BUFFER_SIZE, stdin);
103 if (read_len == 0)
104 break;
105 write_len = fwrite(input_buffer, 1, read_len, dump_file);
106 if (write_len == 0) {
107 LOGE("fwrite error\n");
108 fclose(dump_file);
109 return -1;
110 }
111 }
112
113 fclose(dump_file);
114 return 0;
115 }
116
get_backtrace(int pid,int fd,int sig,const char * comm)117 static int get_backtrace(int pid, int fd, int sig, const char *comm)
118 {
119 char *membkt;
120 char format[FORMAT_LENGTH];
121 size_t len, ret;
122 int flen;
123
124 loginfo(fd, "\nBackTrace:\n\n");
125 memset(format, 0, sizeof(format));
126 if (sig == DEBUGGER_SIGNAL) {
127 flen = snprintf(format, sizeof(format), "-p %d", pid);
128 } else {
129 flen = snprintf(format, sizeof(format), "%s %s", comm,
130 DUMP_FILE);
131 if (save_coredump(DUMP_FILE) == -1) {
132 LOGE("save core file failed\n");
133 return -1;
134 }
135 }
136 if (s_not_expect(flen, sizeof(format))) {
137 LOGE("failed to generate format\n");
138 return -1;
139 }
140 len = exec_out2mem(&membkt, GET_GDB_INFO, format);
141 if (len <= 0) {
142 LOGE("get gdb info failed\n");
143 return -1;
144 }
145 ret = write(fd, membkt, len);
146 free(membkt);
147 if (ret != len) {
148 LOGE("write file failed\n");
149 return -1;
150 }
151 return 0;
152
153 }
154
155 /**
156 * Save process proc info to file.
157 * @pid: process pid
158 * @fd: usercrash file fd
159 * @path: process proc path
160 * @name: a string save to file
161 * return 0 on success, or -1 on error
162 */
save_proc_info(int pid,int fd,const char * path,const char * name)163 static int save_proc_info(int pid, int fd, const char *path, const char *name)
164 {
165 int ret;
166 char *data;
167 char format[128];
168 unsigned long size;
169
170 loginfo(fd, "\n%s:\n\n", name);
171 memset(format, 0, sizeof(format));
172 ret = snprintf(format, sizeof(format), path, pid);
173 if (s_not_expect(ret, sizeof(format))) {
174 LOGE("failed to generate format");
175 return -1;
176 }
177 ret = read_file(format, &size, (void *)&data);
178 if (ret) {
179 LOGE("read file failed\n");
180 return -1;
181 }
182 ret = write(fd, data, size);
183 if ((unsigned long)ret != size) {
184 LOGE("write file failed\n");
185 return -1;
186 }
187 free(data);
188 return 0;
189
190 }
191
get_openfiles(int pid,int fd,const char * path,const char * name)192 static int get_openfiles(int pid, int fd, const char *path, const char *name)
193 {
194 int ret;
195 int loop;
196 int fdcount;
197 char *fdname;
198 char format[128];
199 char *files[256];
200 char linkname[LINK_LEN];
201
202 loginfo(fd, "\n%s:\n\n", name);
203 memset(format, 0, sizeof(format));
204 ret = snprintf(format, sizeof(format), path, pid);
205 if (s_not_expect(ret, sizeof(format))) {
206 LOGE("failed to generate format");
207 return -1;
208 }
209 fdcount = lsdir(format, files, ARRAY_SIZE(files));
210 if (fdcount < 0) {
211 LOGE("get fd list failed\n");
212 return -1;
213 }
214 for (loop = 2; loop < fdcount; loop++) {
215 memset(linkname, 0, LINK_LEN);
216 ret = readlink(files[loop], linkname, LINK_LEN);
217 if (ret < 0 || ret >= LINK_LEN) {
218 LOGE("get fd link fd failed\n");
219 continue;
220 }
221 fdname = strrchr(files[loop], '/') + 1;
222 loginfo(fd, "%s -> %s\n", fdname, linkname);
223 }
224 for (loop = 0; loop < fdcount; loop++)
225 free(files[loop]);
226
227 return 0;
228 }
229
save_usercrash_file(int pid,int tgid,const char * comm,int sig,int out_fd)230 static int save_usercrash_file(int pid, int tgid, const char *comm,
231 int sig, int out_fd)
232 {
233 int ret;
234
235 loginfo(out_fd, "*** *** *** *** *** *** *** *** *** *** *** *** *** ");
236 loginfo(out_fd, "*** *** ***\n");
237 loginfo(out_fd, "pid: %d, tgid: %d, comm: %s\n\n\n", pid, tgid, comm);
238 loginfo(out_fd, "signal: %d (%s)\n", sig, get_signame(sig));
239
240 ret = save_proc_info(pid, out_fd, GET_K_STACK, "Stack");
241 if (ret) {
242 LOGE("save stack failed\n");
243 return -1;
244 }
245
246 ret = save_proc_info(pid, out_fd, GET_MAPS, "Maps");
247 if (ret) {
248 LOGE("save maps failed\n");
249 return -1;
250 }
251
252 ret = get_openfiles(pid, out_fd, GET_OPEN_FILES, "Open files");
253 if (ret) {
254 LOGE("save openfiles failed\n");
255 return -1;
256 }
257
258 ret = get_backtrace(pid, out_fd, sig, comm);
259 if (ret) {
260 LOGE("save backtrace failed\n");
261 return -1;
262 }
263
264 return 0;
265 }
266
get_key_value(int pid,const char * path,const char * key,const size_t klen,char * value,const size_t value_len)267 static int get_key_value(int pid, const char *path, const char *key,
268 const size_t klen, char *value, const size_t value_len)
269 {
270 int len;
271 int ret;
272 unsigned long size;
273 char *data;
274 char *msg;
275 char *start;
276 char *end;
277 char format[128];
278
279 memset(format, 0, sizeof(format));
280 ret = snprintf(format, sizeof(format), path, pid);
281 if (s_not_expect(ret, sizeof(format))) {
282 LOGE("failed to generate format");
283 return -1;
284 }
285 ret = read_file(format, &size, (void *)&data);
286 if (ret || !data) {
287 LOGE("read file failed\n");
288 return -1;
289 }
290 msg = strstr(data, key);
291 if (!msg) {
292 LOGE("find key:%s failed\n", key);
293 free(data);
294 return -1;
295 }
296 end = strchr(msg, '\n');
297 if (end == NULL)
298 end = data + size;
299 start = msg + klen;
300 len = end - start;
301 if (len >= (int)value_len) {
302 free(data);
303 return -1;
304 }
305 memcpy(value, start, len);
306 *(value + len) = 0;
307 free(data);
308
309 return 0;
310 }
311
312 /**
313 * Get and save process info to out_fd.
314 * @pid: process pid
315 * @sig: signal from core dump
316 * @out_fd: file fd to save info
317 */
crash_dump(int pid,int sig,int out_fd)318 void crash_dump(int pid, int sig, int out_fd)
319 {
320 int tgid;
321 int ret;
322 char comm[LINK_LEN];
323 char result[16];
324 char format[128];
325
326 memset(format, 0, sizeof(format));
327 ret = snprintf(format, sizeof(format), GET_COMM, pid);
328 if (s_not_expect(ret, sizeof(format))) {
329 LOGE("failed to generate format\n");
330 return;
331 }
332 ret = readlink(format, comm, LINK_LEN);
333 if (ret < 0 || ret >= LINK_LEN) {
334 LOGE("get process exe link failed\n");
335 return;
336 }
337 comm[ret] = '\0';
338
339 ret = get_key_value(pid, GET_TID, TID, strlen(TID),
340 result, sizeof(result));
341 if (ret) {
342 LOGE("get Tgid error\n");
343 return;
344 }
345 tgid = (int)strtol(result, NULL, 10);
346 if (!sig)
347 sig = DEBUGGER_SIGNAL;
348 if (save_usercrash_file(pid, tgid, comm, sig, out_fd))
349 LOGE("dump log file failed\n");
350 }
351