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
get_pid_for_process_name(const char * procname)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 *
get_process_name_for_pid(pid_t pid)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 *
get_kinfo_proc_for_pid(pid_t pid,const char * process_name)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
get_thread_basic_info(thread_t thread)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
get_thread_identifier_info(thread_t thread)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
inferior_namespace_mach_port_num(task_t task,thread_t examine_threads_port,thread_t * inferior_port)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
get_current_pc(thread_t thread,int * wordsize)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
get_proc_threadinfo(pid_t pid,uint64_t thread_handle,struct proc_threadinfo * pth)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
main(int argc,char ** argv)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