1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <mach/mach.h> 5 #include <mach/task_info.h> 6 #include <time.h> 7 #include <sys/sysctl.h> 8 #include <ctype.h> 9 #include <libproc.h> 10 #include <errno.h> 11 12 /* Step through the process table, find a matching process name, return 13 the pid of that matched process. 14 If there are multiple processes with that name, issue a warning on stdout 15 and return the highest numbered process. 16 The proc_pidpath() call is used which gets the full process name including 17 directories to the executable and the full (longer than 16 character) 18 executable name. */ 19 20 pid_t 21 get_pid_for_process_name (const char *procname) 22 { 23 int process_count = proc_listpids (PROC_ALL_PIDS, 0, NULL, 0) / sizeof (pid_t); 24 if (process_count < 1) 25 { 26 printf ("Only found %d processes running!\n", process_count); 27 exit (1); 28 } 29 30 // Allocate a few extra slots in case new processes are spawned 31 int all_pids_size = sizeof (pid_t) * (process_count + 3); 32 pid_t *all_pids = (pid_t *) malloc (all_pids_size); 33 34 // re-set process_count in case the number of processes changed (got smaller; we won't do bigger) 35 process_count = proc_listpids (PROC_ALL_PIDS, 0, all_pids, all_pids_size) / sizeof (pid_t); 36 37 int i; 38 pid_t highest_pid = 0; 39 int match_count = 0; 40 for (i = 1; i < process_count; i++) 41 { 42 char pidpath[PATH_MAX]; 43 int pidpath_len = proc_pidpath (all_pids[i], pidpath, sizeof (pidpath)); 44 if (pidpath_len == 0) 45 continue; 46 char *j = strrchr (pidpath, '/'); 47 if ((j == NULL && strcmp (procname, pidpath) == 0) 48 || (j != NULL && strcmp (j + 1, procname) == 0)) 49 { 50 match_count++; 51 if (all_pids[i] > highest_pid) 52 highest_pid = all_pids[i]; 53 } 54 } 55 free (all_pids); 56 57 if (match_count == 0) 58 { 59 printf ("Did not find process '%s'.\n", procname); 60 exit (1); 61 } 62 if (match_count > 1) 63 { 64 printf ("Warning: More than one process '%s'!\n", procname); 65 printf (" defaulting to the highest-pid one, %d\n", highest_pid); 66 } 67 return highest_pid; 68 } 69 70 /* Given a pid, get the full executable name (including directory 71 paths and the longer-than-16-chars executable name) and return 72 the basename of that (i.e. do not include the directory components). 73 This function mallocs the memory for the string it returns; 74 the caller must free this memory. */ 75 76 const char * 77 get_process_name_for_pid (pid_t pid) 78 { 79 char tmp_name[PATH_MAX]; 80 if (proc_pidpath (pid, tmp_name, sizeof (tmp_name)) == 0) 81 { 82 printf ("Could not find process with pid of %d\n", (int) pid); 83 exit (1); 84 } 85 if (strrchr (tmp_name, '/')) 86 return strdup (strrchr (tmp_name, '/') + 1); 87 else 88 return strdup (tmp_name); 89 } 90 91 /* Get a struct kinfo_proc structure for a given pid. 92 Process name is required for error printing. 93 Gives you the current state of the process and whether it is being debugged by anyone. 94 memory is malloc()'ed for the returned struct kinfo_proc 95 and must be freed by the caller. */ 96 97 struct kinfo_proc * 98 get_kinfo_proc_for_pid (pid_t pid, const char *process_name) 99 { 100 struct kinfo_proc *kinfo = (struct kinfo_proc *) malloc (sizeof (struct kinfo_proc)); 101 int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, pid }; 102 size_t len = sizeof (struct kinfo_proc); 103 if (sysctl (mib, sizeof (mib) / sizeof (mib[0]), kinfo, &len, NULL, 0) != 0) 104 { 105 free ((void *) kinfo); 106 printf ("Could not get kinfo_proc for pid %d\n", (int) pid); 107 exit (1); 108 } 109 return kinfo; 110 } 111 112 /* Get the basic information (thread_basic_info_t) about a given 113 thread. 114 Gives you the suspend count; thread state; user time; system time; sleep time; etc. 115 The return value is a pointer to malloc'ed memory - it is the caller's 116 responsibility to free it. */ 117 118 thread_basic_info_t 119 get_thread_basic_info (thread_t thread) 120 { 121 kern_return_t kr; 122 integer_t *thinfo = (integer_t *) malloc (sizeof (integer_t) * THREAD_INFO_MAX); 123 mach_msg_type_number_t thread_info_count = THREAD_INFO_MAX; 124 kr = thread_info (thread, THREAD_BASIC_INFO, 125 (thread_info_t) thinfo, &thread_info_count); 126 if (kr != KERN_SUCCESS) 127 { 128 printf ("Error - unable to get basic thread info for a thread\n"); 129 exit (1); 130 } 131 return (thread_basic_info_t) thinfo; 132 } 133 134 /* Get the thread identifier info (thread_identifier_info_data_t) 135 about a given thread. 136 Gives you the system-wide unique thread number; the pthread identifier number 137 */ 138 139 thread_identifier_info_data_t 140 get_thread_identifier_info (thread_t thread) 141 { 142 kern_return_t kr; 143 thread_identifier_info_data_t tident; 144 mach_msg_type_number_t tident_count = THREAD_IDENTIFIER_INFO_COUNT; 145 kr = thread_info (thread, THREAD_IDENTIFIER_INFO, 146 (thread_info_t) &tident, &tident_count); 147 if (kr != KERN_SUCCESS) 148 { 149 printf ("Error - unable to get thread ident for a thread\n"); 150 exit (1); 151 } 152 return tident; 153 } 154 155 156 /* Given a mach port # (in the examine-threads mach port namespace) for a thread, 157 find the mach port # in the inferior program's port namespace. 158 Sets inferior_port if successful. 159 Returns true if successful, false if unable to find the port number. */ 160 161 bool 162 inferior_namespace_mach_port_num (task_t task, thread_t examine_threads_port, thread_t *inferior_port) 163 { 164 kern_return_t retval; 165 mach_port_name_array_t names; 166 mach_msg_type_number_t nameslen; 167 mach_port_type_array_t types; 168 mach_msg_type_number_t typeslen; 169 170 if (inferior_port == NULL) 171 return false; 172 173 retval = mach_port_names (task, &names, &nameslen, &types, &typeslen); 174 if (retval != KERN_SUCCESS) 175 { 176 printf ("Error - unable to get mach port names for inferior.\n"); 177 return false; 178 } 179 int i = 0; 180 for (i = 0; i < nameslen; i++) 181 { 182 mach_port_t local_name; 183 mach_msg_type_name_t local_type; 184 retval = mach_port_extract_right (task, names[i], MACH_MSG_TYPE_COPY_SEND, &local_name, &local_type); 185 if (retval == KERN_SUCCESS) 186 { 187 mach_port_deallocate (mach_task_self(), local_name); 188 if (local_name == examine_threads_port) 189 { 190 *inferior_port = names[i]; 191 vm_deallocate (mach_task_self (), (vm_address_t) names, nameslen * sizeof (mach_port_t)); 192 vm_deallocate (mach_task_self (), (vm_address_t) types, typeslen * sizeof (mach_port_t)); 193 return true; 194 } 195 } 196 } 197 vm_deallocate (mach_task_self (), (vm_address_t) names, nameslen * sizeof (mach_port_t)); 198 vm_deallocate (mach_task_self (), (vm_address_t) types, typeslen * sizeof (mach_port_t)); 199 return false; 200 } 201 202 /* Get the current pc value for a given thread. */ 203 204 uint64_t 205 get_current_pc (thread_t thread, int *wordsize) 206 { 207 kern_return_t kr; 208 209 #if defined (__x86_64__) || defined (__i386__) 210 x86_thread_state_t gp_regs; 211 mach_msg_type_number_t gp_count = x86_THREAD_STATE_COUNT; 212 kr = thread_get_state (thread, x86_THREAD_STATE, 213 (thread_state_t) &gp_regs, &gp_count); 214 if (kr != KERN_SUCCESS) 215 { 216 printf ("Error - unable to get registers for a thread\n"); 217 exit (1); 218 } 219 220 if (gp_regs.tsh.flavor == x86_THREAD_STATE64) 221 { 222 *wordsize = 8; 223 return gp_regs.uts.ts64.__rip; 224 } 225 else 226 { 227 *wordsize = 4; 228 return gp_regs.uts.ts32.__eip; 229 } 230 #endif 231 232 #if defined (__arm__) 233 arm_thread_state_t gp_regs; 234 mach_msg_type_number_t gp_count = ARM_THREAD_STATE_COUNT; 235 kr = thread_get_state (thread, ARM_THREAD_STATE, 236 (thread_state_t) &gp_regs, &gp_count); 237 if (kr != KERN_SUCCESS) 238 { 239 printf ("Error - unable to get registers for a thread\n"); 240 exit (1); 241 } 242 return gp_regs.__pc; 243 *wordsize = 4; 244 #endif 245 246 } 247 248 /* Get the proc_threadinfo for a given thread. 249 Gives you the thread name, if set; current and max priorities. 250 Returns 1 if successful 251 Returns 0 if proc_pidinfo() failed 252 */ 253 254 int 255 get_proc_threadinfo (pid_t pid, uint64_t thread_handle, struct proc_threadinfo *pth) 256 { 257 pth->pth_name[0] = '\0'; 258 int ret = proc_pidinfo (pid, PROC_PIDTHREADINFO, thread_handle, 259 pth, sizeof (struct proc_threadinfo)); 260 if (ret != 0) 261 return 1; 262 else 263 return 0; 264 } 265 266 int 267 main (int argc, char **argv) 268 { 269 kern_return_t kr; 270 task_t task; 271 thread_t thread; 272 pid_t pid = 0; 273 char *procname = NULL; 274 int arg_is_procname = 0; 275 int do_loop = 0; 276 int verbose = 0; 277 int resume_when_done = 0; 278 mach_port_t mytask = mach_task_self (); 279 280 if (argc != 2 && argc != 3 && argc != 4 && argc != 5) 281 { 282 printf ("Usage: tdump [-l] [-v] [-r] pid/procname\n"); 283 exit (1); 284 } 285 286 if (argc == 3 || argc == 4) 287 { 288 int i = 1; 289 while (i < argc - 1) 290 { 291 if (strcmp (argv[i], "-l") == 0) 292 do_loop = 1; 293 if (strcmp (argv[i], "-v") == 0) 294 verbose = 1; 295 if (strcmp (argv[i], "-r") == 0) 296 resume_when_done++; 297 i++; 298 } 299 } 300 301 char *c = argv[argc - 1]; 302 if (*c == '\0') 303 { 304 printf ("Usage: tdump [-l] [-v] pid/procname\n"); 305 exit (1); 306 } 307 while (*c != '\0') 308 { 309 if (!isdigit (*c)) 310 { 311 arg_is_procname = 1; 312 procname = argv[argc - 1]; 313 break; 314 } 315 c++; 316 } 317 318 if (arg_is_procname && procname) 319 { 320 pid = get_pid_for_process_name (procname); 321 } 322 else 323 { 324 errno = 0; 325 pid = (pid_t) strtol (argv[argc - 1], NULL, 10); 326 if (pid == 0 && errno == EINVAL) 327 { 328 printf ("Usage: tdump [-l] [-v] pid/procname\n"); 329 exit (1); 330 } 331 } 332 333 const char *process_name = get_process_name_for_pid (pid); 334 335 // At this point "pid" is the process id and "process_name" is the process name 336 // Now we have to get the process list from the kernel (which only has the truncated 337 // 16 char names) 338 339 struct kinfo_proc *kinfo = get_kinfo_proc_for_pid (pid, process_name); 340 341 printf ("pid %d (%s) is currently ", pid, process_name); 342 switch (kinfo->kp_proc.p_stat) { 343 case SIDL: printf ("being created by fork"); break; 344 case SRUN: printf ("runnable"); break; 345 case SSLEEP: printf ("sleeping on an address"); break; 346 case SSTOP: printf ("suspended"); break; 347 case SZOMB: printf ("zombie state - awaiting collection by parent"); break; 348 default: printf ("unknown"); 349 } 350 if (kinfo->kp_proc.p_flag & P_TRACED) 351 printf (" and is being debugged."); 352 free ((void *) kinfo); 353 354 printf ("\n"); 355 356 kr = task_for_pid (mach_task_self (), pid, &task); 357 if (kr != KERN_SUCCESS) 358 { 359 printf ("Error - unable to task_for_pid()\n"); 360 exit (1); 361 } 362 363 struct task_basic_info info; 364 unsigned int info_count = TASK_BASIC_INFO_COUNT; 365 366 kr = task_info (task, TASK_BASIC_INFO, (task_info_t) &info, &info_count); 367 if (kr != KERN_SUCCESS) 368 { 369 printf ("Error - unable to call task_info.\n"); 370 exit (1); 371 } 372 printf ("Task suspend count: %d.\n", info.suspend_count); 373 374 struct timespec *rqtp = (struct timespec *) malloc (sizeof (struct timespec)); 375 rqtp->tv_sec = 0; 376 rqtp->tv_nsec = 150000000; 377 378 int loop_cnt = 1; 379 do 380 { 381 int i; 382 if (do_loop) 383 printf ("Iteration %d:\n", loop_cnt++); 384 thread_array_t thread_list; 385 mach_msg_type_number_t thread_count; 386 387 kr = task_threads (task, &thread_list, &thread_count); 388 if (kr != KERN_SUCCESS) 389 { 390 printf ("Error - unable to get thread list\n"); 391 exit (1); 392 } 393 printf ("pid %d has %d threads\n", pid, thread_count); 394 if (verbose) 395 printf ("\n"); 396 397 for (i = 0; i < thread_count; i++) 398 { 399 thread_basic_info_t basic_info = get_thread_basic_info (thread_list[i]); 400 401 thread_identifier_info_data_t identifier_info = get_thread_identifier_info (thread_list[i]); 402 403 int wordsize; 404 uint64_t pc = get_current_pc (thread_list[i], &wordsize); 405 406 printf ("thread #%d, system-wide-unique-tid %lld, suspend count is %d, ", i, 407 identifier_info.thread_id, 408 basic_info->suspend_count); 409 if (wordsize == 8) 410 printf ("pc 0x%016llx, ", pc); 411 else 412 printf ("pc 0x%08llx, ", pc); 413 printf ("run state is "); 414 switch (basic_info->run_state) { 415 case TH_STATE_RUNNING: puts ("running"); break; 416 case TH_STATE_STOPPED: puts ("stopped"); break; 417 case TH_STATE_WAITING: puts ("waiting"); break; 418 case TH_STATE_UNINTERRUPTIBLE: puts ("uninterruptible"); break; 419 case TH_STATE_HALTED: puts ("halted"); break; 420 default: puts (""); 421 } 422 if (verbose) 423 { 424 printf (" (examine-threads port namespace) mach port # 0x%4.4x\n", (int) thread_list[i]); 425 thread_t mach_port_inferior_namespace; 426 if (inferior_namespace_mach_port_num (task, thread_list[i], &mach_port_inferior_namespace)) 427 printf (" (inferior port namepsace) mach port # 0x%4.4x\n", (int) mach_port_inferior_namespace); 428 printf (" pthread handle id 0x%llx\n", (uint64_t) identifier_info.thread_handle); 429 430 struct proc_threadinfo pth; 431 int proc_threadinfo_succeeded = get_proc_threadinfo (pid, identifier_info.thread_handle, &pth); 432 433 if (proc_threadinfo_succeeded && pth.pth_name[0] != '\0') 434 printf (" thread name '%s' ", pth.pth_name); 435 436 printf (" user %d.%06ds, system %d.%06ds", 437 basic_info->user_time.seconds, basic_info->user_time.microseconds, 438 basic_info->system_time.seconds, basic_info->system_time.microseconds); 439 if (basic_info->cpu_usage > 0) 440 { 441 float cpu_percentage = basic_info->cpu_usage / 10.0; 442 printf (", using %.1f%% cpu currently", cpu_percentage); 443 } 444 if (basic_info->sleep_time > 0) 445 printf (", this thread has slept for %d seconds", basic_info->sleep_time); 446 447 printf ("\n "); 448 printf ("scheduling policy %d", basic_info->policy); 449 450 if (basic_info->flags != 0) 451 { 452 printf (", flags %d", basic_info->flags); 453 if ((basic_info->flags | TH_FLAGS_SWAPPED) == TH_FLAGS_SWAPPED) 454 printf (" (thread is swapped out)"); 455 if ((basic_info->flags | TH_FLAGS_IDLE) == TH_FLAGS_IDLE) 456 printf (" (thread is idle)"); 457 } 458 if (proc_threadinfo_succeeded) 459 printf (", current pri %d, max pri %d", pth.pth_curpri, pth.pth_maxpriority); 460 461 printf ("\n\n"); 462 } 463 464 free ((void *) basic_info); 465 } 466 if (do_loop) 467 printf ("\n"); 468 vm_deallocate (mytask, (vm_address_t) thread_list, 469 thread_count * sizeof (thread_act_t)); 470 nanosleep (rqtp, NULL); 471 } while (do_loop); 472 473 while (resume_when_done > 0) 474 { 475 kern_return_t err = task_resume (task); 476 if (err != KERN_SUCCESS) 477 printf ("Error resuming task: %d.", err); 478 resume_when_done--; 479 } 480 481 vm_deallocate (mytask, (vm_address_t) task, sizeof (task_t)); 482 free ((void *) process_name); 483 484 return 0; 485 } 486