1 // Copyright 2017 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <inttypes.h>
6 #include <stdarg.h>
7 #include <stdint.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <threads.h>
12 
13 #include <zircon/assert.h>
14 #include <zircon/process.h>
15 #include <zircon/status.h>
16 #include <zircon/syscalls.h>
17 #include <zircon/syscalls/exception.h>
18 #include <zircon/syscalls/port.h>
19 #include <zircon/threads.h>
20 
21 #include <fbl/unique_ptr.h>
22 #include <fbl/vector.h>
23 #include <inspector/inspector.h>
24 #include <pretty/hexdump.h>
25 #include <task-utils/get.h>
26 
27 static int verbosity_level = 0;
28 
print_error(const char * fmt,...)29 void print_error(const char* fmt, ...) {
30     va_list args;
31     va_start(args, fmt);
32     fprintf(stderr, "ERROR: ");
33     vfprintf(stderr, fmt, args);
34     fprintf(stderr, "\n");
35     va_end(args);
36 }
37 
print_zx_error(zx_status_t status,const char * fmt,...)38 void print_zx_error(zx_status_t status, const char* fmt, ...) {
39     va_list args;
40     va_start(args, fmt);
41     fprintf(stderr, "ERROR: ");
42     vfprintf(stderr, fmt, args);
43     fprintf(stderr, ": %d(%s)", status, zx_status_get_string(status));
44     fprintf(stderr, "\n");
45     va_end(args);
46 }
47 
48 // While this should never fail given a valid handle,
49 // returns ZX_KOID_INVALID on failure.
get_koid(zx_handle_t handle)50 zx_koid_t get_koid(zx_handle_t handle) {
51     zx_info_handle_basic_t info;
52     if (zx_object_get_info(handle, ZX_INFO_HANDLE_BASIC, &info, sizeof(info), NULL, NULL) < 0) {
53         // This shouldn't ever happen, so don't just ignore it.
54         print_error("Eh? ZX_INFO_HANDLE_BASIC failed");
55         return ZX_KOID_INVALID;
56     }
57     return info.koid;
58 }
59 
60 // How much memory to dump, in bytes.
61 // Space for this is allocated on the stack, so this can't be too large.
62 constexpr size_t kMemoryDumpSize = 256;
63 
dump_memory(zx_handle_t proc,uintptr_t start,size_t len)64 void dump_memory(zx_handle_t proc, uintptr_t start, size_t len) {
65     // Make sure we're not allocating an excessive amount of stack.
66     ZX_DEBUG_ASSERT(len <= kMemoryDumpSize);
67 
68     uint8_t buf[len];
69     auto res = zx_process_read_memory(proc, start, buf, len, &len);
70     if (res < 0) {
71         printf("failed reading %p memory; error : %d\n", (void*)start, res);
72     } else if (len != 0) {
73         hexdump_ex(buf, len, start);
74     }
75 }
76 
dump_thread(zx_handle_t process,inspector_dsoinfo_t * dso_list,uint64_t tid,zx_handle_t thread)77 void dump_thread(zx_handle_t process, inspector_dsoinfo_t* dso_list,
78                  uint64_t tid, zx_handle_t thread) {
79     zx_thread_state_general_regs_t regs;
80     zx_vaddr_t pc = 0, sp = 0, fp = 0;
81 
82     if (inspector_read_general_regs(thread, &regs) != ZX_OK) {
83         // Error message has already been printed.
84         return;
85     }
86 
87 #if defined(__x86_64__)
88     pc = regs.rip;
89     sp = regs.rsp;
90     fp = regs.rbp;
91 #elif defined(__aarch64__)
92     pc = regs.pc;
93     sp = regs.sp;
94     fp = regs.r[29];
95 #else
96     // It's unlikely we'll get here as trying to read the regs will likely
97     // fail, but we don't assume that.
98     printf("unsupported architecture .. coming soon.\n");
99     return;
100 #endif
101 
102     char thread_name[ZX_MAX_NAME_LEN];
103     auto status = zx_object_get_property(thread, ZX_PROP_NAME, thread_name, sizeof(thread_name));
104     if (status < 0) {
105         strlcpy(thread_name, "unknown", sizeof(thread_name));
106     }
107 
108     printf("<== Thread %s[%" PRIu64 "] ==>\n", thread_name, tid);
109 
110     inspector_print_general_regs(stdout, &regs, nullptr);
111 
112     printf("bottom of user stack:\n");
113     dump_memory(process, sp, kMemoryDumpSize);
114 
115     inspector_print_backtrace(stdout, process, thread, dso_list, pc, sp, fp, true);
116 
117     if (verbosity_level >= 1)
118         printf("Done handling thread %" PRIu64 ".%" PRIu64 ".\n", get_koid(process), get_koid(thread));
119 }
120 
dump_all_threads(uint64_t pid,zx_handle_t process)121 void dump_all_threads(uint64_t pid, zx_handle_t process) {
122     // First get the thread count so that we can allocate an appropriately
123     // sized buffer. This is racy but it's the nature of the beast.
124     size_t num_threads;
125     zx_status_t status =
126         zx_object_get_info(process, ZX_INFO_PROCESS_THREADS, nullptr, 0,
127                            nullptr, &num_threads);
128     if (status != ZX_OK) {
129         print_zx_error(status, "failed to get process thread info (#threads)");
130         exit(1);
131     }
132 
133     auto threads = fbl::unique_ptr<zx_koid_t[]>(new zx_koid_t[num_threads]);
134     size_t records_read;
135     status = zx_object_get_info(process, ZX_INFO_PROCESS_THREADS,
136                                 threads.get(),
137                                 num_threads * sizeof(threads[0]),
138                                 &records_read, nullptr);
139     if (status != ZX_OK) {
140         print_zx_error(status, "failed to get process thread info");
141         exit(1);
142     }
143     ZX_DEBUG_ASSERT(records_read == num_threads);
144 
145     const char* arch = "unknown";
146 #if defined(__x86_64__)
147     arch = "x86_64";
148 #elif defined(__aarch64__)
149     arch = "aarch64";
150 #endif
151     printf("arch: %s\n", arch);
152 
153     printf("%zu thread(s)\n", num_threads);
154 
155     inspector_dsoinfo_t* dso_list = inspector_dso_fetch_list(process);
156     inspector_dso_print_list(stdout, dso_list);
157 
158     // TODO(dje): Move inspector's DebugInfoCache here, so that we can use it
159     // across all threads.
160 
161     for (size_t i = 0; i < num_threads; ++i) {
162         zx_koid_t tid = threads[i];
163         zx_handle_t thread;
164         // TODO(dje): There is value in specifying exactly the rights we need,
165         // but an explicit list this early has a higher risk of bitrot.
166         status = zx_object_get_child(process, tid, ZX_RIGHT_SAME_RIGHTS, &thread);
167         if (status < 0) {
168             printf("WARNING: failed to get a handle to [%" PRIu64 ".%" PRIu64 "] : error %d\n", pid, tid, status);
169             continue;
170         }
171 
172         zx_handle_t suspend_token = ZX_HANDLE_INVALID;
173         status = zx_task_suspend_token(thread, &suspend_token);
174         if (status != ZX_OK) {
175             print_zx_error(status, "unable to suspend thread, skipping");
176             zx_handle_close(thread);
177             continue;
178         }
179 
180         zx_signals_t observed = 0u;
181         // Try to be robust and don't wait forever. The timeout is a little
182         // high as we want to work well in really loaded systems.
183         auto deadline = zx_deadline_after(ZX_SEC(5));
184         // Currently, asking to wait for suspended means only waiting for the
185         // thread to suspend. If the thread terminates instead this will wait
186         // forever (or until the timeout). Thus we need to explicitly wait for
187         // ZX_THREAD_TERMINATED too.
188         zx_signals_t signals = ZX_THREAD_SUSPENDED | ZX_THREAD_TERMINATED;
189         status = zx_object_wait_one(thread, signals, deadline, &observed);
190         if (status == ZX_OK) {
191             if (observed & ZX_THREAD_TERMINATED) {
192                 printf("Unable to print backtrace of thread %" PRIu64 ".%" PRIu64 ": terminated\n",
193                        pid, tid);
194             } else {
195                 dump_thread(process, dso_list, tid, thread);
196             }
197         } else {
198             print_zx_error(status,
199                            "failure waiting for thread %" PRIu64 ".%" PRIu64 " to suspend, skipping",
200                            pid, tid);
201         }
202 
203         zx_handle_close(suspend_token);
204         zx_handle_close(thread);
205     }
206 
207     inspector_dso_free_list(dso_list);
208 }
209 
usage(FILE * f)210 void usage(FILE* f) {
211     fprintf(f, "Usage: threads [options] pid\n");
212     fprintf(f, "Options:\n");
213     fprintf(f, "  -v[n] = set verbosity level to N\n");
214 }
215 
main(int argc,char ** argv)216 int main(int argc, char** argv) {
217     zx_status_t status;
218     zx_koid_t pid = ZX_KOID_INVALID;
219 
220     int i;
221     for (i = 1; i < argc; ++i) {
222         const char* arg = argv[i];
223         if (arg[0] == '-') {
224             if (strcmp(arg, "-h") == 0 || strcmp(arg, "--help") == 0) {
225                 usage(stdout);
226                 return 0;
227             } else if (strncmp(arg, "-v", 2) == 0) {
228                 if (arg[2] != '\0') {
229                     verbosity_level = atoi(arg + 2);
230                 } else {
231                     verbosity_level = 1;
232                 }
233             } else {
234                 usage(stderr);
235                 return 1;
236             }
237         } else {
238             break;
239         }
240     }
241     if (i == argc || i + 1 != argc) {
242             usage(stderr);
243             return 1;
244     }
245     char *endptr;
246     const char* pidstr = argv[i];
247     pid = strtoull(pidstr, &endptr, 0);
248     if (!(pidstr[0] != '\0' && *endptr == '\0')) {
249         fprintf(stderr, "ERROR: invalid pid: %s", pidstr);
250         return 1;
251     }
252 
253     inspector_set_verbosity(verbosity_level);
254 
255     zx_handle_t thread_self = thrd_get_zx_handle(thrd_current());
256     if (thread_self == ZX_HANDLE_INVALID) {
257         print_error("unable to get thread self");
258         return 1;
259     }
260 
261     zx_handle_t process;
262     zx_obj_type_t type;
263     status = get_task_by_koid(pid, &type, &process);
264     if (status < 0) {
265         print_zx_error(status, "unable to get a handle to %" PRIu64, pid);
266         return 1;
267     }
268 
269     if (type != ZX_OBJ_TYPE_PROCESS) {
270         print_error("PID %" PRIu64 " is not a process. Threads can only be dumped from processes", pid);
271         return 1;
272     }
273 
274     char process_name[ZX_MAX_NAME_LEN];
275     status = zx_object_get_property(process, ZX_PROP_NAME, process_name, sizeof(process_name));
276     if (status < 0) {
277         strlcpy(process_name, "unknown", sizeof(process_name));
278     }
279 
280     printf("Backtrace of threads of process %" PRIu64 ": %s\n",
281            pid, process_name);
282 
283     dump_all_threads(pid, process);
284     zx_handle_close(process);
285 
286     return 0;
287 }
288