1 /** @file
2
3 A brief file description
4
5 @section license License
6
7 Licensed to the Apache Software Foundation (ASF) under one
8 or more contributor license agreements. See the NOTICE file
9 distributed with this work for additional information
10 regarding copyright ownership. The ASF licenses this file
11 to you under the Apache License, Version 2.0 (the
12 "License"); you may not use this file except in compliance
13 with the License. You may obtain a copy of the License at
14
15 http://www.apache.org/licenses/LICENSE-2.0
16
17 Unless required by applicable law or agreed to in writing, software
18 distributed under the License is distributed on an "AS IS" BASIS,
19 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 See the License for the specific language governing permissions and
21 limitations under the License.
22 */
23
24 #include "traffic_crashlog.h"
25 #include <sys/utsname.h>
26
27 static int
procfd_open(pid_t pid,const char * fname)28 procfd_open(pid_t pid, const char *fname)
29 {
30 char path[128];
31 snprintf(path, sizeof(path), "/proc/%ld/%s", static_cast<long>(pid), fname);
32 return open(path, O_RDONLY);
33 }
34
35 static char *
procfd_readlink(pid_t pid,const char * fname)36 procfd_readlink(pid_t pid, const char *fname)
37 {
38 char path[128];
39 ssize_t nbytes;
40 ats_scoped_str resolved(static_cast<char *>(ats_malloc(MAXPATHLEN + 1)));
41
42 snprintf(path, sizeof(path), "/proc/%ld/%s", static_cast<long>(pid), fname);
43 nbytes = readlink(path, resolved, MAXPATHLEN);
44 if (nbytes == -1) {
45 Note("readlink failed with %s", strerror(errno));
46 return nullptr;
47 }
48
49 resolved[nbytes] = '\0';
50 return resolved.release();
51 }
52
53 // Suck in a file from /proc/$PID and write it out with the given label.
54 static bool
write_procfd_file(const char * filename,const char * label,FILE * fp,const crashlog_target & target)55 write_procfd_file(const char *filename, const char *label, FILE *fp, const crashlog_target &target)
56 {
57 ats_scoped_fd fd;
58 TextBuffer text(0);
59 fd = procfd_open(target.pid, filename);
60 if (fd != -1) {
61 text.slurp(fd);
62 text.chomp();
63 fprintf(fp, "%s:\n%.*s\n", label, static_cast<int>(text.spaceUsed()), text.bufPtr());
64 }
65
66 return !text.empty();
67 }
68
69 bool
crashlog_write_regions(FILE * fp,const crashlog_target & target)70 crashlog_write_regions(FILE *fp, const crashlog_target &target)
71 {
72 return write_procfd_file("maps", "Memory Regions", fp, target);
73 }
74
75 bool
crashlog_write_procstatus(FILE * fp,const crashlog_target & target)76 crashlog_write_procstatus(FILE *fp, const crashlog_target &target)
77 {
78 return write_procfd_file("status", "Process Status", fp, target);
79 }
80
81 bool
crashlog_write_proclimits(FILE * fp,const crashlog_target & target)82 crashlog_write_proclimits(FILE *fp, const crashlog_target &target)
83 {
84 return write_procfd_file("limits", "Process Limits", fp, target);
85 }
86
87 bool
crashlog_write_uname(FILE * fp,const crashlog_target &)88 crashlog_write_uname(FILE *fp, const crashlog_target &)
89 {
90 struct utsname uts;
91
92 if (uname(&uts) == 0) {
93 fprintf(fp, LABELFMT "%s %s %s %s\n", "System Version:", uts.sysname, uts.machine, uts.version, uts.release);
94 } else {
95 fprintf(fp, LABELFMT "%s\n", "System Version:", "unknown");
96 }
97
98 return true;
99 }
100
101 bool
crashlog_write_exename(FILE * fp,const crashlog_target & target)102 crashlog_write_exename(FILE *fp, const crashlog_target &target)
103 {
104 ats_scoped_str str;
105
106 str = procfd_readlink(target.pid, "exe");
107 if (str) {
108 fprintf(fp, LABELFMT "%s\n", "File:", (const char *)str);
109 return true;
110 }
111
112 return false;
113 }
114
115 bool
crashlog_write_procname(FILE * fp,const crashlog_target & target)116 crashlog_write_procname(FILE *fp, const crashlog_target &target)
117 {
118 ats_scoped_fd fd;
119 ats_scoped_str str;
120 TextBuffer text(0);
121
122 fd = procfd_open(target.pid, "comm");
123 if (fd != -1) {
124 text.slurp(fd);
125 text.chomp();
126 fprintf(fp, LABELFMT "%s [%ld]\n", "Process:", text.bufPtr(), static_cast<long>(target.pid));
127 } else {
128 fprintf(fp, LABELFMT "%ld\n", "Process:", static_cast<long>(target.pid));
129 }
130
131 return true;
132 }
133
134 bool
crashlog_write_datime(FILE * fp,const crashlog_target & target)135 crashlog_write_datime(FILE *fp, const crashlog_target &target)
136 {
137 char buf[128];
138
139 strftime(buf, sizeof(buf), "%a, %d %b %Y %T %z", &target.timestamp);
140 fprintf(fp, LABELFMT "%s\n", "Date:", buf);
141 return true;
142 }
143
144 bool
crashlog_write_backtrace(FILE * fp,const crashlog_target &)145 crashlog_write_backtrace(FILE *fp, const crashlog_target &)
146 {
147 TSString trace = nullptr;
148 TSMgmtError mgmterr;
149
150 // NOTE: sometimes we can't get a backtrace because the ptrace attach will fail with
151 // EPERM. I've seen this happen when a debugger is attached, which makes sense, but it
152 // can also happen without a debugger. Possibly in that case, there is a race with the
153 // kernel locking the process information?
154
155 if ((mgmterr = TSProxyBacktraceGet(0, &trace)) != TS_ERR_OKAY) {
156 char *msg = TSGetErrorMessage(mgmterr);
157 fprintf(fp, "Unable to retrieve backtrace: %s\n", msg);
158 TSfree(msg);
159 return false;
160 }
161
162 fprintf(fp, "%s", trace);
163 TSfree(trace);
164 return true;
165 }
166
167 bool
crashlog_write_records(FILE * fp,const crashlog_target &)168 crashlog_write_records(FILE *fp, const crashlog_target &)
169 {
170 TSMgmtError mgmterr;
171 TSList list = TSListCreate();
172 bool success = false;
173
174 if ((mgmterr = TSRecordGetMatchMlt(".", list)) != TS_ERR_OKAY) {
175 char *msg = TSGetErrorMessage(mgmterr);
176 fprintf(fp, "Unable to retrieve Traffic Server records: %s\n", msg);
177 TSfree(msg);
178 goto done;
179 }
180
181 // If the RPC call failed, the list will be empty, so we won't print anything. Otherwise,
182 // print all the results, freeing them as we go.
183 for (TSRecordEle *rec_ele = (TSRecordEle *)TSListDequeue(list); rec_ele; rec_ele = (TSRecordEle *)TSListDequeue(list)) {
184 if (!success) {
185 success = true;
186 fprintf(fp, "Traffic Server Configuration Records:\n");
187 }
188
189 switch (rec_ele->rec_type) {
190 case TS_REC_INT:
191 fprintf(fp, "%s %" PRId64 "\n", rec_ele->rec_name, rec_ele->valueT.int_val);
192 break;
193 case TS_REC_COUNTER:
194 fprintf(fp, "%s %" PRId64 "\n", rec_ele->rec_name, rec_ele->valueT.counter_val);
195 break;
196 case TS_REC_FLOAT:
197 fprintf(fp, "%s %f\n", rec_ele->rec_name, rec_ele->valueT.float_val);
198 break;
199 case TS_REC_STRING:
200 fprintf(fp, "%s %s\n", rec_ele->rec_name, rec_ele->valueT.string_val);
201 break;
202 default:
203 // just skip it ...
204 break;
205 }
206
207 TSRecordEleDestroy(rec_ele);
208 }
209
210 done:
211 TSListDestroy(list);
212 return success;
213 }
214
215 bool
crashlog_write_siginfo(FILE * fp,const crashlog_target & target)216 crashlog_write_siginfo(FILE *fp, const crashlog_target &target)
217 {
218 char tmp[32];
219
220 if (!(CRASHLOG_HAVE_THREADINFO & target.flags)) {
221 fprintf(fp, "No target signal information\n");
222 return false;
223 }
224
225 fprintf(fp, "Signal Status:\n");
226 fprintf(fp, LABELFMT "%d (%s)\n", "siginfo.si_signo:", target.siginfo.si_signo, strsignal(target.siginfo.si_signo));
227
228 snprintf(tmp, sizeof(tmp), "%ld", static_cast<long>(target.siginfo.si_pid));
229 fprintf(fp, LABELFMT LABELFMT, "siginfo.si_pid:", tmp);
230 fprintf(fp, LABELFMT "%ld", "siginfo.si_uid:", static_cast<long>(target.siginfo.si_uid));
231 fprintf(fp, "\n");
232
233 snprintf(tmp, sizeof(tmp), "0x%x (%d)", target.siginfo.si_code, target.siginfo.si_code);
234 fprintf(fp, LABELFMT LABELFMT, "siginfo.si_code:", tmp);
235 fprintf(fp, LABELFMT ADDRFMT, "siginfo.si_addr:", ADDRCAST(target.siginfo.si_addr));
236 fprintf(fp, "\n");
237
238 if (target.siginfo.si_code == SI_USER) {
239 fprintf(fp, "Signal delivered by user %ld from process %ld\n", static_cast<long>(target.siginfo.si_uid),
240 static_cast<long>(target.siginfo.si_pid));
241 return true;
242 }
243
244 if (target.siginfo.si_signo == SIGSEGV) {
245 const char *msg = "Unknown error";
246
247 switch (target.siginfo.si_code) {
248 case SEGV_MAPERR:
249 msg = "No object mapped";
250 break;
251 case SEGV_ACCERR:
252 msg = "Invalid permissions for mapped object";
253 break;
254 }
255
256 fprintf(fp, "%s at address " ADDRFMT "\n", msg, ADDRCAST(target.siginfo.si_addr));
257 return true;
258 }
259
260 if (target.siginfo.si_signo == SIGBUS) {
261 const char *msg = "Unknown error";
262
263 switch (target.siginfo.si_code) {
264 case BUS_ADRALN:
265 msg = "Invalid address alignment";
266 break;
267 case BUS_ADRERR:
268 msg = "Nonexistent physical address";
269 break;
270 case BUS_OBJERR:
271 msg = "Object-specific hardware error";
272 break;
273 }
274
275 fprintf(fp, "%s at address " ADDRFMT "\n", msg, ADDRCAST(target.siginfo.si_addr));
276 return true;
277 }
278
279 return true;
280 }
281
282 bool
crashlog_write_registers(FILE * fp,const crashlog_target & target)283 crashlog_write_registers(FILE *fp, const crashlog_target &target)
284 {
285 if (!(CRASHLOG_HAVE_THREADINFO & target.flags)) {
286 fprintf(fp, "No target CPU registers\n");
287 return false;
288 }
289
290 #if defined(__linux__) && (defined(__x86_64__) || defined(__i386__))
291
292 // x86 register names as per ucontext.h.
293 #if defined(__i386__)
294 #define REGFMT "0x%08" PRIx32
295 #define REGCAST(x) ((uint32_t)(x))
296 static const char *names[NGREG] = {
297 "GS", "FS", "ES", "DS", "EDI", "ESI", "EBP", "ESP", "EBX", "EDX",
298 "ECX", "EAX", "TRAPNO", "ERR", "EIP", "CS", "EFL", "UESP", "SS",
299 };
300 #endif
301
302 #if defined(__x86_64__)
303 #define REGFMT "0x%016" PRIx64
304 #define REGCAST(x) ((uint64_t)(x))
305 static const char *names[NGREG] = {
306 "R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15", "RDI", "RSI", "RBP", "RBX",
307 "RDX", "RAX", "RCX", "RSP", "RIP", "EFL", "CSGSFS", "ERR", "TRAPNO", "OLDMASK", "CR2",
308 };
309 #endif
310
311 fprintf(fp, "CPU Registers:\n");
312 for (unsigned i = 0; i < countof(names); ++i) {
313 const char *trailer = ((i % 4) == 3) ? "\n" : " ";
314 fprintf(fp, "%-3s:" REGFMT "%s", names[i], REGCAST(target.ucontext.uc_mcontext.gregs[i]), trailer);
315 }
316
317 fprintf(fp, "\n");
318 return true;
319 #else
320 fprintf(fp, "No target CPU register support on this architecture\n");
321 return false;
322 #endif
323 }
324