]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright (c) 2012 Apple Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * This file contains Original Code and/or Modifications of Original Code | |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. Please obtain a copy of the License at | |
10 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
11 | * file. | |
12 | * | |
13 | * The Original Code and all software distributed under the License are | |
14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
18 | * Please see the License for the specific language governing rights and | |
19 | * limitations under the License. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | */ | |
23 | ||
24 | #include <mach/mach.h> | |
25 | #include <err.h> | |
26 | #include <errno.h> | |
27 | #include <stdbool.h> | |
28 | #include <stdint.h> | |
29 | #include <stdio.h> | |
30 | #include <stdlib.h> | |
31 | #include <unistd.h> | |
32 | #include <libproc.h> | |
33 | #include <mach/mach_time.h> | |
34 | ||
35 | static void usage(void); | |
36 | static void do_print(void); | |
37 | static void do_difftime(bool usepercent, uint64_t *olduser, uint64_t *oldsystem, uint64_t *oldidle); | |
38 | static void do_piddifftime(bool userpercent, int pid, uint64_t *old_pid_user, uint64_t *old_pid_system, uint64_t *old_pid_time); | |
39 | static kern_return_t get_processor_time(uint64_t *user, uint64_t *sys, uint64_t *idle); | |
40 | static kern_return_t get_processor_count(int *ncpu); | |
41 | static mach_timebase_info_data_t timebase_info; | |
42 | ||
43 | int | |
44 | main(int argc, char *argv[]) | |
45 | { | |
46 | int ch; | |
47 | const char *optu = NULL; | |
48 | const char *opts = NULL; | |
49 | const char *opti = NULL; | |
50 | const char *tpid = NULL; | |
51 | const char *opt_sleep_time = NULL; | |
52 | int sleep_time = 1; | |
53 | int target_pid; | |
54 | bool systemwide_time = false; | |
55 | int pid; | |
56 | int status; | |
57 | uint64_t olduser, oldsystem, oldidle; | |
58 | uint64_t old_pid_time; | |
59 | uint64_t old_pid_user, old_pid_system; | |
60 | kern_return_t kret; | |
61 | bool usepercent = false; | |
62 | bool recurring = false; | |
63 | ||
64 | while ((ch = getopt(argc, argv, "PrT:t:pu:s:i:")) != -1) { | |
65 | switch (ch) { | |
66 | case 'P': | |
67 | usepercent = true; | |
68 | break; | |
69 | case 'r': | |
70 | recurring = true; | |
71 | break; | |
72 | case 't': | |
73 | opt_sleep_time = optarg; | |
74 | break; | |
75 | case 'T': | |
76 | tpid = optarg; | |
77 | break; | |
78 | case 'p': | |
79 | systemwide_time = true; | |
80 | break; | |
81 | case 'u': | |
82 | optu = optarg; | |
83 | break; | |
84 | case 's': | |
85 | opts = optarg; | |
86 | break; | |
87 | case 'i': | |
88 | opti = optarg; | |
89 | break; | |
90 | case '?': | |
91 | default: | |
92 | usage(); | |
93 | } | |
94 | } | |
95 | ||
96 | mach_timebase_info(&timebase_info); | |
97 | ||
98 | if (opt_sleep_time) { | |
99 | char *endstr; | |
100 | sleep_time = (int)strtoul(opt_sleep_time, &endstr, 0); | |
101 | if (opt_sleep_time[0] == '\0' || endstr[0] != '\0') | |
102 | usage(); | |
103 | } | |
104 | ||
105 | if (systemwide_time) { | |
106 | bool first_pass = true; | |
107 | olduser = oldsystem = oldidle = 0; | |
108 | ||
109 | if (recurring != true) { | |
110 | do_print(); | |
111 | exit(0); | |
112 | } | |
113 | do { | |
114 | if (first_pass) { | |
115 | do_difftime(false, &olduser, &oldsystem, &oldidle); | |
116 | first_pass = false; | |
117 | } else { | |
118 | do_difftime(usepercent, &olduser, &oldsystem, &oldidle); | |
119 | } | |
120 | sleep(sleep_time); | |
121 | } while (recurring); | |
122 | ||
123 | exit(0); | |
124 | } | |
125 | ||
126 | ||
127 | if (tpid) { | |
128 | char *endstr; | |
129 | bool first_pass = true; | |
130 | ||
131 | target_pid = (int)strtoul(tpid, &endstr, 0); | |
132 | if (tpid[0] == '\0' || endstr[0] != '\0') | |
133 | usage(); | |
134 | ||
135 | olduser = oldsystem = oldidle = 0; | |
136 | old_pid_user = old_pid_system = old_pid_time = 0; | |
137 | ||
138 | do { | |
139 | if (first_pass) { | |
140 | do_difftime(false, &olduser, &oldsystem, &oldidle); | |
141 | do_piddifftime(false, target_pid, &old_pid_user, &old_pid_system, &old_pid_time); | |
142 | first_pass = false; | |
143 | } else { | |
144 | do_difftime(usepercent, &olduser, &oldsystem, &oldidle); | |
145 | do_piddifftime(usepercent, target_pid, &old_pid_user, &old_pid_system, &old_pid_time); | |
146 | } | |
147 | ||
148 | sleep(sleep_time); | |
149 | } while (recurring); | |
150 | exit(0); | |
151 | } | |
152 | ||
153 | if (optu || opts || opti) { | |
154 | char *endstr; | |
155 | ||
156 | if (!optu) | |
157 | usage(); | |
158 | olduser = strtoull(optu, &endstr, 0); | |
159 | if (optu[0] == '\0' || endstr[0] != '\0') | |
160 | usage(); | |
161 | ||
162 | if (!opts) | |
163 | usage(); | |
164 | oldsystem = strtoull(opts, &endstr, 0); | |
165 | if (opts[0] == '\0' || endstr[0] != '\0') | |
166 | usage(); | |
167 | ||
168 | if (!opti) | |
169 | usage(); | |
170 | oldidle = strtoull(opti, &endstr, 0); | |
171 | if (opti[0] == '\0' || endstr[0] != '\0') | |
172 | usage(); | |
173 | ||
174 | do_difftime(usepercent, &olduser, &oldsystem, &oldidle); | |
175 | exit(0); | |
176 | } | |
177 | ||
178 | argc -= optind; | |
179 | argv += optind; | |
180 | ||
181 | if (argc == 0) | |
182 | usage(); | |
183 | ||
184 | do { | |
185 | kret = get_processor_time(&olduser, &oldsystem, &oldidle); | |
186 | if (kret) | |
187 | errx(1, "Error getting processor time: %s (%d)", mach_error_string(kret), kret); | |
188 | ||
189 | switch(pid = vfork()) { | |
190 | case -1: /* error */ | |
191 | perror("time"); | |
192 | exit(1); | |
193 | /* NOTREACHED */ | |
194 | case 0: /* child */ | |
195 | execvp(*argv, argv); | |
196 | perror(*argv); | |
197 | _exit((errno == ENOENT) ? 127 : 126); | |
198 | /* NOTREACHED */ | |
199 | } | |
200 | ||
201 | /* parent */ | |
202 | (void)signal(SIGINT, SIG_IGN); | |
203 | (void)signal(SIGQUIT, SIG_IGN); | |
204 | while (wait(&status) != pid); | |
205 | (void)signal(SIGINT, SIG_DFL); | |
206 | (void)signal(SIGQUIT, SIG_DFL); | |
207 | ||
208 | do_difftime(usepercent, &olduser, &oldsystem, &oldidle); | |
209 | ||
210 | sleep(sleep_time); | |
211 | } while (recurring); | |
212 | ||
213 | exit (WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE); | |
214 | ||
215 | return 0; | |
216 | } | |
217 | ||
218 | static void | |
219 | usage(void) | |
220 | { | |
221 | fprintf(stderr, "usage: systime [-P] [-r] [-t sleep_time] utility [argument ...]\n" | |
222 | " systime -p [-r] [-t sleep_time]\n" | |
223 | " systime [-P] -u user -s sys -i idle\n" | |
224 | " systime [-P] [-r] [-t sleep_time] -T target_pid\n"); | |
225 | exit(1); | |
226 | } | |
227 | ||
228 | static void | |
229 | do_print(void) | |
230 | { | |
231 | uint64_t user, system, idle; | |
232 | kern_return_t kret; | |
233 | ||
234 | kret = get_processor_time(&user, &system, &idle); | |
235 | if (kret) | |
236 | errx(1, "Error getting processor time: %s (%d)", mach_error_string(kret), kret); | |
237 | ||
238 | printf("systime_user=%llu\n", user); | |
239 | printf("systime_sys=%llu\n", system); | |
240 | printf("systime_idle=%llu\n", idle); | |
241 | } | |
242 | ||
243 | static void | |
244 | do_difftime(bool usepercent, uint64_t *olduser, uint64_t *oldsystem, uint64_t *oldidle) | |
245 | { | |
246 | uint64_t user, system, idle; | |
247 | uint64_t userelapsed, systemelapsed, idleelapsed, totalelapsed; | |
248 | kern_return_t kret; | |
249 | ||
250 | kret = get_processor_time(&user, &system, &idle); | |
251 | if (kret) | |
252 | errx(1, "Error getting processor time: %s (%d)", mach_error_string(kret), kret); | |
253 | ||
254 | userelapsed = user - *olduser; | |
255 | systemelapsed = system - *oldsystem; | |
256 | idleelapsed = idle - *oldidle; | |
257 | totalelapsed = userelapsed + systemelapsed + idleelapsed; | |
258 | ||
259 | if (usepercent) { | |
260 | fprintf(stderr, "%1.02f%% user %1.02f%% sys %1.02f%% idle\n", | |
261 | ((double)userelapsed * 100)/totalelapsed, | |
262 | ((double)systemelapsed * 100)/totalelapsed, | |
263 | ((double)idleelapsed * 100)/totalelapsed); | |
264 | } else { | |
265 | int ncpu; | |
266 | ||
267 | kret = get_processor_count(&ncpu); | |
268 | if (kret) | |
269 | errx(1, "Error getting processor count: %s (%d)", mach_error_string(kret), kret); | |
270 | ||
271 | fprintf(stderr, "%1.02f real %1.02f user %1.02f sys\n", | |
272 | ((double)totalelapsed) / 1000 /* ms per sec */ / ncpu, | |
273 | ((double)userelapsed) / 1000, | |
274 | ((double)systemelapsed) / 1000); | |
275 | } | |
276 | *olduser = user; | |
277 | *oldsystem = system; | |
278 | *oldidle = idle; | |
279 | } | |
280 | ||
281 | static void | |
282 | do_piddifftime(bool usepercent, int pid, uint64_t *old_pid_user, uint64_t *old_pid_system, uint64_t *old_pid_time) | |
283 | { | |
284 | uint64_t pid_user, pid_system, pid_time; | |
285 | uint64_t userelapsed, systemelapsed, totalelapsed; | |
286 | struct proc_taskinfo info; | |
287 | int size; | |
288 | ||
289 | size = proc_pidinfo(pid, PROC_PIDTASKINFO, 0, &info, sizeof(info)); | |
290 | if (size == PROC_PIDTASKINFO_SIZE) { | |
291 | pid_user = info.pti_total_user; | |
292 | pid_system = info.pti_total_system; | |
293 | } else { | |
294 | fprintf(stderr, "Error in proc_pidinfo(): %s", | |
295 | strerror(errno)); | |
296 | exit(1); | |
297 | } | |
298 | ||
299 | pid_time = mach_absolute_time(); | |
300 | ||
301 | userelapsed = pid_user - *old_pid_user; | |
302 | systemelapsed = pid_system - *old_pid_system; | |
303 | totalelapsed = pid_time - *old_pid_time; | |
304 | ||
305 | if (usepercent) { | |
306 | fprintf(stderr, "Pid %d: %1.02f%% user %1.02f%% sys\n", | |
307 | pid, | |
308 | ((double)userelapsed * 100)/totalelapsed, | |
309 | ((double)systemelapsed * 100)/totalelapsed); | |
310 | } else { | |
311 | fprintf(stderr, "Pid %d: %1.02f user %1.02f sys\n", | |
312 | pid, | |
313 | (((double)userelapsed) * timebase_info.numer / timebase_info.denom) / 1000000000, | |
314 | (((double)systemelapsed) * timebase_info.numer / timebase_info.denom) / 1000000000); | |
315 | } | |
316 | ||
317 | *old_pid_user = pid_user; | |
318 | *old_pid_system = pid_system; | |
319 | *old_pid_time = pid_time; | |
320 | ||
321 | } | |
322 | ||
323 | static kern_return_t | |
324 | get_processor_time(uint64_t *user, uint64_t *sys, uint64_t *idle) | |
325 | { | |
326 | host_name_port_t host; | |
327 | kern_return_t kret; | |
328 | host_cpu_load_info_data_t host_load; | |
329 | mach_msg_type_number_t count; | |
330 | ||
331 | host = mach_host_self(); | |
332 | ||
333 | count = HOST_CPU_LOAD_INFO_COUNT; | |
334 | ||
335 | kret = host_statistics(host, HOST_CPU_LOAD_INFO, (host_info_t)&host_load, &count); | |
336 | if (kret) | |
337 | return kret; | |
338 | ||
339 | *user = ((uint64_t)host_load.cpu_ticks[CPU_STATE_USER]) * 10 /* ms per tick */; | |
340 | *sys = ((uint64_t)host_load.cpu_ticks[CPU_STATE_SYSTEM]) * 10; | |
341 | *idle = ((uint64_t)host_load.cpu_ticks[CPU_STATE_IDLE]) * 10; | |
342 | ||
343 | return KERN_SUCCESS; | |
344 | } | |
345 | ||
346 | static kern_return_t | |
347 | get_processor_count(int *ncpu) | |
348 | { | |
349 | host_name_port_t host; | |
350 | kern_return_t kret; | |
351 | host_basic_info_data_t hi; | |
352 | mach_msg_type_number_t count; | |
353 | ||
354 | host = mach_host_self(); | |
355 | ||
356 | count = HOST_BASIC_INFO_COUNT; | |
357 | ||
358 | kret = host_info(host, HOST_BASIC_INFO, (host_info_t)&hi, &count); | |
359 | if (kret) | |
360 | return kret; | |
361 | ||
362 | *ncpu = hi.avail_cpus; | |
363 | ||
364 | return KERN_SUCCESS; | |
365 | } |