]> git.saurik.com Git - apple/system_cmds.git/blob - ltop.tproj/ltop.c
system_cmds-735.50.6.tar.gz
[apple/system_cmds.git] / ltop.tproj / ltop.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <stdint.h>
4 #include <errno.h>
5 #include <string.h>
6 #include <unistd.h>
7 #include <libproc.h>
8 #include <sys/types.h>
9 #include <sys/sysctl.h>
10 #include <Kernel/kern/ledger.h>
11 #include <mach/mach_types.h>
12
13 extern int ledger(int cmd, caddr_t arg1, caddr_t arg2, caddr_t arg3);
14
15 int pid = -1;
16 char *group_print = NULL;
17 char *resource_print = NULL;
18 int diff_mode = 0;
19
20 struct proc_list {
21 int pid;
22 int seen;
23 char name[2 * MAXCOMLEN];
24 struct ledger *ledger;
25 struct proc_list *next;
26 };
27
28 struct proc_list *procs = NULL;
29 struct ledger_template_info *template = NULL;
30 int entry_cnt = 0;
31
32 struct ledger {
33 int64_t id;
34 int seen;
35 int64_t entries;
36 struct ledger_entry_info *info;
37 struct ledger_entry_info *old_info;
38 struct ledger *next;
39 };
40
41 struct ledger *ledgers = NULL;
42
43 static void
44 get_template_info(void)
45 {
46
47 void *buf;
48 int cnt;
49
50 top:
51 /* Allocate enough space to accomodate a few new entries */
52 cnt = entry_cnt + 5;
53 buf = malloc(cnt * sizeof (struct ledger_template_info));
54 if (buf == NULL) {
55 fprintf(stderr, "Out of memory\n");
56 exit (1);
57 }
58
59 if (ledger(LEDGER_TEMPLATE_INFO, (caddr_t)buf, (caddr_t)&cnt, NULL) < 0) {
60 perror("ledger() system call failed");
61 exit(1);
62 }
63
64 /* We underestimated how many entries we needed. Let's try again */
65 if (cnt == entry_cnt + 5) {
66 entry_cnt += 5;
67 free(buf);
68 goto top;
69 }
70 entry_cnt = cnt;
71 template = buf;
72 }
73
74 /*
75 * Note - this is a destructive operation. Unless we're about to exit, this
76 * needs to be followed by another call to get_template_info().
77 */
78 static void
79 dump_template_info(void)
80 {
81 int i, j;
82 const char *group = NULL;
83
84 printf("Resources being tracked:\n");
85 printf("\t%10s %15s %8s\n", "GROUP", "RESOURCE", "UNITS");
86 for (i = 0; i < entry_cnt; i++) {
87 if (strlen(template[i].lti_name) == 0)
88 continue;
89
90 group = template[i].lti_group;
91 for (j = i; j < entry_cnt; j++) {
92 if (strcmp(template[j].lti_group, group))
93 continue;
94 printf("\t%10s %15s %8s\n", template[j].lti_group,
95 template[j].lti_name, template[j].lti_units);
96 template[j].lti_name[0] = '\0';
97 }
98 }
99 }
100
101 static void
102 validate_group(void)
103 {
104 int i;
105
106 if (template == NULL)
107 get_template_info();
108
109 for (i = 0; i < entry_cnt; i++)
110 if (!strcmp(group_print, template[i].lti_group))
111 return;
112
113 fprintf(stderr, "No such group: %s\n", group_print);
114 exit (1);
115 }
116
117 static void
118 validate_resource(void)
119 {
120 int i;
121
122 if (template == NULL)
123 get_template_info();
124
125 for (i = 0; i < entry_cnt; i++)
126 if (!strcmp(resource_print, template[i].lti_name))
127 return;
128
129 fprintf(stderr, "No such resource: %s\n", resource_print);
130 exit (1);
131 }
132
133 static size_t
134 get_kern_max_proc(void)
135 {
136 int mib[] = { CTL_KERN, KERN_MAXPROC };
137 int max;
138 size_t max_sz = sizeof (max);
139
140 if (sysctl(mib, 2, &max, &max_sz, NULL, 0) < 0) {
141 perror("Failed to get max proc count");
142 exit (1);
143 }
144
145 return (max);
146 }
147
148 static struct ledger *
149 ledger_find(struct ledger_info *li)
150 {
151 struct ledger *l;
152
153 for (l = ledgers; l && (li->li_id != l->id); l = l->next)
154 ;
155
156 if (l == NULL) {
157 l = (struct ledger *)malloc(sizeof (*l));
158 if (l == NULL) {
159 fprintf(stderr, "Out of memory");
160 exit (1);
161 }
162 l->id = li->li_id;
163 l->entries = li->li_entries;
164 l->next = ledgers;
165 l->seen = 0;
166 l->info = NULL;
167 l->old_info = NULL;
168 ledgers = l;
169 }
170 return (l);
171 }
172
173 static void
174 ledger_update(pid_t pid, struct ledger *l)
175 {
176 void *arg;
177 struct ledger_entry_info *lei;
178 int64_t cnt;
179
180 cnt = l->entries;
181 if (cnt > entry_cnt)
182 cnt = entry_cnt;
183 arg = (void *)(long)pid;
184 lei = (struct ledger_entry_info *)malloc((size_t)(cnt * sizeof (*lei)));
185 if (ledger(LEDGER_ENTRY_INFO, arg, (caddr_t)lei, (caddr_t)&cnt) < 0) {
186 perror("ledger_info() failed: ");
187 exit (1);
188 }
189 l->info = lei;
190 }
191
192 static void
193 get_proc_info(int pid)
194 {
195 struct ledger_info li;
196 struct ledger *ledgerp;
197 struct proc_list *proc;
198 void *arg;
199
200 if (pid == 0)
201 return;
202
203 arg = (void *)(long)pid;
204 errno = 0;
205 if (ledger(LEDGER_INFO, arg, (caddr_t)&li, NULL) < 0) {
206
207 if (errno == ENOENT || errno == ESRCH)
208 return;
209
210 perror("ledger_info() failed: ");
211 exit (1);
212 }
213
214 ledgerp = ledger_find(&li);
215 ledger_update(pid, ledgerp);
216 ledgerp->seen = 1;
217
218 for (proc = procs; proc; proc = proc->next)
219 if (proc->pid == pid)
220 break;
221 if (proc == NULL) {
222 proc = (struct proc_list *)malloc(sizeof (*proc));
223 if (proc == NULL) {
224 fprintf(stderr, "Out of memory\n");
225 exit (1);
226 }
227
228 if (proc_name(pid, proc->name, sizeof (proc->name)) == 0)
229 strlcpy(proc->name, "Error", sizeof (proc->name));
230
231 proc->pid = pid;
232 proc->ledger = ledgerp;
233 proc->next = procs;
234 procs = proc;
235 }
236 proc->seen = 1;
237 }
238
239 static int
240 pid_compare(const void *a, const void *b)
241 {
242 pid_t *pid_a = (pid_t *)a;
243 pid_t *pid_b = (pid_t *)b;
244
245 return (*pid_b - *pid_a);
246 }
247
248 static void
249 get_all_info(void)
250 {
251 pid_t *pids;
252 int sz, cnt, i;
253
254 if (pid < 0)
255 cnt = (int) get_kern_max_proc();
256 else
257 cnt = 1;
258
259 sz = cnt * sizeof(pid_t);
260 pids = (pid_t *)malloc(sz);
261 if (pids == NULL) {
262 perror("can't allocate memory for proc buffer\n");
263 exit (1);
264 }
265
266 if (pid < 0) {
267 cnt = proc_listallpids(pids, sz);
268 if (cnt < 0) {
269 perror("failed to get list of active pids");
270 exit (1);
271 }
272 qsort(pids, cnt, sizeof (pid_t), pid_compare);
273 } else {
274 pids[0] = pid;
275 }
276
277 for (i = 0; i < cnt; i++)
278 get_proc_info(pids[i]);
279 free(pids);
280 }
281
282 static void
283 print_num(int64_t num, int64_t delta)
284 {
285 char suf = '\0';
286 char posneg = ' ';
287 int numwidth;
288
289 if (diff_mode) {
290 num = delta;
291 }
292
293 if (num == LEDGER_LIMIT_INFINITY) {
294 printf("%10s ", "-");
295 return;
296 }
297
298 if (llabs(num) > 10000000000) {
299 num /= 1000000000;
300 suf = 'G';
301 } else if (llabs(num) > 10000000) {
302 num /= 1000000;
303 suf = 'M';
304 } else if (llabs(num) > 100000) {
305 num /= 1000;
306 suf = 'K';
307 }
308
309 posneg = (delta < 0) ? '-' : ((delta > 0) ? '+' : ' ');
310
311 numwidth = 10;
312
313 if (suf != '\0')
314 numwidth--;
315
316 printf("%*lld%c%c ", numwidth, num, suf, posneg);
317 }
318
319 static void
320 dump_all_info(void)
321 {
322 struct ledger_entry_info *info, *old;
323 struct proc_list *p;
324 int line, i;
325 int64_t d;
326
327 printf("\n%5s %32s %32s %10s %10s %10s %10s %10s \n", "PID", "COMMAND",
328 "RESOURCE", "CREDITS", "DEBITS", "BALANCE", "LIMIT", "PERIOD");
329
330 for (p = procs; p; p = p->next) {
331 if (p->seen == 0)
332 continue;
333
334 printf("%5d %32s ", p->pid, p->name);
335 line = 0;
336
337 info = p->ledger->info;
338 old = p->ledger->old_info;
339 for (i = 0; i < p->ledger->entries; i++) {
340 if (group_print &&
341 strcmp(group_print, template[i].lti_group))
342 continue;
343
344 if (resource_print &&
345 strcmp(resource_print, template[i].lti_name))
346 continue;
347
348 if (line++)
349 printf("%5s %32s ", "", "");
350 printf("%32s ", template[i].lti_name);
351
352 d = old ? info[i].lei_credit - old[i].lei_credit : 0;
353 print_num(info[i].lei_credit, d);
354
355 d = old ? info[i].lei_debit - old[i].lei_debit : 0;
356 print_num(info[i].lei_debit, d);
357
358 d = old ? info[i].lei_balance - old[i].lei_balance : 0;
359 print_num(info[i].lei_balance, d);
360
361 if (info[i].lei_limit == LEDGER_LIMIT_INFINITY) {
362 printf("%10s %10s", "none", "-");
363 } else {
364 print_num(info[i].lei_limit, 0);
365 print_num(info[i].lei_refill_period, 0);
366 }
367 printf("\n");
368 }
369 }
370
371 if (line == 0)
372 exit (0);
373 }
374
375 static void
376 cleanup(void)
377 {
378 struct proc_list *p, *pnext, *plast;
379 struct ledger *l, *lnext, *llast;
380
381 plast = NULL;
382 for (p = procs; p; p = pnext) {
383 pnext = p->next;
384 if (p->seen == 0) {
385 if (plast)
386 plast->next = pnext;
387 else
388 procs = pnext;
389
390 free(p);
391 } else {
392 p->seen = 0;
393 }
394 }
395
396 llast = NULL;
397 for (l = ledgers; l; l = lnext) {
398 lnext = l->next;
399 if (l->seen == 0) {
400 if (llast)
401 llast->next = lnext;
402 else
403 ledgers = lnext;
404 free(l->info);
405 if (l->old_info)
406 free(l->old_info);
407 free(l);
408 } else {
409 l->seen = 0;
410 free(l->old_info);
411 l->old_info = l->info;
412 l->info = NULL;
413 }
414 }
415
416 free(template);
417 template = NULL;
418 }
419
420 const char *pname;
421
422 static void
423 usage(void)
424 {
425 printf("%s [-hdL] [-g group] [-p pid] [-r resource] [interval]\n", pname);
426 }
427
428 int
429 main(int argc, char **argv)
430 {
431 int c;
432 int interval = 0;
433
434 pname = argv[0];
435
436 while ((c = getopt(argc, argv, "g:hdLp:r:")) != -1) {
437 switch (c) {
438 case 'g':
439 group_print = optarg;
440 break;
441
442 case 'd':
443 diff_mode = 1;
444 break;
445
446 case 'h':
447 usage();
448 exit(0);
449
450 case 'L':
451 get_template_info();
452 dump_template_info();
453 exit(0);
454
455 case 'p':
456 pid = atoi(optarg);
457 break;
458
459 case 'r':
460 resource_print = optarg;
461 break;
462
463 default:
464 usage();
465 exit(1);
466 }
467
468 }
469 argc -= optind;
470 argv += optind;
471
472 if (argc)
473 interval = atoi(argv[0]);
474
475 if (group_print && resource_print) {
476 fprintf(stderr, "Cannot specify both a resource and a group\n");
477 exit (1);
478 }
479
480 if (group_print)
481 validate_group();
482 if (resource_print)
483 validate_resource();
484
485 do {
486 get_template_info();
487 get_all_info();
488 dump_all_info();
489 cleanup();
490 sleep(interval);
491 } while (interval);
492 }