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 27static int verbosity_level = 0; 28 29void 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 38void 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. 50zx_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. 62constexpr size_t kMemoryDumpSize = 256; 63 64void 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 77void 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, ®s) != 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, ®s, 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 121void 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 210void 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 216int 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