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