Home | History | Annotate | Download | only in darwin-threads
      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