]> git.saurik.com Git - apple/system_cmds.git/blob - ltop.tproj/ltop.c
system_cmds-880.100.5.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 const char *suf = "";
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 numwidth -= strlen(suf);
314
315 printf("%*lld%s%c ", numwidth, num, suf, posneg);
316 }
317
318 static void
319 dump_all_info(void)
320 {
321 struct ledger_entry_info *info, *old;
322 struct proc_list *p;
323 int line, i;
324 int64_t d;
325
326 printf("\n%5s %32s %32s %10s %10s %10s %10s %10s \n", "PID", "COMMAND",
327 "RESOURCE", "CREDITS", "DEBITS", "BALANCE", "LIMIT", "PERIOD");
328
329 for (p = procs; p; p = p->next) {
330 if (p->seen == 0)
331 continue;
332
333 printf("%5d %32s ", p->pid, p->name);
334 line = 0;
335
336 info = p->ledger->info;
337 old = p->ledger->old_info;
338 for (i = 0; i < p->ledger->entries; i++) {
339 if (group_print &&
340 strcmp(group_print, template[i].lti_group))
341 continue;
342
343 if (resource_print &&
344 strcmp(resource_print, template[i].lti_name))
345 continue;
346
347 if (line++)
348 printf("%5s %32s ", "", "");
349 printf("%32s ", template[i].lti_name);
350
351 d = old ? info[i].lei_credit - old[i].lei_credit : 0;
352 print_num(info[i].lei_credit, d);
353
354 d = old ? info[i].lei_debit - old[i].lei_debit : 0;
355 print_num(info[i].lei_debit, d);
356
357 d = old ? info[i].lei_balance - old[i].lei_balance : 0;
358 print_num(info[i].lei_balance, d);
359
360 if (info[i].lei_limit == LEDGER_LIMIT_INFINITY) {
361 printf("%10s %10s", "none", "-");
362 } else {
363 print_num(info[i].lei_limit, 0);
364 print_num(info[i].lei_refill_period, 0);
365 }
366 printf("\n");
367 }
368 }
369
370 if (line == 0)
371 exit (0);
372 }
373
374 static void
375 cleanup(void)
376 {
377 struct proc_list *p, *pnext, *plast;
378 struct ledger *l, *lnext, *llast;
379
380 plast = NULL;
381 for (p = procs; p; p = pnext) {
382 pnext = p->next;
383 if (p->seen == 0) {
384 if (plast)
385 plast->next = pnext;
386 else
387 procs = pnext;
388
389 free(p);
390 } else {
391 p->seen = 0;
392 }
393 }
394
395 llast = NULL;
396 for (l = ledgers; l; l = lnext) {
397 lnext = l->next;
398 if (l->seen == 0) {
399 if (llast)
400 llast->next = lnext;
401 else
402 ledgers = lnext;
403 free(l->info);
404 if (l->old_info)
405 free(l->old_info);
406 free(l);
407 } else {
408 l->seen = 0;
409 free(l->old_info);
410 l->old_info = l->info;
411 l->info = NULL;
412 }
413 }
414
415 free(template);
416 template = NULL;
417 }
418
419 const char *pname;
420
421 static void
422 usage(void)
423 {
424 printf("%s [-hdL] [-g group] [-p pid] [-r resource] [interval]\n", pname);
425 }
426
427 int
428 main(int argc, char **argv)
429 {
430 int c;
431 int interval = 0;
432
433 pname = argv[0];
434
435 while ((c = getopt(argc, argv, "g:hdLp:r:")) != -1) {
436 switch (c) {
437 case 'g':
438 group_print = optarg;
439 break;
440
441 case 'd':
442 diff_mode = 1;
443 break;
444
445 case 'h':
446 usage();
447 exit(0);
448
449 case 'L':
450 get_template_info();
451 dump_template_info();
452 exit(0);
453
454 case 'p':
455 pid = atoi(optarg);
456 break;
457
458 case 'r':
459 resource_print = optarg;
460 break;
461
462 default:
463 usage();
464 exit(1);
465 }
466
467 }
468 argc -= optind;
469 argv += optind;
470
471 if (argc)
472 interval = atoi(argv[0]);
473
474 if (group_print && resource_print) {
475 fprintf(stderr, "Cannot specify both a resource and a group\n");
476 exit (1);
477 }
478
479 if (group_print)
480 validate_group();
481 if (resource_print)
482 validate_resource();
483
484 do {
485 get_template_info();
486 get_all_info();
487 dump_all_info();
488 cleanup();
489 sleep(interval);
490 } while (interval);
491 }