2 * Copyright (c) 1999-2016 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
7 * Reserved. This file contains Original Code and/or Modifications of
8 * Original Code as defined in and that are subject to the Apple Public
9 * Source License Version 1.0 (the 'License'). You may not use this file
10 * except in compliance with the License. Please obtain a copy of the
11 * License at http://www.apple.com/publicsource and read it before using
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
19 * License for the specific language governing rights and limitations
22 * @APPLE_LICENSE_HEADER_END@
26 cc -I. -DPRIVATE -D__APPLE_PRIVATE -O -o sc_usage sc_usage.c -lncurses
29 #define Default_DELAY 1 /* default delay interval */
39 #include <sys/types.h>
40 #include <sys/param.h>
42 #include <sys/ptrace.h>
48 #include <sys/ioctl.h>
50 #ifndef KERNEL_PRIVATE
51 #define KERNEL_PRIVATE
52 #include <sys/kdebug.h>
55 #include <sys/kdebug.h>
56 #endif /*KERNEL_PRIVATE*/
58 #include <sys/sysctl.h>
60 #include <mach/mach_time.h>
64 /* Number of lines of header information on the standard screen */
65 #define HEADER_LINES 5
68 int Header_lines
= HEADER_LINES
;
71 int no_screen_refresh
= 0;
77 int waiting_index
= 0;
78 FILE *dfp
= 0; /*Debug output file */
81 #define SAMPLE_SIZE 20000
83 #define DBG_ZERO_FILL_FAULT 1
84 #define DBG_PAGEIN_FAULT 2
85 #define DBG_COW_FAULT 3
86 #define DBG_CACHE_HIT_FAULT 4
89 #define MAX_THREADS 16
97 char *state_name
[] = {
106 #define KERNEL_MODE 1
127 long pathname
[NUMPARMS
+ 1];
128 struct entry th_entry
[MAX_NESTED
];
136 unsigned int stime_secs
;
138 unsigned int wtime_secs
;
140 unsigned int delta_wtime_secs
;
141 double delta_wtime_usecs
;
144 struct th_info th_state
[MAX_THREADS
];
145 struct sc_entry faults
[MAX_FAULTS
];
147 struct sc_entry
*sc_tab
;
149 int msgcode_cnt
; /* number of MSG_ codes */
151 int num_of_threads
= 0;
152 int now_collect_cpu_time
= 0;
154 unsigned int utime_secs
;
158 unsigned int itime_secs
;
160 unsigned int delta_itime_secs
;
161 double delta_itime_usecs
;
165 unsigned int otime_secs
;
167 unsigned int delta_otime_secs
;
168 double delta_otime_usecs
;
176 int mach_stkhandoff
= 0;
178 int mach_vmfault
= 0;
185 #define DBG_FUNC_ALL (DBG_FUNC_START | DBG_FUNC_END)
186 #define DBG_FUNC_MASK 0xfffffffc
193 /* Default divisor */
194 #define DIVISOR 16.6666 /* Trace divisor converts to microseconds */
195 double divisor
= DIVISOR
;
201 kbufinfo_t bufinfo
= {0, 0, 0, 0};
203 int trace_enabled
= 0;
204 int set_remove_flag
= 1;
206 struct kinfo_proc
*kp_buffer
= 0;
207 size_t kp_nentries
= 0;
209 extern char **environ
;
211 static void set_enable(int);
212 static void set_pidcheck(int, int);
213 static void set_remove(void);
214 static void set_numbufs(int);
215 static void set_init(void);
217 int argtopid(char *);
218 int argtoi(int, char*, char*, int);
220 void get_bufinfo(kbufinfo_t
*);
221 static void reset_counters(void);
222 static void getdivisor(void);
223 static void screen_update(void);
224 static void sc_tab_init(char *);
225 static void sort_scalls(void);
226 static void sample_sc(void);
227 static int find_msgcode(int);
233 /* exit under normal conditions -- INT handler */
235 leave(__unused
int unused
)
237 if (no_screen_refresh
== 0) {
243 set_pidcheck(pid
, 0);
249 sigwinch(__unused
int unused
)
251 if (no_screen_refresh
== 0)
256 exit_usage(char *myname
)
258 fprintf(stderr
, "Usage: %s [-c codefile] [-e] [-l] [-sn] pid | cmd | -E execute path\n", myname
);
259 fprintf(stderr
, " -c name of codefile containing mappings for syscalls\n");
260 fprintf(stderr
, " Default is /usr/share/misc/trace.codes\n");
261 fprintf(stderr
, " -e enable sort by call count\n");
262 fprintf(stderr
, " -l turn off top style output\n");
263 fprintf(stderr
, " -sn change sample rate to every n seconds\n");
264 fprintf(stderr
, " pid selects process to sample\n");
265 fprintf(stderr
, " cmd selects command to sample\n");
266 fprintf(stderr
, " -E Execute the given path and optional arguments\n");
271 #define usec_to_1000ths(t) ((t) / 1000)
274 print_time(char *p
, unsigned int useconds
, unsigned int seconds
)
278 minutes
= seconds
/ 60;
279 hours
= minutes
/ 60;
281 if (minutes
< 100) { // up to 100 minutes
282 sprintf(p
, "%02ld:%02ld.%03ld", minutes
, (unsigned long)(seconds
% 60),
283 (unsigned long)usec_to_1000ths(useconds
));
285 else if (hours
< 100) { // up to 100 hours
286 sprintf(p
, "%02ld:%02ld:%02ld ", hours
, (minutes
% 60),
287 (unsigned long)(seconds
% 60));
290 sprintf(p
, "%4ld hrs ", hours
);
295 main(int argc
, char *argv
[])
297 char *myname
= "sc_usage";
298 char *codefile
= "/usr/share/misc/trace.codes";
301 int delay
= Default_DELAY
;
303 if ( geteuid() != 0 ) {
304 printf("'sc_usage' must be run as root...\n");
308 if (0 != reexec_to_match_kernel()) {
309 fprintf(stderr
, "Could not re-execute: %d\n", errno
);
315 if ((myname
= rindex(argv
[0], '/')) == 0) {
323 while ((ch
= getopt(argc
, argv
, "c:els:d:E")) != EOF
) {
326 delay
= argtoi('s', "decimal number", optarg
, 10);
332 no_screen_refresh
= 1;
341 /* exit_usage(myname);*/
342 exit_usage("default");
348 sc_tab_init(codefile
);
354 /* parse a pid or a command */
355 if((pid
= argtopid(argv
[optind
])) < 0 )
359 { /* execute this command */
364 ptr
= strrchr(argv
[optind
], '/');
370 strncpy(proc_name
, ptr
, sizeof(proc_name
)-1);
371 proc_name
[sizeof(proc_name
)-1] = '\0';
381 fprintf(stderr
, "Starting program: %s\n", argv
[optind
]);
384 switch ((pid
= vfork()))
391 ptrace(0,(pid_t
)0,(caddr_t
)0,0); /* PT_TRACE_ME */
392 execve(argv
[optind
], &argv
[optind
], environ
);
407 if (no_screen_refresh
== 0) {
409 /* initializes curses and screen (last) */
410 if (initscr() == (WINDOW
*) 0)
412 printf("Unrecognized TERM type, try vt100\n");
424 /* set up signal handlers */
425 signal(SIGINT
, leave
);
426 signal(SIGQUIT
, leave
);
427 signal(SIGHUP
, leave
);
428 signal(SIGTERM
, leave
);
429 signal(SIGWINCH
, sigwinch
);
431 if (no_screen_refresh
== 0)
432 topn
= LINES
- Header_lines
;
439 set_numbufs(SAMPLE_SIZE
);
441 set_pidcheck(pid
, 1);
444 ptrace(7, pid
, (caddr_t
)1, 0); /* PT_CONTINUE */
449 if ((sort_now
= 10 / delay
) < 2)
452 get_bufinfo(&bufinfo
);
454 my_buffer
= malloc(bufinfo
.nkdbufs
* sizeof(kd_buf
));
455 if(my_buffer
== (char *) 0)
456 quit("can't allocate memory for tracing info\n");
459 (void)screen_update();
467 for (i
= 0; i
< (10 * delay
) && newLINES
== 0; i
++) {
469 if (no_screen_refresh
== 0) {
470 if ((c
= getch()) != ERR
&& (char)c
== 'q')
482 No need to check for initscr error return.
483 We won't get here if it fails on the first call.
489 topn
= LINES
- Header_lines
;
492 (void)screen_update();
497 print_row(struct sc_entry
*se
, int no_wtime
)
503 sprintf(tbuf
, "%-23.23s %8d(%d)", se
->name
, se
->total_count
, se
->delta_count
);
505 sprintf(tbuf
, "%-23.23s %8d", se
->name
, se
->total_count
);
508 memset(&tbuf
[clen
], ' ', 45 - clen
);
510 print_time(&tbuf
[45], (unsigned int)(se
->stime_usecs
), se
->stime_secs
);
513 if (no_wtime
== 0 && (se
->wtime_usecs
|| se
->wtime_secs
)) {
514 sprintf(&tbuf
[clen
], " ");
515 clen
+= strlen(&tbuf
[clen
]);
517 print_time(&tbuf
[clen
], (unsigned int)(se
->wtime_usecs
), se
->wtime_secs
);
518 clen
+= strlen(&tbuf
[clen
]);
520 if (se
->waiting
|| se
->delta_wtime_usecs
|| se
->delta_wtime_secs
) {
522 sprintf(&tbuf
[clen
], "(");
523 clen
+= strlen(&tbuf
[clen
]);
525 print_time(&tbuf
[clen
], (unsigned int)(se
->delta_wtime_usecs
),
526 se
->delta_wtime_secs
);
527 clen
+= strlen(&tbuf
[clen
]);
529 sprintf(&tbuf
[clen
], ")");
530 clen
+= strlen(&tbuf
[clen
]);
533 if (se
->waiting
== 1)
534 sprintf(&tbuf
[clen
], " W");
536 sprintf(&tbuf
[clen
], " %d", se
->waiting
);
537 clen
+= strlen(&tbuf
[clen
]);
541 sprintf(&tbuf
[clen
], "\n");
542 if (no_screen_refresh
)
565 if (no_screen_refresh
== 0) {
566 /* clear for new display */
572 sprintf(tbuf
, "%-14.14s", proc_name
);
580 p2
= "context switch ";
582 p2
= "context switches";
583 if (num_of_threads
== 1)
588 sprintf(&tbuf
[clen
], " %4d %s %4d %s %4d %s",
589 preempted
, p1
, csw
, p2
, num_of_threads
, p3
);
590 clen
+= strlen(&tbuf
[clen
]);
593 * Display the current time.
594 * "ctime" always returns a string that looks like this:
596 * Sun Sep 16 01:03:52 1973
597 * 012345678901234567890123
600 * We want indices 11 thru 18 (length 8).
602 curr_time
= time((long *)0);
605 start_time
= curr_time
;
607 elapsed_secs
= curr_time
- start_time
;
608 minutes
= elapsed_secs
/ 60;
609 hours
= minutes
/ 60;
611 memset(&tbuf
[clen
], ' ', 78 - clen
);
615 sprintf(&tbuf
[clen
], "%-8.8s\n", &(ctime(&curr_time
)[11]));
616 if (no_screen_refresh
)
621 if (total_faults
== 1)
629 sprintf(tbuf
, " %4d %s %4d %s",
630 total_faults
, p1
, scalls
, p2
);
633 sprintf(&tbuf
[clen
], " %3ld:%02ld:%02ld\n",
634 hours
, minutes
% 60, elapsed_secs
% 60);
635 if (no_screen_refresh
)
642 sprintf(tbuf
, "\nTYPE NUMBER CPU_TIME WAIT_TIME\n");
643 if (no_screen_refresh
)
648 sprintf(tbuf
, "------------------------------------------------------------------------------\n");
649 if (no_screen_refresh
)
657 sprintf(tbuf
, "System Idle ");
660 print_time(&tbuf
[clen
], itime_usecs
, itime_secs
);
661 clen
+= strlen(&tbuf
[clen
]);
663 if (delta_itime_usecs
|| delta_itime_secs
) {
665 sprintf(&tbuf
[clen
], "(");
666 clen
+= strlen(&tbuf
[clen
]);
668 print_time(&tbuf
[clen
], delta_itime_usecs
, delta_itime_secs
);
669 clen
+= strlen(&tbuf
[clen
]);
671 sprintf(&tbuf
[clen
], ")");
672 clen
+= strlen(&tbuf
[clen
]);
674 sprintf(&tbuf
[clen
], "\n");
675 if (no_screen_refresh
)
683 sprintf(tbuf
, "System Busy ");
686 print_time(&tbuf
[clen
], otime_usecs
, otime_secs
);
687 clen
+= strlen(&tbuf
[clen
]);
689 if (delta_otime_usecs
|| delta_otime_secs
) {
691 sprintf(&tbuf
[clen
], "(");
692 clen
+= strlen(&tbuf
[clen
]);
694 print_time(&tbuf
[clen
], delta_otime_usecs
, delta_otime_secs
);
695 clen
+= strlen(&tbuf
[clen
]);
697 sprintf(&tbuf
[clen
], ")");
698 clen
+= strlen(&tbuf
[clen
]);
700 sprintf(&tbuf
[clen
], "\n");
701 if (no_screen_refresh
)
708 sprintf(tbuf
, "%-14.14s Usermode ", proc_name
);
711 print_time(&tbuf
[clen
], utime_usecs
, utime_secs
);
712 clen
+= strlen(&tbuf
[clen
]);
714 sprintf(&tbuf
[clen
], "\n");
715 if (no_screen_refresh
)
722 max_rows
= topn
- (num_of_threads
+ 3);
726 for (output_lf
= 1, n
= 1; rows
< max_rows
&& n
< MAX_FAULTS
; n
++) {
729 if (se
->total_count
== 0)
731 if (output_lf
== 1) {
733 if (no_screen_refresh
)
739 if (rows
>= max_rows
)
748 if (no_screen_refresh
)
754 for (i
= 0; rows
< max_rows
; i
++) {
756 n
= sort_by_count
[i
];
758 n
= sort_by_wtime
[i
];
761 print_row(&sc_tab
[n
], 0);
767 if (no_screen_refresh
== 0) {
768 while (rows
++ < max_rows
)
773 if (num_of_threads
) {
774 sprintf(tbuf
, "\nCURRENT_TYPE LAST_PATHNAME_WAITED_FOR CUR_WAIT_TIME THRD# PRI\n");
776 if (no_screen_refresh
)
781 sprintf(tbuf
, "------------------------------------------------------------------------------\n");
782 if (no_screen_refresh
)
789 for (i
= 0; i
< num_of_threads
; i
++, ti
++) {
793 int secs
, time_secs
, time_usecs
;
795 now
= mach_absolute_time();
797 while (ti
->thread
== 0 && ti
< &th_state
[MAX_THREADS
])
799 if (ti
== &th_state
[MAX_THREADS
])
803 te
= &ti
->th_entry
[ti
->depth
- 1];
805 if (te
->sc_state
== WAITING
) {
807 sprintf(tbuf
, "%-23.23s", sc_tab
[te
->code
].name
);
809 sprintf(tbuf
, "%-23.23s", "vm_fault");
811 sprintf(tbuf
, "%-23.23s", state_name
[te
->sc_state
]);
813 te
= &ti
->th_entry
[0];
814 sprintf(tbuf
, "%-23.23s", state_name
[te
->sc_state
]);
818 /* print the tail end of the pathname */
819 p
= (char *)ti
->pathname
;
826 sprintf(&tbuf
[clen
], " %-26.26s ", &p
[plen
]);
828 clen
+= strlen(&tbuf
[clen
]);
830 time_usecs
= (((double)now
- te
->otime
) / divisor
);
831 secs
= time_usecs
/ 1000000;
832 time_usecs
-= secs
* 1000000;
835 print_time(&tbuf
[clen
], time_usecs
, time_secs
);
836 clen
+= strlen(&tbuf
[clen
]);
837 sprintf(&tbuf
[clen
], " %2d %3d\n", i
, ti
->curpri
);
838 if (no_screen_refresh
)
843 if (no_screen_refresh
== 0) {
847 printf("\n=================\n");
851 for (i
= 0; i
< (MAX_SC
+ msgcode_cnt
); i
++) {
852 if ((n
= sort_by_count
[i
]) == -1)
854 sc_tab
[n
].delta_count
= 0;
855 sc_tab
[n
].waiting
= 0;
856 sc_tab
[n
].delta_wtime_usecs
= 0;
857 sc_tab
[n
].delta_wtime_secs
= 0;
859 for (i
= 1; i
< MAX_FAULTS
; i
++) {
860 faults
[i
].delta_count
= 0;
861 faults
[i
].waiting
= 0;
862 faults
[i
].delta_wtime_usecs
= 0;
863 faults
[i
].delta_wtime_secs
= 0;
869 delta_itime_secs
= 0;
870 delta_itime_usecs
= 0;
871 delta_otime_secs
= 0;
872 delta_otime_usecs
= 0;
880 for (i
= 0; i
< (MAX_SC
+ msgcode_cnt
) ; i
++) {
881 sc_tab
[i
].delta_count
= 0;
882 sc_tab
[i
].total_count
= 0;
883 sc_tab
[i
].waiting
= 0;
884 sc_tab
[i
].delta_wtime_usecs
= 0;
885 sc_tab
[i
].delta_wtime_secs
= 0;
886 sc_tab
[i
].wtime_usecs
= 0;
887 sc_tab
[i
].wtime_secs
= 0;
888 sc_tab
[i
].stime_usecs
= 0;
889 sc_tab
[i
].stime_secs
= 0;
891 for (i
= 1; i
< MAX_FAULTS
; i
++) {
892 faults
[i
].delta_count
= 0;
893 faults
[i
].total_count
= 0;
894 faults
[i
].waiting
= 0;
895 faults
[i
].delta_wtime_usecs
= 0;
896 faults
[i
].delta_wtime_secs
= 0;
897 faults
[i
].wtime_usecs
= 0;
898 faults
[i
].wtime_secs
= 0;
899 faults
[i
].stime_usecs
= 0;
900 faults
[i
].stime_secs
= 0;
912 delta_itime_secs
= 0;
913 delta_itime_usecs
= 0;
916 delta_otime_secs
= 0;
917 delta_otime_usecs
= 0;
921 sc_tab_init(char *codefile
)
929 if ((fp
= fopen(codefile
,"r")) == (FILE *)0) {
930 printf("Failed to open code description file %s\n", codefile
);
934 /* Count Mach message MSG_ codes */
935 for (msgcode_cnt
=0;;) {
936 n
= fscanf(fp
, "%x%55s\n", &code
, &name
[0]);
939 if (strncmp ("MSG_", &name
[0], 4) == 0)
941 if (strcmp("TRACE_LAST_WRAPPER", &name
[0]) == 0)
945 sc_tab
= (struct sc_entry
*)malloc((MAX_SC
+msgcode_cnt
) * sizeof (struct sc_entry
));
947 quit("can't allocate memory for system call table\n");
948 bzero(sc_tab
,(MAX_SC
+msgcode_cnt
) * sizeof (struct sc_entry
));
950 msgcode_tab
= (int *)malloc(msgcode_cnt
* sizeof(int));
952 quit("can't allocate memory for msgcode table\n");
953 bzero(msgcode_tab
,(msgcode_cnt
* sizeof(int)));
955 sort_by_count
= (int *)malloc((MAX_SC
+ msgcode_cnt
+ 1) * sizeof(int));
957 quit("can't allocate memory for sort_by_count table\n");
958 bzero(sort_by_count
,(MAX_SC
+ msgcode_cnt
+ 1) * sizeof(int));
960 sort_by_wtime
= (int *)malloc((MAX_SC
+ msgcode_cnt
+ 1) * sizeof(int));
962 quit("can't allocate memory for sort_by_wtime table\n");
963 bzero(sort_by_wtime
, (MAX_SC
+ msgcode_cnt
+ 1) * sizeof(int));
969 n
= fscanf(fp
, "%x%55s\n", &code
, &name
[0]);
974 if (strcmp("MACH_vmfault", &name
[0]) == 0) {
978 if (strcmp("MACH_SCHED", &name
[0]) == 0) {
982 if (strcmp("MACH_STKHANDOFF", &name
[0]) == 0) {
983 mach_stkhandoff
= code
;
986 if (strcmp("MACH_IDLE", &name
[0]) == 0) {
990 if (strcmp("VFS_LOOKUP", &name
[0]) == 0) {
994 if (strcmp("BSC_SysCall", &name
[0]) == 0) {
998 if (strcmp("MACH_SysCall", &name
[0]) == 0) {
1002 if (strcmp("BSC_exit", &name
[0]) == 0) {
1006 if (strncmp("MSG_", &name
[0], 4) == 0) {
1007 msgcode_tab
[msgcode_indx
] = ((code
& 0x00ffffff) >>2);
1008 n
= MAX_SC
+ msgcode_indx
;
1009 strncpy(&sc_tab
[n
].name
[0], &name
[4], 31 );
1013 if (strncmp("MSC_", &name
[0], 4) == 0) {
1014 n
= 512 + ((code
>>2) & 0x1ff);
1015 strcpy(&sc_tab
[n
].name
[0], &name
[4]);
1018 if (strncmp("BSC_", &name
[0], 4) == 0) {
1019 n
= (code
>>2) & 0x1ff;
1020 strcpy(&sc_tab
[n
].name
[0], &name
[4]);
1023 if (strcmp("TRACE_LAST_WRAPPER", &name
[0]) == 0)
1026 strcpy(&faults
[1].name
[0], "zero_fill");
1027 strcpy(&faults
[2].name
[0], "pagein");
1028 strcpy(&faults
[3].name
[0], "copy_on_write");
1029 strcpy(&faults
[4].name
[0], "cache_hit");
1033 find_proc_names(void)
1036 struct kinfo_proc
*kp
;
1040 mib
[2] = KERN_PROC_ALL
;
1043 if (sysctl(mib
, 4, NULL
, &bufSize
, NULL
, 0) < 0)
1044 quit("trace facility failure, KERN_PROC_ALL\n");
1046 if((kp
= (struct kinfo_proc
*)malloc(bufSize
)) == (struct kinfo_proc
*)0)
1047 quit("can't allocate memory for proc buffer\n");
1049 if (sysctl(mib
, 4, kp
, &bufSize
, NULL
, 0) < 0)
1050 quit("trace facility failure, KERN_PROC_ALL\n");
1052 kp_nentries
= bufSize
/ sizeof(struct kinfo_proc
);
1056 static struct th_info
*
1057 find_thread(uintptr_t thread
)
1061 for (ti
= th_state
; ti
< &th_state
[MAX_THREADS
]; ti
++) {
1062 if (ti
->thread
== thread
)
1065 return ((struct th_info
*)0);
1069 cmp_wtime(struct sc_entry
*s1
, struct sc_entry
*s2
)
1071 if (s1
->wtime_secs
< s2
->wtime_secs
)
1073 if (s1
->wtime_secs
> s2
->wtime_secs
)
1075 if (s1
->wtime_usecs
<= s2
->wtime_usecs
)
1083 int i
, n
, k
, cnt
, secs
;
1085 struct sc_entry
*se
;
1089 now
= mach_absolute_time();
1091 for (ti
= th_state
; ti
< &th_state
[MAX_THREADS
]; ti
++) {
1092 if (ti
->thread
== 0)
1096 te
= &ti
->th_entry
[ti
->depth
-1];
1098 if (te
->sc_state
== WAITING
) {
1100 se
= &sc_tab
[te
->code
];
1102 se
= &faults
[DBG_PAGEIN_FAULT
];
1104 se
->wtime_usecs
+= ((double)now
- te
->stime
) / divisor
;
1105 se
->delta_wtime_usecs
+= ((double)now
- te
->stime
) / divisor
;
1106 te
->stime
= (double)now
;
1108 secs
= se
->wtime_usecs
/ 1000000;
1109 se
->wtime_usecs
-= secs
* 1000000;
1110 se
->wtime_secs
+= secs
;
1112 secs
= se
->delta_wtime_usecs
/ 1000000;
1113 se
->delta_wtime_usecs
-= secs
* 1000000;
1114 se
->delta_wtime_secs
+= secs
;
1117 te
= &ti
->th_entry
[0];
1119 if (te
->sc_state
== PREEMPTED
) {
1120 if ((unsigned long)(((double)now
- te
->otime
) / divisor
) > 5000000) {
1123 ti
->pathptr
= (long *)NULL
;
1124 ti
->pathname
[0] = 0;
1130 if ((called
% sort_now
) == 0) {
1131 sort_by_count
[0] = -1;
1132 sort_by_wtime
[0] = -1;
1133 for (cnt
= 1, n
= 1; n
< (MAX_SC
+ msgcode_cnt
); n
++) {
1134 if (sc_tab
[n
].total_count
) {
1135 for (i
= 0; i
< cnt
; i
++) {
1136 if ((k
= sort_by_count
[i
]) == -1 ||
1137 sc_tab
[n
].total_count
> sc_tab
[k
].total_count
) {
1139 for (k
= cnt
- 1; k
>= i
; k
--)
1140 sort_by_count
[k
+1] = sort_by_count
[k
];
1141 sort_by_count
[i
] = n
;
1145 if (how_to_sort
== 0) {
1146 for (i
= 0; i
< cnt
; i
++) {
1147 if ((k
= sort_by_wtime
[i
]) == -1 ||
1148 cmp_wtime(&sc_tab
[n
], &sc_tab
[k
])) {
1150 for (k
= cnt
- 1; k
>= i
; k
--)
1151 sort_by_wtime
[k
+1] = sort_by_wtime
[k
];
1152 sort_by_wtime
[i
] = n
;
1168 mib
[1] = KERN_KDEBUG
;
1169 mib
[2] = KERN_KDENABLE
; /* protocol */
1172 mib
[5] = 0; /* no flags */
1173 if (sysctl(mib
, 4, NULL
, &needed
, NULL
, 0) < 0)
1174 quit("trace facility failure, KERN_KDENABLE\n");
1183 set_numbufs(int nbufs
)
1186 mib
[1] = KERN_KDEBUG
;
1187 mib
[2] = KERN_KDSETBUF
;
1190 mib
[5] = 0; /* no flags */
1191 if (sysctl(mib
, 4, NULL
, &needed
, NULL
, 0) < 0)
1192 quit("trace facility failure, KERN_KDSETBUF\n");
1195 mib
[1] = KERN_KDEBUG
;
1196 mib
[2] = KERN_KDSETUP
;
1199 mib
[5] = 0; /* no flags */
1200 if (sysctl(mib
, 3, NULL
, &needed
, NULL
, 0) < 0)
1201 quit("trace facility failure, KERN_KDSETUP\n");
1206 set_pidcheck(int pid
, int on_off
)
1210 kr
.type
= KDBG_TYPENONE
;
1213 needed
= sizeof(kd_regtype
);
1215 mib
[1] = KERN_KDEBUG
;
1216 mib
[2] = KERN_KDPIDTR
;
1220 if (sysctl(mib
, 3, &kr
, &needed
, NULL
, 0) < 0) {
1222 printf("pid %d does not exist\n", pid
);
1230 get_bufinfo(kbufinfo_t
*val
)
1232 needed
= sizeof (*val
);
1234 mib
[1] = KERN_KDEBUG
;
1235 mib
[2] = KERN_KDGETBUF
;
1238 mib
[5] = 0; /* no flags */
1239 if (sysctl(mib
, 3, val
, &needed
, 0, 0) < 0)
1240 quit("trace facility failure, KERN_KDGETBUF\n");
1252 mib
[1] = KERN_KDEBUG
;
1253 mib
[2] = KERN_KDREMOVE
; /* protocol */
1256 mib
[5] = 0; /* no flags */
1258 if (sysctl(mib
, 3, NULL
, &needed
, NULL
, 0) < 0)
1260 set_remove_flag
= 0;
1263 quit("The trace facility is currently in use...\n Note: fs_usage, sc_usage, and latency use this feature.\n\n");
1265 quit("trace facility failure, KERN_KDREMOVE\n");
1274 kr
.type
= KDBG_RANGETYPE
;
1277 needed
= sizeof(kd_regtype
);
1279 mib
[1] = KERN_KDEBUG
;
1280 mib
[2] = KERN_KDSETREG
;
1283 mib
[5] = 0; /* no flags */
1284 if (sysctl(mib
, 3, &kr
, &needed
, NULL
, 0) < 0)
1285 quit("trace facility failure, KERN_KDSETREG\n");
1288 mib
[1] = KERN_KDEBUG
;
1289 mib
[2] = KERN_KDSETUP
;
1292 mib
[5] = 0; /* no flags */
1293 if (sysctl(mib
, 3, NULL
, &needed
, NULL
, 0) < 0)
1294 quit("trace facility failure, KERN_KDSETUP\n");
1308 get_bufinfo(&bufinfo
);
1310 needed
= bufinfo
.nkdbufs
* sizeof(kd_buf
);
1312 mib
[1] = KERN_KDEBUG
;
1313 mib
[2] = KERN_KDREADTR
;
1318 if (sysctl(mib
, 3, my_buffer
, &needed
, NULL
, 0) < 0)
1319 quit("trace facility failure, KERN_KDREADTR\n");
1323 if (bufinfo
.flags
& KDBG_WRAPPED
) {
1324 for (i
= 0; i
< MAX_THREADS
; i
++) {
1325 th_state
[i
].depth
= 0;
1326 th_state
[i
].thread
= 0;
1327 th_state
[i
].vfslookup
= 0;
1328 th_state
[i
].pathptr
= (long *)NULL
;
1329 th_state
[i
].pathname
[0] = 0;
1337 set_pidcheck(pid
, 1);
1338 set_enable(1); /* re-enable kernel logging */
1340 kd
= (kd_buf
*)my_buffer
;
1342 for (i
= 0; i
< count
; i
++) {
1343 int debugid
, baseid
;
1347 struct th_info
*ti
, *switched_out
, *switched_in
;
1348 struct sc_entry
*se
;
1351 thread
= kd
[i
].arg5
;
1352 debugid
= kd
[i
].debugid
;
1353 type
= kd
[i
].debugid
& DBG_FUNC_MASK
;
1356 switched_out
= (struct th_info
*)0;
1357 switched_in
= (struct th_info
*)0;
1359 now
= kd
[i
].timestamp
& KDBG_TIMESTAMP_MASK
;
1361 baseid
= debugid
& 0xffff0000;
1363 if (type
== vfs_lookup
) {
1366 if ((ti
= find_thread(thread
)) == (struct th_info
*)0)
1369 if (ti
->vfslookup
== 1) {
1371 sargptr
= ti
->pathname
;
1373 *sargptr
++ = kd
[i
].arg2
;
1374 *sargptr
++ = kd
[i
].arg3
;
1375 *sargptr
++ = kd
[i
].arg4
;
1377 * NULL terminate the 'string'
1381 ti
->pathptr
= sargptr
;
1383 } else if (ti
->vfslookup
> 1) {
1385 sargptr
= ti
->pathptr
;
1388 We don't want to overrun our pathname buffer if the
1389 kernel sends us more VFS_LOOKUP entries than we can
1393 if (sargptr
>= &ti
->pathname
[NUMPARMS
])
1397 We need to detect consecutive vfslookup entries.
1398 So, if we get here and find a START entry,
1399 fake the pathptr so we can bypass all further
1403 if (debugid
& DBG_FUNC_START
)
1405 ti
->pathptr
= &ti
->pathname
[NUMPARMS
];
1409 *sargptr
++ = kd
[i
].arg1
;
1410 *sargptr
++ = kd
[i
].arg2
;
1411 *sargptr
++ = kd
[i
].arg3
;
1412 *sargptr
++ = kd
[i
].arg4
;
1414 * NULL terminate the 'string'
1418 ti
->pathptr
= sargptr
;
1422 } else if (baseid
== bsc_base
)
1423 code
= (debugid
>> 2) & 0x1ff;
1424 else if (baseid
== msc_base
)
1425 code
= 512 + ((debugid
>> 2) & 0x1ff);
1426 else if (type
== mach_idle
) {
1427 if (debugid
& DBG_FUNC_START
) {
1428 switched_out
= find_thread(kd
[i
].arg5
);
1432 if (debugid
& DBG_FUNC_END
) {
1433 switched_in
= find_thread(kd
[i
].arg5
);
1438 itime_usecs
+= ((double)now
- idle_start
) / divisor
;
1439 delta_itime_usecs
+= ((double)now
- idle_start
) / divisor
;
1441 } else if (in_other
) {
1442 otime_usecs
+= ((double)now
- other_start
) / divisor
;
1443 delta_otime_usecs
+= ((double)now
- other_start
) / divisor
;
1446 if ( !switched_in
) {
1448 * not one of the target proc's threads
1450 if (now_collect_cpu_time
) {
1452 idle_start
= (double)now
;
1456 if (now_collect_cpu_time
) {
1459 other_start
= (double)now
;
1462 if ( !switched_in
&& !switched_out
)
1466 else if (type
== mach_sched
|| type
== mach_stkhandoff
) {
1467 switched_out
= find_thread(kd
[i
].arg5
);
1468 switched_in
= find_thread(kd
[i
].arg2
);
1471 itime_usecs
+= ((double)now
- idle_start
) / divisor
;
1472 delta_itime_usecs
+= ((double)now
- idle_start
) / divisor
;
1474 } else if (in_other
) {
1475 otime_usecs
+= ((double)now
- other_start
) / divisor
;
1476 delta_otime_usecs
+= ((double)now
- other_start
) / divisor
;
1479 if ( !switched_in
) {
1481 * not one of the target proc's threads
1483 if (now_collect_cpu_time
) {
1485 other_start
= (double)now
;
1488 if ( !switched_in
&& !switched_out
)
1492 else if ((baseid
& 0xff000000) == 0xff000000) {
1493 code
= find_msgcode (debugid
);
1496 } else if (baseid
!= mach_vmfault
)
1499 if (switched_out
|| switched_in
) {
1502 ti
->curpri
= (int)kd
[i
].arg3
;
1505 te
= &ti
->th_entry
[ti
->depth
-1];
1507 if (te
->sc_state
== KERNEL_MODE
)
1508 te
->ctime
+= (double)now
- te
->stime
;
1509 te
->sc_state
= WAITING
;
1514 te
= &ti
->th_entry
[0];
1516 if (te
->sc_state
== USER_MODE
)
1517 utime_usecs
+= ((double)now
- te
->stime
) / divisor
;
1518 te
->sc_state
= PREEMPTED
;
1521 te
->stime
= (double)now
;
1522 te
->otime
= (double)now
;
1523 now_collect_cpu_time
= 1;
1528 ti
->curpri
= (int)kd
[i
].arg4
;
1531 te
= &ti
->th_entry
[ti
->depth
-1];
1533 if (te
->sc_state
== WAITING
)
1534 te
->wtime
+= (double)now
- te
->stime
;
1535 te
->sc_state
= KERNEL_MODE
;
1537 te
= &ti
->th_entry
[0];
1539 te
->sc_state
= USER_MODE
;
1541 te
->stime
= (double)now
;
1542 te
->otime
= (double)now
;
1546 if ((ti
= find_thread(thread
)) == (struct th_info
*)0) {
1547 for (ti
= &th_state
[0]; ti
< &th_state
[MAX_THREADS
]; ti
++) {
1548 if (ti
->thread
== 0) {
1549 ti
->thread
= thread
;
1554 if (ti
== &th_state
[MAX_THREADS
])
1557 if (debugid
& DBG_FUNC_START
) {
1561 te
= &ti
->th_entry
[ti
->depth
-1];
1563 if (te
->sc_state
== KERNEL_MODE
)
1564 te
->ctime
+= (double)now
- te
->stime
;
1566 te
= &ti
->th_entry
[0];
1568 if (te
->sc_state
== USER_MODE
)
1569 utime_usecs
+= ((double)now
- te
->stime
) / divisor
;
1571 te
->stime
= (double)now
;
1572 te
->otime
= (double)now
;
1574 if (ti
->depth
< MAX_NESTED
) {
1575 te
= &ti
->th_entry
[ti
->depth
];
1577 te
->sc_state
= KERNEL_MODE
;
1580 te
->stime
= (double)now
;
1581 te
->otime
= (double)now
;
1582 te
->ctime
= (double)0;
1583 te
->wtime
= (double)0;
1587 } else if (debugid
& DBG_FUNC_END
) {
1592 se
= &faults
[kd
[i
].arg4
];
1595 if (se
->total_count
== 0)
1601 te
= &ti
->th_entry
[ti
->depth
-1];
1603 if (te
->type
== type
) {
1604 se
->stime_usecs
+= te
->ctime
/ divisor
;
1605 se
->stime_usecs
+= ((double)now
- te
->stime
) / divisor
;
1607 se
->wtime_usecs
+= te
->wtime
/ divisor
;
1608 se
->delta_wtime_usecs
+= te
->wtime
/ divisor
;
1610 secs
= se
->stime_usecs
/ 1000000;
1611 se
->stime_usecs
-= secs
* 1000000;
1612 se
->stime_secs
+= secs
;
1614 secs
= se
->wtime_usecs
/ 1000000;
1615 se
->wtime_usecs
-= secs
* 1000000;
1616 se
->wtime_secs
+= secs
;
1618 secs
= se
->delta_wtime_usecs
/ 1000000;
1619 se
->delta_wtime_usecs
-= secs
* 1000000;
1620 se
->delta_wtime_secs
+= secs
;
1624 if (ti
->depth
== 0) {
1626 * headed back to user mode
1627 * start the time accumulation
1629 te
= &ti
->th_entry
[0];
1630 te
->sc_state
= USER_MODE
;
1632 te
= &ti
->th_entry
[ti
->depth
-1];
1634 te
->stime
= (double)now
;
1635 te
->otime
= (double)now
;
1641 if (ti
->depth
== 0) {
1643 * headed back to user mode
1644 * start the time accumulation
1646 te
= &ti
->th_entry
[0];
1647 te
->sc_state
= USER_MODE
;
1648 te
->stime
= (double)now
;
1649 te
->otime
= (double)now
;
1654 secs
= utime_usecs
/ 1000000;
1655 utime_usecs
-= secs
* 1000000;
1658 secs
= itime_usecs
/ 1000000;
1659 itime_usecs
-= secs
* 1000000;
1662 secs
= delta_itime_usecs
/ 1000000;
1663 delta_itime_usecs
-= secs
* 1000000;
1664 delta_itime_secs
+= secs
;
1666 secs
= otime_usecs
/ 1000000;
1667 otime_usecs
-= secs
* 1000000;
1670 secs
= delta_otime_usecs
/ 1000000;
1671 delta_otime_usecs
-= secs
* 1000000;
1672 delta_otime_secs
+= secs
;
1682 This flag is turned off when calling
1683 quit() due to a set_remove() failure.
1685 if (set_remove_flag
)
1688 if (no_screen_refresh
== 0) {
1689 /* clear for new display */
1696 printf("sc_usage: ");
1706 mach_timebase_info_data_t info
;
1708 (void) mach_timebase_info (&info
);
1710 divisor
= ( (double)info
.denom
/ (double)info
.numer
) * 1000;
1723 ret
= (int)strtol(str
, &cp
, 10);
1724 if (cp
== str
|| *cp
) {
1725 /* Assume this is a command string and find first matching pid */
1726 for (i
=0; i
< kp_nentries
; i
++) {
1727 if (kp_buffer
[i
].kp_proc
.p_stat
== 0)
1730 if (!strcmp(str
, kp_buffer
[i
].kp_proc
.p_comm
)) {
1732 kp_buffer
[i
].kp_proc
.p_comm
,
1733 sizeof(proc_name
)-1);
1734 proc_name
[sizeof(proc_name
)-1] = '\0';
1735 return (kp_buffer
[i
].kp_proc
.p_pid
);
1740 for (i
=0; i
< kp_nentries
; i
++) {
1741 if (kp_buffer
[i
].kp_proc
.p_stat
== 0)
1743 else if (kp_buffer
[i
].kp_proc
.p_pid
== ret
) {
1745 kp_buffer
[i
].kp_proc
.p_comm
,
1746 sizeof(proc_name
)-1);
1747 proc_name
[sizeof(proc_name
)-1] = '\0';
1748 return (kp_buffer
[i
].kp_proc
.p_pid
);
1755 /* Returns index into sc_tab for a mach msg entry */
1757 find_msgcode(int debugid
)
1761 for (indx
=0; indx
< msgcode_cnt
; indx
++) {
1762 if (msgcode_tab
[indx
] == ((debugid
& 0x00ffffff) >>2))
1763 return (MAX_SC
+indx
);
1769 argtoi(int flag
, char *req
, char *str
, int base
)
1774 ret
= (int)strtol(str
, &cp
, base
);
1775 if (cp
== str
|| *cp
)
1776 errx(EINVAL
, "-%c flag requires a %s", flag
, req
);