Assorted whitespace cleanup and typo fixes.
[haiku.git] / src / bin / top.cpp
blobbc3b9637735f66c2b6284109c1a841dbc1d301ad
1 /*
2 * Copyright (C) 1996 Be, Inc.
3 * All Rights Reserved
4 *
5 * This source code was published by Be, Inc. in the file gnu_x86.tar.gz for R3,
6 * a mirror is at http://linux.inf.elte.hu/ftp/beos/development/gnu/r3.0/
7 * needs to link to termcap.
8 * The GPL should apply here, since AFAIK termcap is GPLed.
9 */
12 * Top -- a program for finding the top cpu-eating threads
14 #include <signal.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <OS.h>
19 #include <termios.h>
21 #include <list>
23 #include "termcap.h"
25 static const char IDLE_NAME[] = "idle thread ";
26 static bigtime_t lastMeasure = 0;
29 * Keeps track of a single thread's times
31 struct ThreadTime {
32 thread_id thid;
33 bigtime_t user_time;
34 bigtime_t kernel_time;
36 bigtime_t total_time() const {
37 return user_time + kernel_time;
40 bool operator< (const ThreadTime& other) const {
41 return total_time() > other.total_time();
46 * Keeps track of all the threads' times
48 typedef std::list<ThreadTime> ThreadTimeList;
51 static char *clear_string; /*output string for clearing the screen */
52 static char *enter_ca_mode; /* output string for switching screen buffer */
53 static char *exit_ca_mode; /* output string for releasing screen buffer */
54 static char *cursor_home; /* Places cursor back to (1,1) */
55 static char *restore_cursor;
56 static char *save_cursor;
57 static int columns; /* Columns on screen */
58 static int rows; /* how many rows on the screen */
59 static int screen_size_changed = 0; /* tells to refresh the screen size */
60 static int cpus; /* how many cpus we are runing on */
62 /* SIGWINCH handler */
63 static void
64 winch_handler(int notused)
66 (void) notused;
67 screen_size_changed = 1;
71 /* SIGINT handler */
72 static void
73 sigint_handler(int)
75 printf(exit_ca_mode);
76 printf(restore_cursor);
77 exit(1);
81 static void
82 init_term()
84 static char buf[2048];
85 char *entries = &buf[0];
87 tgetent(buf, getenv("TERM"));
88 exit_ca_mode = tgetstr("te", &entries);
89 enter_ca_mode = tgetstr("ti", &entries);
90 cursor_home = tgetstr("ho", &entries);
91 clear_string = tgetstr("cl", &entries);
92 restore_cursor = tgetstr("rc", &entries);
93 save_cursor = tgetstr("sc", &entries);
95 printf(save_cursor);
96 printf(enter_ca_mode);
97 printf(clear_string);
102 * Calculate the cpu percentage used by a given thread
103 * Remember: for multiple CPUs, multiply the interval by # cpus
104 * when calculating
106 static float
107 cpu_perc(bigtime_t spent, bigtime_t interval)
109 return ((100.0 * spent) / (cpus * interval));
114 * Compare an old snapshot with the new one
116 static void
117 compare(
118 ThreadTimeList *old,
119 ThreadTimeList *newList,
120 bigtime_t uinterval,
121 int refresh
124 bigtime_t oldtime;
125 bigtime_t newtime;
126 ThreadTimeList times;
127 thread_info t;
128 team_info tm;
129 bigtime_t total;
130 bigtime_t utotal;
131 bigtime_t ktotal;
132 bigtime_t gtotal;
133 bigtime_t idletime;
134 int newthread;
135 int ignore;
136 int linecount;
137 //system_info info;
138 char *p;
141 * First, merge both old and new lists, computing the differences in time
142 * Threads in only one list are dropped.
143 * Threads with no elapsed time are dropped too.
145 gtotal = 0;
146 utotal = 0;
147 ktotal = 0;
148 ThreadTimeList::iterator it;
149 ThreadTimeList::iterator itOld;
150 ThreadTime entry;
152 for (it = newList->begin(); it != newList->end(); it++) {
153 newthread = 1;
154 ignore = 0;
155 for (itOld = old->begin(); itOld != old->end(); itOld++) {
156 if (itOld->thid != it->thid) {
157 continue;
159 newthread = 0;
160 oldtime = itOld->total_time();
161 newtime = it->total_time();
162 if (oldtime == newtime) {
163 ignore = 1;
164 break;
166 entry.thid = it->thid;
167 entry.user_time = (it->user_time - itOld->user_time);
168 entry.kernel_time = (it->kernel_time - itOld->kernel_time);
170 if (newthread) {
171 entry.thid = it->thid;
172 entry.user_time = it->user_time;
173 entry.kernel_time = it->kernel_time;
175 if (!ignore) {
176 times.push_back(entry);
178 total = entry.total_time();
179 gtotal += total;
180 utotal += entry.user_time;
181 ktotal += entry.kernel_time;
186 * Sort what we found and print
188 times.sort();
190 printf("%6s %7s %7s %7s %4s %16s %-16s \n", "THID", "TOTAL", "USER", "KERNEL",
191 "%CPU", "TEAM NAME", "THREAD NAME");
192 linecount = 1;
193 idletime = 0;
194 gtotal = 0;
195 ktotal = 0;
196 utotal = 0;
197 for (it = times.begin(); it != times.end(); it++) {
198 ignore = 0;
199 if (get_thread_info(it->thid, &t) < B_NO_ERROR) {
200 strcpy(t.name, "(unknown)");
201 strcpy(tm.args, "(unknown)");
202 } else {
203 if (strncmp(t.name, IDLE_NAME, strlen(IDLE_NAME)) == 0) {
204 ignore++;
206 if (get_team_info(t.team, &tm) < B_NO_ERROR) {
207 strcpy(tm.args, "(unknown)");
208 } else {
209 if ((p = strrchr(tm.args, '/'))) {
210 strcpy(tm.args, p + 1);
215 tm.args[16] = 0;
217 if (columns <= 80)
218 t.name[16] = 0;
219 else if (columns - 64 < sizeof(t.name))
220 t.name[columns - 64] = 0;
222 total = it->total_time();
223 if (ignore) {
224 idletime += total;
225 } else {
226 gtotal += total;
227 ktotal += it->kernel_time;
228 utotal += it->user_time;
230 if (!ignore && (!refresh || (linecount < (rows - 1)))) {
232 printf("%6ld %7.2f %7.2f %7.2f %4.1f %16s %s \n",
233 it->thid,
234 total / 1000.0,
235 (double)(it->user_time / 1000),
236 (double)(it->kernel_time / 1000),
237 cpu_perc(total, uinterval),
238 tm.args,
239 t.name);
240 linecount++;
244 printf("------ %7.2f %7.2f %7.2f %4.1f%% TOTAL (%4.1f%% idle time, %4.1f%% unknown)",
245 (double) (gtotal / 1000),
246 (double) (utotal / 1000),
247 (double) (ktotal / 1000),
248 cpu_perc(gtotal, uinterval),
249 cpu_perc(idletime, uinterval),
250 cpu_perc(cpus * uinterval - (gtotal + idletime), uinterval));
251 fflush(stdout);
252 printf(clear_string);
253 printf(cursor_home);
257 static int
258 adjust_term(bool onlyRows)
260 struct winsize ws;
262 if (ioctl(1, TIOCGWINSZ, &ws) < 0) {
263 return (0);
265 if (ws.ws_row <= 0) {
266 return (0);
268 columns = ws.ws_col;
269 rows = ws.ws_row;
270 if (onlyRows)
271 return 1;
273 return (1);
278 * Gather up thread data since previous run
280 static ThreadTimeList
281 gather(ThreadTimeList *old, int refresh)
283 int32 tmcookie;
284 int32 thcookie;
285 thread_info t;
286 team_info tm;
287 ThreadTimeList times;
288 bigtime_t oldLastMeasure;
290 tmcookie = 0;
291 oldLastMeasure = lastMeasure;
292 lastMeasure = system_time();
294 while (get_next_team_info(&tmcookie, &tm) == B_NO_ERROR) {
295 thcookie = 0;
296 while (get_next_thread_info(tm.team, &thcookie, &t) == B_NO_ERROR) {
297 ThreadTime entry;
298 entry.thid = t.thread;
299 entry.user_time = t.user_time;
300 entry.kernel_time = t.kernel_time;
301 times.push_back(entry);
304 if (old != NULL) {
305 if (screen_size_changed) {
306 adjust_term(true);
307 screen_size_changed = 0;
309 compare(old, &times, system_time() - oldLastMeasure, refresh);
310 old->clear();
312 return (times);
317 * print usage message and exit
319 static void
320 usage(const char *myname)
322 fprintf(stderr, "usage: %s [-d] [-i interval] [-n ntimes]\n", myname);
323 fprintf(stderr,
324 " -d, do not clear the screen between displays\n");
325 fprintf(stderr,
326 " -i interval, wait `interval' seconds before displaying\n");
327 fprintf(stderr,
328 " -n ntimes, display `ntimes' times before exiting\n");
329 fprintf(stderr, "Default is clear screen, interval=5, ntimes=infinite\n");
330 exit(1);
335 main(int argc, char **argv)
337 ThreadTimeList baseline;
338 int i;
339 int iters = -1;
340 int interval = 5;
341 int refresh = 1;
342 system_info sysinfo;
343 bigtime_t uinterval;
344 bigtime_t elapsed;
345 char *myname;
347 get_system_info (&sysinfo);
348 cpus = sysinfo.cpu_count;
350 myname = argv[0];
351 for (argc--, argv++; argc > 0 && **argv == '-'; argc--, argv++) {
352 if (strcmp(argv[0], "-i") == 0) {
353 argc--, argv++;
354 if (argc == 0) {
355 usage(myname);
357 interval = atoi(argv[0]);
358 } else if (strcmp(argv[0], "-n") == 0) {
359 argc--, argv++;
360 if (argc == 0) {
361 usage(myname);
363 iters = atoi(argv[0]);
364 } else if (strcmp(argv[0], "-d") == 0) {
365 refresh = 0;
366 } else {
367 usage(myname);
370 if (argc) {
371 usage(myname);
374 init_term();
376 if (refresh) {
377 if (!adjust_term(false)) {
378 refresh = 0;
381 if (iters >= 0) {
382 printf("Starting: %d interval%s of %d second%s each\n", iters,
383 (iters == 1) ? "" : "s", interval,
384 (interval == 1) ? "" : "s");
387 signal(SIGWINCH, winch_handler);
388 signal(SIGINT, sigint_handler);
390 lastMeasure = system_time();
391 if (iters < 0) {
392 // You will only have to wait half a second for the first iteration.
393 uinterval = 1 * 1000000 / 2;
394 baseline = gather(NULL, refresh);
395 elapsed = system_time() - lastMeasure;
396 if (elapsed < uinterval)
397 snooze(uinterval - elapsed);
398 // then = system_time();
399 baseline = gather(&baseline, refresh);
401 } else
402 baseline = gather(NULL, refresh);
404 uinterval = interval * 1000000;
405 for (i = 0; iters < 0 || i < iters; i++) {
406 elapsed = system_time() - lastMeasure;
407 if (elapsed < uinterval)
408 snooze(uinterval - elapsed);
409 baseline = gather(&baseline, refresh);
412 printf(exit_ca_mode);
413 printf(restore_cursor);
415 exit(0);