2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
27 cc -I. -DKERNEL_PRIVATE -O -o sc_usage sc_usage.c -lncurses
30 #define Default_DELAY 1 /* default delay interval */
40 #include <sys/types.h>
41 #include <sys/param.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
93 /* If NUMPARMS from kernel changes, it will be reflected in PATHLENGTH as well */
95 #define PATHLENGTH (NUMPARMS*sizeof(long))
97 char *state_name
[] = {
106 #define KERNEL_MODE 1
127 char pathname
[PATHLENGTH
+ 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
;
175 int mach_stkhandoff
= 0;
177 int mach_vmfault
= 0;
184 #define DBG_FUNC_ALL (DBG_FUNC_START | DBG_FUNC_END)
185 #define DBG_FUNC_MASK 0xfffffffc
192 /* Default divisor */
193 #define DIVISOR 16.6666 /* Trace divisor converts to microseconds */
194 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;
209 extern char **environ
;
222 void leave() /* exit under normal conditions -- INT handler */
225 if (no_screen_refresh
== 0) {
231 set_pidcheck(pid
, 0);
236 void err_leave(s
) /* exit under error conditions */
240 if (no_screen_refresh
== 0) {
246 printf("sc_usage: ");
251 set_pidcheck(pid
, 0);
259 if (no_screen_refresh
== 0)
266 fprintf(stderr
, "Usage: %s [-c codefile] [-e] [-l] [-sn] pid | cmd | -E execute path\n", myname
);
267 fprintf(stderr
, " -c name of codefile containing mappings for syscalls\n");
268 fprintf(stderr
, " Default is /usr/share/misc/trace.codes\n");
269 fprintf(stderr
, " -e enable sort by call count\n");
270 fprintf(stderr
, " -l turn off top style output\n");
271 fprintf(stderr
, " -sn change sample rate to every n seconds\n");
272 fprintf(stderr
, " pid selects process to sample\n");
273 fprintf(stderr
, " cmd selects command to sample\n");
274 fprintf(stderr
, " -E Execute the given path and optional arguments\n");
280 #define usec_to_1000ths(t) ((t) / 1000)
282 void print_time(char *p
, unsigned int useconds
, unsigned int seconds
)
286 minutes
= seconds
/ 60;
287 hours
= minutes
/ 60;
289 if (minutes
< 100) { // up to 100 minutes
290 sprintf(p
, "%2ld:%02ld.%03ld", minutes
, seconds
% 60,
291 usec_to_1000ths(useconds
));
293 else if (hours
< 100) { // up to 100 hours
294 sprintf(p
, "%2ld:%02ld:%02ld ", hours
, minutes
% 60,
298 sprintf(p
, "%4ld hrs ", hours
);
307 char *myname
= "sc_usage";
308 char *codefile
= "/usr/share/misc/trace.codes";
311 int delay
= Default_DELAY
;
313 void screen_update();
317 void reset_counters();
322 if ( geteuid() != 0 ) {
323 printf("'sc_usage' must be run as root...\n");
329 if ((myname
= rindex(argv
[0], '/')) == 0) {
337 while ((ch
= getopt(argc
, argv
, "c:els:d:E")) != EOF
) {
340 delay
= argtoi('s', "decimal number", optarg
, 10);
346 no_screen_refresh
= 1;
355 /* exit_usage(myname);*/
356 exit_usage("default");
362 sc_tab_init(codefile
);
368 /* parse a pid or a command */
369 if((pid
= argtopid(argv
[optind
])) < 0 )
373 { /* execute this command */
378 ptr
= strrchr(argv
[optind
], '/');
384 strncpy(proc_name
, ptr
, sizeof(proc_name
)-1);
385 proc_name
[sizeof(proc_name
)-1] = '\0';
395 fprintf(stderr
, "Starting program: %s\n", argv
[optind
]);
398 switch ((pid
= vfork()))
405 ptrace(0,0,0,0); /* PT_TRACE_ME */
406 execve(argv
[optind
], &argv
[optind
], environ
);
421 if (no_screen_refresh
== 0) {
423 /* initializes curses and screen (last) */
424 if (initscr() == (WINDOW
*) 0)
426 printf("Unrecognized TERM type, try vt100\n");
438 /* set up signal handlers */
439 signal(SIGINT
, leave
);
440 signal(SIGQUIT
, leave
);
441 signal(SIGHUP
, leave
);
442 signal(SIGTERM
, leave
);
443 signal(SIGWINCH
, sigwinch
);
445 if (no_screen_refresh
== 0)
446 topn
= LINES
- Header_lines
;
451 if ((my_buffer
= malloc(SAMPLE_SIZE
* sizeof(kd_buf
))) == (char *)0)
452 quit("can't allocate memory for tracing info\n");
455 set_numbufs(SAMPLE_SIZE
);
457 set_pidcheck(pid
, 1);
460 ptrace(7, pid
, 1, 0); /* PT_CONTINUE */
465 if ((sort_now
= 10 / delay
) < 2)
469 (void)screen_update();
479 for (i
= 0; i
< (10 * delay
) && newLINES
== 0; i
++) {
481 if (no_screen_refresh
== 0) {
482 if ((c
= getch()) != ERR
&& (char)c
== 'q')
494 No need to check for initscr error return.
495 We won't get here if it fails on the first call.
501 topn
= LINES
- Header_lines
;
504 (void)screen_update();
509 print_row(struct sc_entry
*se
, int no_wtime
) {
514 sprintf(tbuf
, "%-23.23s %8d(%d)", se
->name
, se
->total_count
, se
->delta_count
);
516 sprintf(tbuf
, "%-23.23s %8d", se
->name
, se
->total_count
);
519 memset(&tbuf
[clen
], ' ', 45 - clen
);
521 print_time(&tbuf
[45], (unsigned long)(se
->stime_usecs
), se
->stime_secs
);
524 if (no_wtime
== 0 && (se
->wtime_usecs
|| se
->wtime_secs
)) {
525 sprintf(&tbuf
[clen
], " ");
526 clen
+= strlen(&tbuf
[clen
]);
528 print_time(&tbuf
[clen
], (unsigned long)(se
->wtime_usecs
), se
->wtime_secs
);
529 clen
+= strlen(&tbuf
[clen
]);
531 if (se
->waiting
|| se
->delta_wtime_usecs
|| se
->delta_wtime_secs
) {
533 sprintf(&tbuf
[clen
], "(");
534 clen
+= strlen(&tbuf
[clen
]);
536 print_time(&tbuf
[clen
], (unsigned long)(se
->delta_wtime_usecs
),
537 se
->delta_wtime_secs
);
538 clen
+= strlen(&tbuf
[clen
]);
540 sprintf(&tbuf
[clen
], ")");
541 clen
+= strlen(&tbuf
[clen
]);
544 if (se
->waiting
== 1)
545 sprintf(&tbuf
[clen
], " W");
547 sprintf(&tbuf
[clen
], " %d", se
->waiting
);
548 clen
+= strlen(&tbuf
[clen
]);
552 sprintf(&tbuf
[clen
], "\n");
554 if (tbuf
[COLS
-2] != '\n') {
558 if (no_screen_refresh
)
581 if (no_screen_refresh
== 0) {
582 /* clear for new display */
588 sprintf(tbuf
, "%-14.14s", proc_name
);
596 p2
= "context switch ";
598 p2
= "context switches";
599 if (num_of_threads
== 1)
604 sprintf(&tbuf
[clen
], " %4d %s %4d %s %4d %s",
605 preempted
, p1
, csw
, p2
, num_of_threads
, p3
);
606 clen
+= strlen(&tbuf
[clen
]);
609 * Display the current time.
610 * "ctime" always returns a string that looks like this:
612 * Sun Sep 16 01:03:52 1973
613 * 012345678901234567890123
616 * We want indices 11 thru 18 (length 8).
618 curr_time
= time((long *)0);
621 start_time
= curr_time
;
623 elapsed_secs
= curr_time
- start_time
;
624 minutes
= elapsed_secs
/ 60;
625 hours
= minutes
/ 60;
627 memset(&tbuf
[clen
], ' ', 78 - clen
);
631 sprintf(&tbuf
[clen
], "%-8.8s\n", &(ctime(&curr_time
)[11]));
633 if (tbuf
[COLS
-2] != '\n') {
637 if (no_screen_refresh
)
642 if (total_faults
== 1)
650 sprintf(tbuf
, " %4d %s %4d %s",
651 total_faults
, p1
, scalls
, p2
);
654 sprintf(&tbuf
[clen
], " %3ld:%02ld:%02ld\n",
655 hours
, minutes
% 60, elapsed_secs
% 60);
657 if (tbuf
[COLS
-2] != '\n') {
661 if (no_screen_refresh
)
668 sprintf(tbuf
, "\nTYPE NUMBER CPU_TIME WAIT_TIME\n");
670 if (tbuf
[COLS
-2] != '\n') {
674 if (no_screen_refresh
)
679 sprintf(tbuf
, "------------------------------------------------------------------------------\n");
680 if (tbuf
[COLS
-2] != '\n') {
684 if (no_screen_refresh
)
692 sprintf(tbuf
, "System Idle ");
695 print_time(&tbuf
[clen
], (unsigned long)(itime_usecs
), itime_secs
);
696 clen
+= strlen(&tbuf
[clen
]);
698 if (delta_itime_usecs
|| delta_itime_secs
) {
700 sprintf(&tbuf
[clen
], "(");
701 clen
+= strlen(&tbuf
[clen
]);
703 print_time(&tbuf
[clen
], (unsigned long)(delta_itime_usecs
), delta_itime_secs
);
704 clen
+= strlen(&tbuf
[clen
]);
706 sprintf(&tbuf
[clen
], ")");
707 clen
+= strlen(&tbuf
[clen
]);
709 sprintf(&tbuf
[clen
], "\n");
711 if (tbuf
[COLS
-2] != '\n') {
715 if (no_screen_refresh
)
723 sprintf(tbuf
, "System Busy ");
726 print_time(&tbuf
[clen
], (unsigned long)(otime_usecs
), otime_secs
);
727 clen
+= strlen(&tbuf
[clen
]);
729 if (delta_otime_usecs
|| delta_otime_secs
) {
731 sprintf(&tbuf
[clen
], "(");
732 clen
+= strlen(&tbuf
[clen
]);
734 print_time(&tbuf
[clen
], (unsigned long)(delta_otime_usecs
), delta_otime_secs
);
735 clen
+= strlen(&tbuf
[clen
]);
737 sprintf(&tbuf
[clen
], ")");
738 clen
+= strlen(&tbuf
[clen
]);
740 sprintf(&tbuf
[clen
], "\n");
742 if (tbuf
[COLS
-2] != '\n') {
746 if (no_screen_refresh
)
753 sprintf(tbuf
, "%-14.14s Usermode ", proc_name
);
756 print_time(&tbuf
[clen
], (unsigned long)(utime_usecs
), utime_secs
);
757 clen
+= strlen(&tbuf
[clen
]);
758 sprintf(&tbuf
[clen
], "\n");
760 if (tbuf
[COLS
-2] != '\n') {
764 if (no_screen_refresh
)
771 max_rows
= topn
- (num_of_threads
+ 3);
775 for (output_lf
= 1, n
= 1; rows
< max_rows
&& n
< MAX_FAULTS
; n
++) {
778 if (se
->total_count
== 0)
780 if (output_lf
== 1) {
782 if (no_screen_refresh
)
788 if (rows
>= max_rows
)
797 if (no_screen_refresh
)
803 for (i
= 0; rows
< max_rows
; i
++) {
805 n
= sort_by_count
[i
];
807 n
= sort_by_wtime
[i
];
810 print_row(&sc_tab
[n
], 0);
813 if (no_screen_refresh
== 0) {
816 while (rows
++ < max_rows
)
821 if (num_of_threads
) {
822 sprintf(tbuf
, "\nCURRENT_TYPE LAST_PATHNAME_WAITED_FOR CUR_WAIT_TIME THRD# PRI\n");
824 if (tbuf
[COLS
-2] != '\n') {
828 if (no_screen_refresh
)
833 sprintf(tbuf
, "------------------------------------------------------------------------------\n");
834 if (tbuf
[COLS
-2] != '\n') {
838 if (no_screen_refresh
)
845 for (i
= 0; i
< num_of_threads
; i
++, ti
++) {
848 int secs
, time_secs
, time_usecs
;
850 now
= mach_absolute_time();
852 while (ti
->thread
== 0 && ti
< &th_state
[MAX_THREADS
])
854 if (ti
== &th_state
[MAX_THREADS
])
858 te
= &ti
->th_entry
[ti
->depth
- 1];
860 if (te
->sc_state
== WAITING
) {
862 sprintf(tbuf
, "%-23.23s", sc_tab
[te
->code
].name
);
864 sprintf(tbuf
, "%-23.23s", "vm_fault");
866 sprintf(tbuf
, "%-23.23s", state_name
[te
->sc_state
]);
868 te
= &ti
->th_entry
[0];
869 sprintf(tbuf
, "%-23.23s", state_name
[te
->sc_state
]);
873 /* print the tail end of the pathname */
874 plen
= strlen(ti
->pathname
);
879 sprintf(&tbuf
[clen
], " %-34.34s ", &ti
->pathname
[plen
]);
881 clen
+= strlen(&tbuf
[clen
]);
883 time_usecs
= (unsigned long)(((double)now
- te
->otime
) / divisor
);
884 secs
= time_usecs
/ 1000000;
885 time_usecs
-= secs
* 1000000;
888 print_time(&tbuf
[clen
], time_usecs
, time_secs
);
889 clen
+= strlen(&tbuf
[clen
]);
890 sprintf(&tbuf
[clen
], " %2d %3d\n", i
, ti
->curpri
);
892 if (tbuf
[COLS
-2] != '\n') {
896 if (no_screen_refresh
)
901 if (no_screen_refresh
== 0) {
905 printf("\n=================\n");
909 for (i
= 0; i
< (MAX_SC
+ msgcode_cnt
); i
++) {
910 if ((n
= sort_by_count
[i
]) == -1)
912 sc_tab
[n
].delta_count
= 0;
913 sc_tab
[n
].waiting
= 0;
914 sc_tab
[n
].delta_wtime_usecs
= 0;
915 sc_tab
[n
].delta_wtime_secs
= 0;
917 for (i
= 1; i
< MAX_FAULTS
; i
++) {
918 faults
[i
].delta_count
= 0;
919 faults
[i
].waiting
= 0;
920 faults
[i
].delta_wtime_usecs
= 0;
921 faults
[i
].delta_wtime_secs
= 0;
927 delta_itime_secs
= 0;
928 delta_itime_usecs
= 0;
929 delta_otime_secs
= 0;
930 delta_otime_usecs
= 0;
937 for (i
= 0; i
< (MAX_SC
+ msgcode_cnt
) ; i
++) {
938 sc_tab
[i
].delta_count
= 0;
939 sc_tab
[i
].total_count
= 0;
940 sc_tab
[i
].waiting
= 0;
941 sc_tab
[i
].delta_wtime_usecs
= 0;
942 sc_tab
[i
].delta_wtime_secs
= 0;
943 sc_tab
[i
].wtime_usecs
= 0;
944 sc_tab
[i
].wtime_secs
= 0;
945 sc_tab
[i
].stime_usecs
= 0;
946 sc_tab
[i
].stime_secs
= 0;
948 for (i
= 1; i
< MAX_FAULTS
; i
++) {
949 faults
[i
].delta_count
= 0;
950 faults
[i
].total_count
= 0;
951 faults
[i
].waiting
= 0;
952 faults
[i
].delta_wtime_usecs
= 0;
953 faults
[i
].delta_wtime_secs
= 0;
954 faults
[i
].wtime_usecs
= 0;
955 faults
[i
].wtime_secs
= 0;
956 faults
[i
].stime_usecs
= 0;
957 faults
[i
].stime_secs
= 0;
969 delta_itime_secs
= 0;
970 delta_itime_usecs
= 0;
973 delta_otime_secs
= 0;
974 delta_otime_usecs
= 0;
978 sc_tab_init(char *codefile
) {
986 if ((fp
= fopen(codefile
,"r")) == (FILE *)0) {
987 printf("Failed to open code description file %s\n", codefile
);
991 n
= fscanf(fp
, "%d\n", &cnt
);
995 /* Count Mach message MSG_ codes */
996 for (msgcode_cnt
=0;;) {
997 n
= fscanf(fp
, "%x%s\n", &code
, &name
[0]);
1000 if (strncmp ("MSG_", &name
[0], 4) == 0)
1002 if (strcmp("USER_TEST", &name
[0]) == 0)
1006 sc_tab
= (struct sc_entry
*)malloc((MAX_SC
+msgcode_cnt
) * sizeof (struct sc_entry
));
1008 quit("can't allocate memory for system call table\n");
1009 bzero(sc_tab
,(MAX_SC
+msgcode_cnt
) * sizeof (struct sc_entry
));
1011 msgcode_tab
= (int *)malloc(msgcode_cnt
* sizeof(int));
1013 quit("can't allocate memory for msgcode table\n");
1014 bzero(msgcode_tab
,(msgcode_cnt
* sizeof(int)));
1016 sort_by_count
= (int *)malloc((MAX_SC
+ msgcode_cnt
+ 1) * sizeof(int));
1018 quit("can't allocate memory for sort_by_count table\n");
1019 bzero(sort_by_count
,(MAX_SC
+ msgcode_cnt
+ 1) * sizeof(int));
1021 sort_by_wtime
= (int *)malloc((MAX_SC
+ msgcode_cnt
+ 1) * sizeof(int));
1023 quit("can't allocate memory for sort_by_wtime table\n");
1024 bzero(sort_by_wtime
, (MAX_SC
+ msgcode_cnt
+ 1) * sizeof(int));
1029 n
= fscanf(fp
, "%d\n", &cnt
);
1035 n
= fscanf(fp
, "%x%s\n", &code
, &name
[0]);
1040 if (strcmp("MACH_vmfault", &name
[0]) == 0) {
1041 mach_vmfault
= code
;
1044 if (strcmp("MACH_SCHED", &name
[0]) == 0) {
1048 if (strcmp("MACH_STKHANDOFF", &name
[0]) == 0) {
1049 mach_stkhandoff
= code
;
1052 if (strcmp("VFS_LOOKUP", &name
[0]) == 0) {
1056 if (strcmp("BSC_SysCall", &name
[0]) == 0) {
1060 if (strcmp("MACH_SysCall", &name
[0]) == 0) {
1064 if (strcmp("BSC_exit", &name
[0]) == 0) {
1068 if (strncmp("MSG_", &name
[0], 4) == 0) {
1069 msgcode_tab
[msgcode_indx
] = ((code
& 0x00ffffff) >>2);
1070 n
= MAX_SC
+ msgcode_indx
;
1071 strncpy(&sc_tab
[n
].name
[0], &name
[4], 31 );
1075 if (strncmp("MSC_", &name
[0], 4) == 0) {
1076 n
= 512 + ((code
>>2) & 0x1ff);
1077 strcpy(&sc_tab
[n
].name
[0], &name
[4]);
1080 if (strncmp("BSC_", &name
[0], 4) == 0) {
1081 n
= (code
>>2) & 0x1ff;
1082 strcpy(&sc_tab
[n
].name
[0], &name
[4]);
1085 if (strcmp("USER_TEST", &name
[0]) == 0)
1088 strcpy(&faults
[1].name
[0], "zero_fill");
1089 strcpy(&faults
[2].name
[0], "pagein");
1090 strcpy(&faults
[3].name
[0], "copy_on_write");
1091 strcpy(&faults
[4].name
[0], "cache_hit");
1098 struct kinfo_proc
*kp
;
1102 mib
[2] = KERN_PROC_ALL
;
1105 if (sysctl(mib
, 4, NULL
, &bufSize
, NULL
, 0) < 0)
1106 quit("trace facility failure, KERN_PROC_ALL\n");
1108 if((kp
= (struct kinfo_proc
*)malloc(bufSize
)) == (struct kinfo_proc
*)0)
1109 quit("can't allocate memory for proc buffer\n");
1111 if (sysctl(mib
, 4, kp
, &bufSize
, NULL
, 0) < 0)
1112 quit("trace facility failure, KERN_PROC_ALL\n");
1114 kp_nentries
= bufSize
/ sizeof(struct kinfo_proc
);
1118 struct th_info
*find_thread(int thread
) {
1121 for (ti
= th_state
; ti
< &th_state
[MAX_THREADS
]; ti
++) {
1122 if (ti
->thread
== thread
)
1125 return ((struct th_info
*)0);
1130 cmp_wtime(struct sc_entry
*s1
, struct sc_entry
*s2
) {
1132 if (s1
->wtime_secs
< s2
->wtime_secs
)
1134 if (s1
->wtime_secs
> s2
->wtime_secs
)
1136 if (s1
->wtime_usecs
<= s2
->wtime_usecs
)
1144 int i
, n
, k
, cnt
, secs
;
1146 struct sc_entry
*se
;
1150 now
= mach_absolute_time();
1152 for (ti
= th_state
; ti
< &th_state
[MAX_THREADS
]; ti
++) {
1153 if (ti
->thread
== 0)
1157 te
= &ti
->th_entry
[ti
->depth
-1];
1159 if (te
->sc_state
== WAITING
) {
1161 se
= &sc_tab
[te
->code
];
1163 se
= &faults
[DBG_PAGEIN_FAULT
];
1165 se
->wtime_usecs
+= ((double)now
- te
->stime
) / divisor
;
1166 se
->delta_wtime_usecs
+= ((double)now
- te
->stime
) / divisor
;
1167 te
->stime
= (double)now
;
1169 secs
= se
->wtime_usecs
/ 1000000;
1170 se
->wtime_usecs
-= secs
* 1000000;
1171 se
->wtime_secs
+= secs
;
1173 secs
= se
->delta_wtime_usecs
/ 1000000;
1174 se
->delta_wtime_usecs
-= secs
* 1000000;
1175 se
->delta_wtime_secs
+= secs
;
1178 te
= &ti
->th_entry
[0];
1180 if (te
->sc_state
== PREEMPTED
) {
1181 if ((unsigned long)(((double)now
- te
->otime
) / divisor
) > 5000000) {
1184 ti
->pathptr
= (long *)0;
1185 ti
->pathname
[0] = 0;
1191 if ((called
% sort_now
) == 0) {
1192 sort_by_count
[0] = -1;
1193 sort_by_wtime
[0] = -1;
1194 for (cnt
= 1, n
= 1; n
< (MAX_SC
+ msgcode_cnt
); n
++) {
1195 if (sc_tab
[n
].total_count
) {
1196 for (i
= 0; i
< cnt
; i
++) {
1197 if ((k
= sort_by_count
[i
]) == -1 ||
1198 sc_tab
[n
].total_count
> sc_tab
[k
].total_count
) {
1200 for (k
= cnt
- 1; k
>= i
; k
--)
1201 sort_by_count
[k
+1] = sort_by_count
[k
];
1202 sort_by_count
[i
] = n
;
1206 if (how_to_sort
== 0) {
1207 for (i
= 0; i
< cnt
; i
++) {
1208 if ((k
= sort_by_wtime
[i
]) == -1 ||
1209 cmp_wtime(&sc_tab
[n
], &sc_tab
[k
])) {
1211 for (k
= cnt
- 1; k
>= i
; k
--)
1212 sort_by_wtime
[k
+1] = sort_by_wtime
[k
];
1213 sort_by_wtime
[i
] = n
;
1229 mib
[1] = KERN_KDEBUG
;
1230 mib
[2] = KERN_KDENABLE
; /* protocol */
1233 mib
[5] = 0; /* no flags */
1234 if (sysctl(mib
, 4, NULL
, &needed
, NULL
, 0) < 0)
1235 quit("trace facility failure, KERN_KDENABLE\n");
1244 set_numbufs(int nbufs
)
1247 mib
[1] = KERN_KDEBUG
;
1248 mib
[2] = KERN_KDSETBUF
;
1251 mib
[5] = 0; /* no flags */
1252 if (sysctl(mib
, 4, NULL
, &needed
, NULL
, 0) < 0)
1253 quit("trace facility failure, KERN_KDSETBUF\n");
1256 mib
[1] = KERN_KDEBUG
;
1257 mib
[2] = KERN_KDSETUP
;
1260 mib
[5] = 0; /* no flags */
1261 if (sysctl(mib
, 3, NULL
, &needed
, NULL
, 0) < 0)
1262 quit("trace facility failure, KERN_KDSETUP\n");
1267 set_pidcheck(int pid
, int on_off
)
1271 kr
.type
= KDBG_TYPENONE
;
1274 needed
= sizeof(kd_regtype
);
1276 mib
[1] = KERN_KDEBUG
;
1277 mib
[2] = KERN_KDPIDTR
;
1281 if (sysctl(mib
, 3, &kr
, &needed
, NULL
, 0) < 0) {
1283 printf("pid %d does not exist\n", pid
);
1291 get_bufinfo(kbufinfo_t
*val
)
1293 needed
= sizeof (*val
);
1295 mib
[1] = KERN_KDEBUG
;
1296 mib
[2] = KERN_KDGETBUF
;
1299 mib
[5] = 0; /* no flags */
1300 if (sysctl(mib
, 3, val
, &needed
, 0, 0) < 0)
1301 quit("trace facility failure, KERN_KDGETBUF\n");
1313 mib
[1] = KERN_KDEBUG
;
1314 mib
[2] = KERN_KDREMOVE
; /* protocol */
1317 mib
[5] = 0; /* no flags */
1319 if (sysctl(mib
, 3, NULL
, &needed
, NULL
, 0) < 0)
1321 set_remove_flag
= 0;
1324 quit("the trace facility is currently in use...\n fs_usage, sc_usage, and latency use this feature.\n\n");
1326 quit("trace facility failure, KERN_KDREMOVE\n");
1334 kr
.type
= KDBG_RANGETYPE
;
1337 needed
= sizeof(kd_regtype
);
1339 mib
[1] = KERN_KDEBUG
;
1340 mib
[2] = KERN_KDSETREG
;
1343 mib
[5] = 0; /* no flags */
1344 if (sysctl(mib
, 3, &kr
, &needed
, NULL
, 0) < 0)
1345 quit("trace facility failure, KERN_KDSETREG\n");
1348 mib
[1] = KERN_KDEBUG
;
1349 mib
[2] = KERN_KDSETUP
;
1352 mib
[5] = 0; /* no flags */
1353 if (sysctl(mib
, 3, NULL
, &needed
, NULL
, 0) < 0)
1354 quit("trace facility failure, KERN_KDSETUP\n");
1366 /* Get kernel buffer information */
1367 get_bufinfo(&bufinfo
);
1371 needed
= bufinfo
.nkdbufs
* sizeof(kd_buf
);
1373 mib
[1] = KERN_KDEBUG
;
1374 mib
[2] = KERN_KDREADTR
;
1377 mib
[5] = 0; /* no flags */
1378 if (sysctl(mib
, 3, my_buffer
, &needed
, NULL
, 0) < 0)
1379 quit("trace facility failure, KERN_KDREADTR\n");
1382 if (bufinfo
.flags
& KDBG_WRAPPED
) {
1383 for (i
= 0; i
< MAX_THREADS
; i
++) {
1384 th_state
[i
].depth
= 0;
1385 th_state
[i
].thread
= 0;
1386 th_state
[i
].vfslookup
= 0;
1387 th_state
[i
].pathptr
= (long *)0;
1388 th_state
[i
].pathname
[0] = 0;
1395 set_pidcheck(pid
, 1);
1396 set_enable(1); /* re-enable kernel logging */
1398 kd
= (kd_buf
*)my_buffer
;
1400 for (i
= 0; i
< count
; i
++) {
1401 int debugid
, baseid
, thread
;
1404 struct th_info
*ti
, *switched_out
, *switched_in
;
1405 struct sc_entry
*se
;
1408 thread
= kd
[i
].arg5
& KDBG_THREAD_MASK
;
1409 debugid
= kd
[i
].debugid
;
1410 type
= kd
[i
].debugid
& DBG_FUNC_MASK
;
1413 switched_out
= (struct th_info
*)0;
1414 switched_in
= (struct th_info
*)0;
1416 now
= (((uint64_t)kd
[i
].timestamp
.tv_sec
) << 32) |
1417 (uint64_t)((unsigned int)(kd
[i
].timestamp
.tv_nsec
));
1418 baseid
= debugid
& 0xffff0000;
1420 if (type
== vfs_lookup
) {
1423 if ((ti
= find_thread(thread
)) == (struct th_info
*)0)
1426 if (ti
->vfslookup
== 1) {
1428 memset(&ti
->pathname
[0], 0, (PATHLENGTH
+ 1));
1429 sargptr
= (long *)&ti
->pathname
[0];
1431 *sargptr
++ = kd
[i
].arg2
;
1432 *sargptr
++ = kd
[i
].arg3
;
1433 *sargptr
++ = kd
[i
].arg4
;
1434 ti
->pathptr
= sargptr
;
1436 } else if (ti
->vfslookup
> 1) {
1438 sargptr
= ti
->pathptr
;
1441 We don't want to overrun our pathname buffer if the
1442 kernel sends us more VFS_LOOKUP entries than we can
1446 if ((long *)sargptr
>= (long *)&ti
->pathname
[PATHLENGTH
])
1450 We need to detect consecutive vfslookup entries.
1451 So, if we get here and find a START entry,
1452 fake the pathptr so we can bypass all further
1456 if (debugid
& DBG_FUNC_START
)
1458 (long *)ti
->pathptr
= (long *)&ti
->pathname
[PATHLENGTH
];
1462 *sargptr
++ = kd
[i
].arg1
;
1463 *sargptr
++ = kd
[i
].arg2
;
1464 *sargptr
++ = kd
[i
].arg3
;
1465 *sargptr
++ = kd
[i
].arg4
;
1466 ti
->pathptr
= sargptr
;
1470 } else if (baseid
== bsc_base
)
1471 code
= (debugid
>> 2) & 0x1ff;
1472 else if (baseid
== msc_base
)
1473 code
= 512 + ((debugid
>> 2) & 0x1ff);
1474 else if (baseid
== mach_sched
|| baseid
== mach_stkhandoff
) {
1475 switched_out
= find_thread(kd
[i
].arg1
);
1476 switched_in
= find_thread(kd
[i
].arg2
);
1479 itime_usecs
+= ((double)now
- idle_start
) / divisor
;
1480 delta_itime_usecs
+= ((double)now
- idle_start
) / divisor
;
1482 } else if (in_other
) {
1483 otime_usecs
+= ((double)now
- other_start
) / divisor
;
1484 delta_otime_usecs
+= ((double)now
- other_start
) / divisor
;
1487 if ( !switched_in
) {
1489 * not one of the target proc's threads
1491 if (now_collect_cpu_time
) {
1492 if (kd
[i
].arg4
== 0) {
1494 idle_start
= (double)now
;
1497 other_start
= (double)now
;
1501 if ( !switched_in
&& !switched_out
)
1505 else if ((baseid
& 0xff000000) == 0xff000000) {
1506 code
= find_msgcode (debugid
);
1509 } else if (baseid
!= mach_vmfault
)
1512 if (switched_out
|| switched_in
) {
1515 ti
->curpri
= kd
[i
].arg3
;
1518 te
= &ti
->th_entry
[ti
->depth
-1];
1520 if (te
->sc_state
== KERNEL_MODE
)
1521 te
->ctime
+= (double)now
- te
->stime
;
1522 te
->sc_state
= WAITING
;
1527 te
= &ti
->th_entry
[0];
1529 if (te
->sc_state
== USER_MODE
)
1530 utime_usecs
+= ((double)now
- te
->stime
) / divisor
;
1531 te
->sc_state
= PREEMPTED
;
1534 te
->stime
= (double)now
;
1535 te
->otime
= (double)now
;
1536 now_collect_cpu_time
= 1;
1541 ti
->curpri
= kd
[i
].arg4
;
1544 te
= &ti
->th_entry
[ti
->depth
-1];
1546 if (te
->sc_state
== WAITING
)
1547 te
->wtime
+= (double)now
- te
->stime
;
1548 te
->sc_state
= KERNEL_MODE
;
1550 te
= &ti
->th_entry
[0];
1552 te
->sc_state
= USER_MODE
;
1554 te
->stime
= (double)now
;
1555 te
->otime
= (double)now
;
1559 if ((ti
= find_thread(thread
)) == (struct th_info
*)0) {
1560 for (ti
= &th_state
[0]; ti
< &th_state
[MAX_THREADS
]; ti
++) {
1561 if (ti
->thread
== 0) {
1562 ti
->thread
= thread
;
1567 if (ti
== &th_state
[MAX_THREADS
])
1570 if (debugid
& DBG_FUNC_START
) {
1574 te
= &ti
->th_entry
[ti
->depth
-1];
1576 if (te
->sc_state
== KERNEL_MODE
)
1577 te
->ctime
+= (double)now
- te
->stime
;
1579 te
= &ti
->th_entry
[0];
1581 if (te
->sc_state
== USER_MODE
)
1582 utime_usecs
+= ((double)now
- te
->stime
) / divisor
;
1584 te
->stime
= (double)now
;
1585 te
->otime
= (double)now
;
1587 if (ti
->depth
< MAX_NESTED
) {
1588 te
= &ti
->th_entry
[ti
->depth
];
1590 te
->sc_state
= KERNEL_MODE
;
1593 te
->stime
= (double)now
;
1594 te
->otime
= (double)now
;
1595 te
->ctime
= (double)0;
1596 te
->wtime
= (double)0;
1600 } else if (debugid
& DBG_FUNC_END
) {
1605 se
= &faults
[kd
[i
].arg2
];
1608 if (se
->total_count
== 0)
1614 te
= &ti
->th_entry
[ti
->depth
-1];
1616 if (te
->type
== type
) {
1617 se
->stime_usecs
+= te
->ctime
/ divisor
;
1618 se
->stime_usecs
+= ((double)now
- te
->stime
) / divisor
;
1620 se
->wtime_usecs
+= te
->wtime
/ divisor
;
1621 se
->delta_wtime_usecs
+= te
->wtime
/ divisor
;
1623 secs
= se
->stime_usecs
/ 1000000;
1624 se
->stime_usecs
-= secs
* 1000000;
1625 se
->stime_secs
+= secs
;
1627 secs
= se
->wtime_usecs
/ 1000000;
1628 se
->wtime_usecs
-= secs
* 1000000;
1629 se
->wtime_secs
+= secs
;
1631 secs
= se
->delta_wtime_usecs
/ 1000000;
1632 se
->delta_wtime_usecs
-= secs
* 1000000;
1633 se
->delta_wtime_secs
+= secs
;
1637 if (ti
->depth
== 0) {
1639 * headed back to user mode
1640 * start the time accumulation
1642 te
= &ti
->th_entry
[0];
1643 te
->sc_state
= USER_MODE
;
1645 te
= &ti
->th_entry
[ti
->depth
-1];
1647 te
->stime
= (double)now
;
1648 te
->otime
= (double)now
;
1654 if (ti
->depth
== 0) {
1656 * headed back to user mode
1657 * start the time accumulation
1659 te
= &ti
->th_entry
[0];
1660 te
->sc_state
= USER_MODE
;
1661 te
->stime
= (double)now
;
1662 te
->otime
= (double)now
;
1667 secs
= utime_usecs
/ 1000000;
1668 utime_usecs
-= secs
* 1000000;
1671 secs
= itime_usecs
/ 1000000;
1672 itime_usecs
-= secs
* 1000000;
1675 secs
= delta_itime_usecs
/ 1000000;
1676 delta_itime_usecs
-= secs
* 1000000;
1677 delta_itime_secs
+= secs
;
1679 secs
= otime_usecs
/ 1000000;
1680 otime_usecs
-= secs
* 1000000;
1683 secs
= delta_otime_usecs
/ 1000000;
1684 delta_otime_usecs
-= secs
* 1000000;
1685 delta_otime_secs
+= secs
;
1696 This flag is turned off when calling
1697 quit() due to a set_remove() failure.
1699 if (set_remove_flag
)
1702 printf("sc_usage: ");
1711 mach_timebase_info_data_t info
;
1713 (void) mach_timebase_info (&info
);
1715 divisor
= ( (double)info
.denom
/ (double)info
.numer
) * 1000;
1730 ret
= (int)strtol(str
, &cp
, 10);
1731 if (cp
== str
|| *cp
) {
1732 /* Assume this is a command string and find first matching pid */
1733 for (i
=0; i
< kp_nentries
; i
++) {
1734 if(kp_buffer
[i
].kp_proc
.p_stat
== 0)
1737 if(!strcmp(str
, kp_buffer
[i
].kp_proc
.p_comm
))
1739 strncpy(proc_name
, kp_buffer
[i
].kp_proc
.p_comm
, sizeof(proc_name
)-1);
1740 proc_name
[sizeof(proc_name
)-1] = '\0';
1741 return(kp_buffer
[i
].kp_proc
.p_pid
);
1748 for (i
=0; i
< kp_nentries
; i
++)
1750 if(kp_buffer
[i
].kp_proc
.p_stat
== 0)
1752 else if (kp_buffer
[i
].kp_proc
.p_pid
== ret
) {
1753 strncpy(proc_name
, kp_buffer
[i
].kp_proc
.p_comm
, sizeof(proc_name
)-1);
1754 proc_name
[sizeof(proc_name
)-1] = '\0';
1755 return(kp_buffer
[i
].kp_proc
.p_pid
);
1763 /* Returns index into sc_tab for a mach msg entry */
1765 find_msgcode(int debugid
)
1770 for (indx
=0; indx
< msgcode_cnt
; indx
++)
1772 if (msgcode_tab
[indx
] == ((debugid
& 0x00ffffff) >>2))
1773 return (MAX_SC
+indx
);
1778 argtoi(flag
, req
, str
, base
)
1786 ret
= (int)strtol(str
, &cp
, base
);
1787 if (cp
== str
|| *cp
)
1788 errx(EINVAL
, "-%c flag requires a %s", flag
, req
);