forked from OSchip/llvm-project
Replace examine-threads.c with a similar little utility I wrote a
while back. By default its output will be less verbose than the old examine-threads.c but adding the '-v' command line flag will give all the information that examine-threads.c provided plus some. Of note, this implementation can take a process name -- and it will use the libproc API so it can match program names longer than 16 characters. llvm-svn: 133500
This commit is contained in:
parent
184f3b37e2
commit
98322e64c3
|
|
@ -1,104 +1,339 @@
|
|||
#include <mach/mach.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <mach/mach.h>
|
||||
#include <time.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <ctype.h>
|
||||
#include <libproc.h>
|
||||
|
||||
void
|
||||
dump_thread_basic_info (uint32_t index, uint32_t tid, struct thread_basic_info *i)
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
const char * thread_run_state = NULL;
|
||||
kern_return_t kr;
|
||||
task_t task;
|
||||
thread_t thread;
|
||||
pid_t pid = 0;
|
||||
char *procname = NULL;
|
||||
int arg_is_procname = 0;
|
||||
int do_loop = 0;
|
||||
int verbose = 0;
|
||||
mach_port_t mytask = mach_task_self ();
|
||||
|
||||
switch (i->run_state)
|
||||
if (argc != 2 && argc != 3 && argc != 4)
|
||||
{
|
||||
case TH_STATE_RUNNING: thread_run_state = "running"; break; // 1 thread is running normally
|
||||
case TH_STATE_STOPPED: thread_run_state = "stopped"; break; // 2 thread is stopped
|
||||
case TH_STATE_WAITING: thread_run_state = "waiting"; break; // 3 thread is waiting normally
|
||||
case TH_STATE_UNINTERRUPTIBLE: thread_run_state = "uninter"; break; // 4 thread is in an uninterruptible wait
|
||||
case TH_STATE_HALTED: thread_run_state = "halted "; break; // 5 thread is halted at a
|
||||
default: thread_run_state = "???"; break;
|
||||
printf ("Usage: tdump [-l] [-v] pid/procname\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
// printf("[%3u] tid: 0x%4.4x, pc: 0x%16.16llx, sp: 0x%16.16llx, user: %d.%06.6d, system: %d.%06.6d, cpu: %2d, policy: %2d, run_state: %2d (%s), flags: %2d, suspend_count: %2d (current %2d), sleep_time: %d",
|
||||
printf("[%3u] tid: 0x%4.4x user: %d.%06d, system: %d.%06d, cpu: %2d, policy: %2d, run_state: %2d (%s), flags: %2d, suspend_count: %2d, sleep_time: %d\n",
|
||||
index,
|
||||
tid,
|
||||
i->user_time.seconds, i->user_time.microseconds,
|
||||
i->system_time.seconds, i->system_time.microseconds,
|
||||
i->cpu_usage,
|
||||
i->policy,
|
||||
i->run_state,
|
||||
thread_run_state,
|
||||
i->flags,
|
||||
i->suspend_count,
|
||||
i->sleep_time);
|
||||
//DumpRegisterState(0);
|
||||
}
|
||||
|
||||
|
||||
int main (int argc, char ** argv)
|
||||
{
|
||||
kern_return_t kret;
|
||||
task_t itask;
|
||||
thread_array_t thread_list;
|
||||
unsigned int nthreads;
|
||||
pid_t pid;
|
||||
|
||||
if (argc < 2)
|
||||
if (argc == 3 || argc == 4)
|
||||
{
|
||||
printf ("Usage: %s <PID>.\n", argv[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pid = atoi (argv[1]);
|
||||
printf ("Examining process: %d.\n", pid);
|
||||
|
||||
kret = task_for_pid (mach_task_self (), pid, &itask);
|
||||
if (kret != KERN_SUCCESS)
|
||||
{
|
||||
printf ("Could not get task for pid %d.\n", pid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct task_basic_info info_for_task;
|
||||
unsigned int task_info_count = TASK_BASIC_INFO_COUNT;
|
||||
|
||||
kret =
|
||||
task_info (itask, TASK_BASIC_INFO, (task_info_t) &info_for_task, &task_info_count);
|
||||
if (kret != KERN_SUCCESS)
|
||||
{
|
||||
printf ("Could not get task info for task: 0x%4.4x.\n", itask);
|
||||
}
|
||||
|
||||
printf ("Task suspend: %d.\n", info_for_task.suspend_count);
|
||||
|
||||
kret = task_threads (itask, &thread_list, &nthreads);
|
||||
if (kret != KERN_SUCCESS)
|
||||
{
|
||||
printf ("Could not get task threads for task 0x%4.4x.\n", itask);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int i;
|
||||
for (i = 0; i < nthreads; i++)
|
||||
{
|
||||
struct thread_basic_info info;
|
||||
unsigned int thread_info_count = THREAD_BASIC_INFO_COUNT;
|
||||
kern_return_t kret;
|
||||
|
||||
kret = thread_info (thread_list[i], THREAD_BASIC_INFO,
|
||||
(thread_info_t) & info, &thread_info_count);
|
||||
|
||||
if (kret != KERN_SUCCESS)
|
||||
int i = 1;
|
||||
while (i < argc - 1)
|
||||
{
|
||||
printf ("Error getting thread basic info for thread 0x%4.4x.\n", thread_list[i]);
|
||||
if (strcmp (argv[i], "-l") == 0)
|
||||
do_loop = 1;
|
||||
if (strcmp (argv[i], "-v") == 0)
|
||||
verbose = 1;
|
||||
i++;
|
||||
}
|
||||
else
|
||||
{
|
||||
dump_thread_basic_info (i + 1, thread_list[i], &info);
|
||||
}
|
||||
if (argc > 2) printf("thread_resume (tid = 0x%4.4x) => %i\n", thread_list[i], thread_resume (thread_list[i]));
|
||||
}
|
||||
|
||||
if (argc > 2) printf("task_resume (task = 0x%4.4x) => %i\n", itask, task_resume (itask));
|
||||
return 1;
|
||||
char *c = argv[argc - 1];
|
||||
if (*c == '\0')
|
||||
{
|
||||
printf ("Usage: tdump [-l] [-v] pid/procname\n");
|
||||
exit (1);
|
||||
}
|
||||
while (*c != '\0')
|
||||
{
|
||||
if (!isdigit (*c))
|
||||
{
|
||||
arg_is_procname = 1;
|
||||
procname = argv[argc - 1];
|
||||
break;
|
||||
}
|
||||
c++;
|
||||
}
|
||||
|
||||
// the argument is a pid
|
||||
if (arg_is_procname == 0)
|
||||
{
|
||||
pid = atoi (argv[argc - 1]);
|
||||
if (pid == 0)
|
||||
{
|
||||
printf ("Usage: tdump [-l] [-v] pid/procname\n");
|
||||
exit (1);
|
||||
}
|
||||
}
|
||||
|
||||
// Look up the pid for the provided process name
|
||||
if (arg_is_procname)
|
||||
{
|
||||
int process_count = proc_listpids (PROC_ALL_PIDS, 0, NULL, 0) / sizeof (pid_t);
|
||||
if (process_count < 1)
|
||||
{
|
||||
printf ("Only found %d processes running!\n", process_count);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
// Allocate a few extra slots in case new processes are spawned
|
||||
int all_pids_size = sizeof (pid_t) * (process_count + 3);
|
||||
pid_t *all_pids = (pid_t *) malloc (all_pids_size);
|
||||
|
||||
// re-set process_count in case the number of processes changed (got smaller; we won't do bigger)
|
||||
process_count = proc_listpids (PROC_ALL_PIDS, 0, all_pids, all_pids_size) / sizeof (pid_t);
|
||||
|
||||
int i;
|
||||
pid_t highest_pid = 0;
|
||||
int match_count = 0;
|
||||
for (i = 1; i < process_count; i++)
|
||||
{
|
||||
char pidpath[PATH_MAX];
|
||||
int pidpath_len = proc_pidpath (all_pids[i], pidpath, sizeof (pidpath));
|
||||
if (pidpath_len == 0)
|
||||
continue;
|
||||
char *j = strrchr (pidpath, '/');
|
||||
if ((j == NULL && strcmp (procname, pidpath) == 0)
|
||||
|| (j != NULL && strcmp (j + 1, procname) == 0))
|
||||
{
|
||||
match_count++;
|
||||
if (all_pids[i] > highest_pid)
|
||||
highest_pid = all_pids[i];
|
||||
}
|
||||
}
|
||||
free (all_pids);
|
||||
|
||||
if (match_count == 0)
|
||||
{
|
||||
printf ("Did not find process '%s'.\n", procname);
|
||||
exit (1);
|
||||
}
|
||||
if (match_count > 1)
|
||||
{
|
||||
printf ("Warning: More than one process '%s'!\n", procname);
|
||||
printf (" defaulting to the highest-pid one, %d\n", highest_pid);
|
||||
}
|
||||
pid = highest_pid;
|
||||
}
|
||||
|
||||
char process_name[PATH_MAX];
|
||||
char tmp_name[PATH_MAX];
|
||||
if (proc_pidpath (pid, tmp_name, sizeof (tmp_name)) == 0)
|
||||
{
|
||||
printf ("Could not find process with pid of %d\n", (int) pid);
|
||||
exit (1);
|
||||
}
|
||||
if (strrchr (tmp_name, '/'))
|
||||
strcpy (process_name, strrchr (tmp_name, '/') + 1);
|
||||
else
|
||||
strcpy (process_name, tmp_name);
|
||||
|
||||
|
||||
// At this point "pid" is the process id and "process_name" is the process name
|
||||
// Now we have to get the process list from the kernel (which only has the truncated
|
||||
// 16 char names)
|
||||
|
||||
struct kinfo_proc *all_kinfos;
|
||||
int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0 };
|
||||
size_t len;
|
||||
if (sysctl (mib, 3, NULL, &len, NULL, 0) != 0)
|
||||
{
|
||||
printf ("Could not number of processes\n");
|
||||
exit (1);
|
||||
}
|
||||
all_kinfos = (struct kinfo_proc *) malloc (len);
|
||||
if (sysctl (mib, 3, all_kinfos, &len, NULL, 0) != 0)
|
||||
{
|
||||
printf ("Could not get process infos\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
struct kinfo_proc *kinfo = NULL;
|
||||
int proc_count, i;
|
||||
proc_count = len / sizeof (struct kinfo_proc);
|
||||
for (i = 0 ; i < proc_count; i++)
|
||||
if (all_kinfos[i].kp_proc.p_pid == pid)
|
||||
{
|
||||
kinfo = &all_kinfos[i];
|
||||
break;
|
||||
}
|
||||
if (kinfo == NULL)
|
||||
{
|
||||
printf ("Did not find process '%s' when re-getting proc table.\n", process_name);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
printf ("pid %d (%s) is currently ", pid, process_name);
|
||||
switch (kinfo->kp_proc.p_stat) {
|
||||
case SIDL: printf ("being created by fork"); break;
|
||||
case SRUN: printf ("runnable"); break;
|
||||
case SSLEEP: printf ("sleeping on an address"); break;
|
||||
case SSTOP: printf ("suspended"); break;
|
||||
case SZOMB: printf ("zombie state - awaiting collection by parent"); break;
|
||||
default: printf ("unknown");
|
||||
}
|
||||
if (kinfo->kp_proc.p_flag & P_TRACED)
|
||||
printf (" and is being debugged.");
|
||||
|
||||
printf ("\n");
|
||||
|
||||
kr = task_for_pid (mach_task_self (), pid, &task);
|
||||
if (kr != KERN_SUCCESS)
|
||||
{
|
||||
printf ("Error - unable to task_for_pid()\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
struct timespec *rqtp = (struct timespec *) malloc (sizeof (struct timespec));
|
||||
rqtp->tv_sec = 0;
|
||||
rqtp->tv_nsec = 150000000;
|
||||
|
||||
int loop_cnt = 1;
|
||||
do
|
||||
{
|
||||
int i;
|
||||
if (do_loop)
|
||||
printf ("Iteration %d:\n", loop_cnt++);
|
||||
thread_array_t thread_list;
|
||||
mach_msg_type_number_t thread_count;
|
||||
|
||||
kr = task_threads (task, &thread_list, &thread_count);
|
||||
if (kr != KERN_SUCCESS)
|
||||
{
|
||||
printf ("Error - unable to get thread list\n");
|
||||
exit (1);
|
||||
}
|
||||
printf ("pid %d has %d threads\n", pid, thread_count);
|
||||
|
||||
for (i = 0; i < thread_count; i++)
|
||||
{
|
||||
thread_info_data_t thinfo;
|
||||
mach_msg_type_number_t thread_info_count = THREAD_INFO_MAX;
|
||||
kr = thread_info (thread_list[i], THREAD_BASIC_INFO,
|
||||
(thread_info_t) thinfo, &thread_info_count);
|
||||
if (kr != KERN_SUCCESS)
|
||||
{
|
||||
printf ("Error - unable to get basic thread info for a thread\n");
|
||||
exit (1);
|
||||
}
|
||||
thread_basic_info_t basic_info_th = (thread_basic_info_t) thinfo;
|
||||
|
||||
thread_identifier_info_data_t tident;
|
||||
mach_msg_type_number_t tident_count = THREAD_IDENTIFIER_INFO_COUNT;
|
||||
kr = thread_info (thread_list[i], THREAD_IDENTIFIER_INFO,
|
||||
(thread_info_t) &tident, &tident_count);
|
||||
if (kr != KERN_SUCCESS)
|
||||
{
|
||||
printf ("Error - unable to get thread ident for a thread\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
uint64_t pc;
|
||||
int width;
|
||||
#if defined (__x86_64__) || defined (__i386__)
|
||||
x86_thread_state_t gp_regs;
|
||||
mach_msg_type_number_t gp_count = x86_THREAD_STATE_COUNT;
|
||||
kr = thread_get_state (thread_list[i], x86_THREAD_STATE,
|
||||
(thread_state_t) &gp_regs, &gp_count);
|
||||
if (kr != KERN_SUCCESS)
|
||||
{
|
||||
printf ("Error - unable to get registers for a thread\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
if (gp_regs.tsh.flavor == x86_THREAD_STATE64)
|
||||
{
|
||||
pc = gp_regs.uts.ts64.__rip;
|
||||
width = 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
pc = gp_regs.uts.ts32.__eip;
|
||||
width = 4;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined (__arm__)
|
||||
arm_thread_state_t gp_regs;
|
||||
mach_msg_type_number_t gp_count = ARM_THREAD_STATE_COUNT;
|
||||
kr = thread_get_state (thread_list[i], ARM_THREAD_STATE,
|
||||
(thread_state_t) &gp_regs, &gp_count);
|
||||
if (kr != KERN_SUCCESS)
|
||||
{
|
||||
printf ("Error - unable to get registers for a thread\n");
|
||||
exit (1);
|
||||
}
|
||||
pc = gp_regs.__pc;
|
||||
width = 4;
|
||||
#endif
|
||||
|
||||
printf ("thread #%d, unique tid %lld, suspend count is %d, ", i,
|
||||
tident.thread_id,
|
||||
basic_info_th->suspend_count);
|
||||
if (width == 8)
|
||||
printf ("pc 0x%016llx, ", pc);
|
||||
else
|
||||
printf ("pc 0x%08llx, ", pc);
|
||||
printf ("run state is ");
|
||||
switch (basic_info_th->run_state) {
|
||||
case TH_STATE_RUNNING: puts ("running"); break;
|
||||
case TH_STATE_STOPPED: puts ("stopped"); break;
|
||||
case TH_STATE_WAITING: puts ("waiting"); break;
|
||||
case TH_STATE_UNINTERRUPTIBLE: puts ("uninterruptible"); break;
|
||||
case TH_STATE_HALTED: puts ("halted"); break;
|
||||
default: puts ("");
|
||||
}
|
||||
if (verbose)
|
||||
{
|
||||
printf (" ");
|
||||
printf ("mach thread #0x%4.4x ", (int) thread_list[i]);
|
||||
printf ("pthread handle id 0x%llx ", (uint64_t) tident.thread_handle);
|
||||
|
||||
struct proc_threadinfo pth;
|
||||
pth.pth_name[0] = '\0';
|
||||
int ret = proc_pidinfo (pid, PROC_PIDTHREADINFO, tident.thread_handle,
|
||||
&pth, sizeof (pth));
|
||||
if (ret != 0 && pth.pth_name[0] != '\0')
|
||||
printf ("thread name '%s' ", pth.pth_name);
|
||||
|
||||
printf ("\n ");
|
||||
printf ("user %d.%06ds, system %d.%06ds",
|
||||
basic_info_th->user_time.seconds, basic_info_th->user_time.microseconds,
|
||||
basic_info_th->system_time.seconds, basic_info_th->system_time.microseconds);
|
||||
if (basic_info_th->cpu_usage > 0)
|
||||
{
|
||||
float cpu_percentage = basic_info_th->cpu_usage / 10.0;
|
||||
printf (", using %.1f%% cpu currently", cpu_percentage);
|
||||
}
|
||||
if (basic_info_th->sleep_time > 0)
|
||||
printf (", this thread has slept for %d seconds", basic_info_th->sleep_time);
|
||||
|
||||
printf ("\n ");
|
||||
printf ("scheduling policy %d", basic_info_th->policy);
|
||||
|
||||
if (basic_info_th->flags != 0)
|
||||
{
|
||||
printf (", flags %d", basic_info_th->flags);
|
||||
if ((basic_info_th->flags | TH_FLAGS_SWAPPED) == TH_FLAGS_SWAPPED)
|
||||
printf (" (thread is swapped out)");
|
||||
if ((basic_info_th->flags | TH_FLAGS_IDLE) == TH_FLAGS_IDLE)
|
||||
printf (" (thread is idle)");
|
||||
}
|
||||
if (ret != 0)
|
||||
printf (", current pri %d, max pri %d", pth.pth_curpri, pth.pth_maxpriority);
|
||||
|
||||
puts ("");
|
||||
}
|
||||
}
|
||||
if (do_loop)
|
||||
printf ("\n");
|
||||
vm_deallocate (mytask, (vm_address_t) thread_list,
|
||||
thread_count * sizeof (thread_act_t));
|
||||
nanosleep (rqtp, NULL);
|
||||
} while (do_loop);
|
||||
|
||||
vm_deallocate (mytask, (vm_address_t) task, sizeof (task_t));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue