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 */
10
11 /*
12 * Top -- a program for finding the top cpu-eating threads
13 */
14 #include <signal.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <OS.h>
19 #include <termios.h>
20
21 #include <list>
22
23 #include "termcap.h"
24
25 static const char IDLE_NAME[] = "idle thread ";
26 static bigtime_t lastMeasure = 0;
27
28 /*
29 * Keeps track of a single thread's times
30 */
31 struct ThreadTime {
32 thread_id thid;
33 bigtime_t user_time;
34 bigtime_t kernel_time;
35
total_timeThreadTime36 bigtime_t total_time() const {
37 return user_time + kernel_time;
38 }
39
operator <ThreadTime40 bool operator< (const ThreadTime& other) const {
41 return total_time() > other.total_time();
42 }
43 };
44
45 /*
46 * Keeps track of all the threads' times
47 */
48 typedef std::list<ThreadTime> ThreadTimeList;
49
50
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 */
61
62 /* SIGWINCH handler */
63 static void
winch_handler(int notused)64 winch_handler(int notused)
65 {
66 (void) notused;
67 screen_size_changed = 1;
68 }
69
70
71 /* SIGINT handler */
72 static void
sigint_handler(int)73 sigint_handler(int)
74 {
75 tputs(exit_ca_mode, 1, putchar);
76 tputs(restore_cursor, 1, putchar);
77 exit(1);
78 }
79
80
81 static void
init_term()82 init_term()
83 {
84 static char buf[2048];
85 char *entries = &buf[0];
86
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);
94
95 tputs(save_cursor, 1, putchar);
96 tputs(enter_ca_mode, 1, putchar);
97 tputs(clear_string, 1, putchar);
98 }
99
100
101 /*
102 * Calculate the cpu percentage used by a given thread
103 * Remember: for multiple CPUs, multiply the interval by # cpus
104 * when calculating
105 */
106 static float
cpu_perc(bigtime_t spent,bigtime_t interval)107 cpu_perc(bigtime_t spent, bigtime_t interval)
108 {
109 return ((100.0 * spent) / (cpus * interval));
110 }
111
112
113 /*
114 * Compare an old snapshot with the new one
115 */
116 static void
compare(ThreadTimeList * old,ThreadTimeList * newList,bigtime_t uinterval,int refresh)117 compare(
118 ThreadTimeList *old,
119 ThreadTimeList *newList,
120 bigtime_t uinterval,
121 int refresh
122 )
123 {
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;
139
140 /*
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.
144 */
145 gtotal = 0;
146 utotal = 0;
147 ktotal = 0;
148 ThreadTimeList::iterator it;
149 ThreadTimeList::iterator itOld;
150 ThreadTime entry;
151
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;
158 }
159 newthread = 0;
160 oldtime = itOld->total_time();
161 newtime = it->total_time();
162 if (oldtime == newtime) {
163 ignore = 1;
164 break;
165 }
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);
169 }
170 if (newthread) {
171 entry.thid = it->thid;
172 entry.user_time = it->user_time;
173 entry.kernel_time = it->kernel_time;
174 }
175 if (!ignore) {
176 times.push_back(entry);
177
178 total = entry.total_time();
179 gtotal += total;
180 utotal += entry.user_time;
181 ktotal += entry.kernel_time;
182 }
183 }
184
185 /*
186 * Sort what we found and print
187 */
188 times.sort();
189
190 printf("%6s %7s %7s %7s %4s %16s %-16s \n", "THID", "TOTAL", "USER",
191 "KERNEL", "%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++;
205 }
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 strlcpy(tm.args, p + 1, sizeof(tm.args));
211 }
212 }
213 }
214
215 tm.args[16] = 0;
216
217 if (columns <= 80)
218 t.name[16] = 0;
219 else if (columns - 64 < sizeof(t.name))
220 t.name[columns - 64] = 0;
221
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;
229 }
230 if (!ignore && (!refresh || (linecount < (rows - 1)))) {
231
232 printf("%6" B_PRId32 " %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++;
241 }
242 }
243
244 printf("------ %7.2f %7.2f %7.2f %4.1f%% "
245 "TOTAL (%4.1f%% idle time, %4.1f%% unknown)",
246 (double) (gtotal / 1000),
247 (double) (utotal / 1000),
248 (double) (ktotal / 1000),
249 cpu_perc(gtotal, uinterval),
250 cpu_perc(idletime, uinterval),
251 cpu_perc(cpus * uinterval - (gtotal + idletime), uinterval));
252 fflush(stdout);
253 tputs(clear_string, 1, putchar);
254 tputs(cursor_home, 1, putchar);
255 }
256
257
258 static int
adjust_term(bool onlyRows)259 adjust_term(bool onlyRows)
260 {
261 struct winsize ws;
262
263 if (ioctl(1, TIOCGWINSZ, &ws) < 0) {
264 return (0);
265 }
266 if (ws.ws_row <= 0) {
267 return (0);
268 }
269 columns = ws.ws_col;
270 rows = ws.ws_row;
271 if (onlyRows)
272 return 1;
273
274 return (1);
275 }
276
277
278 /*
279 * Gather up thread data since previous run
280 */
281 static ThreadTimeList
gather(ThreadTimeList * old,int refresh)282 gather(ThreadTimeList *old, int refresh)
283 {
284 int32 tmcookie;
285 int32 thcookie;
286 thread_info t;
287 team_info tm;
288 ThreadTimeList times;
289 bigtime_t oldLastMeasure;
290
291 tmcookie = 0;
292 oldLastMeasure = lastMeasure;
293 lastMeasure = system_time();
294
295 while (get_next_team_info(&tmcookie, &tm) == B_NO_ERROR) {
296 thcookie = 0;
297 while (get_next_thread_info(tm.team, &thcookie, &t) == B_NO_ERROR) {
298 ThreadTime entry;
299 entry.thid = t.thread;
300 entry.user_time = t.user_time;
301 entry.kernel_time = t.kernel_time;
302 times.push_back(entry);
303 }
304 }
305 if (old != NULL) {
306 if (screen_size_changed) {
307 adjust_term(true);
308 screen_size_changed = 0;
309 }
310 compare(old, ×, system_time() - oldLastMeasure, refresh);
311 old->clear();
312 }
313 return (times);
314 }
315
316
317 /*
318 * print usage message and exit
319 */
320 static void
usage(const char * myname)321 usage(const char *myname)
322 {
323 fprintf(stderr, "usage: %s [-d] [-i interval] [-n ntimes]\n", myname);
324 fprintf(stderr,
325 " -d, do not clear the screen between displays\n");
326 fprintf(stderr,
327 " -i interval, wait `interval' seconds before displaying\n");
328 fprintf(stderr,
329 " -n ntimes, display `ntimes' times before exiting\n");
330 fprintf(stderr, "Default is clear screen, interval=5, ntimes=infinite\n");
331 exit(1);
332 }
333
334
335 int
main(int argc,char ** argv)336 main(int argc, char **argv)
337 {
338 ThreadTimeList baseline;
339 int i;
340 int iters = -1;
341 int interval = 5;
342 int refresh = 1;
343 system_info sysinfo;
344 bigtime_t uinterval;
345 bigtime_t elapsed;
346 char *myname;
347
348 get_system_info (&sysinfo);
349 cpus = sysinfo.cpu_count;
350
351 myname = argv[0];
352 for (argc--, argv++; argc > 0 && **argv == '-'; argc--, argv++) {
353 if (strcmp(argv[0], "-i") == 0) {
354 argc--, argv++;
355 if (argc == 0) {
356 usage(myname);
357 }
358 interval = atoi(argv[0]);
359 } else if (strcmp(argv[0], "-n") == 0) {
360 argc--, argv++;
361 if (argc == 0) {
362 usage(myname);
363 }
364 iters = atoi(argv[0]);
365 } else if (strcmp(argv[0], "-d") == 0) {
366 refresh = 0;
367 } else {
368 usage(myname);
369 }
370 }
371 if (argc) {
372 usage(myname);
373 }
374
375 init_term();
376
377 if (refresh) {
378 if (!adjust_term(false)) {
379 refresh = 0;
380 }
381 }
382 if (iters >= 0) {
383 printf("Starting: %d interval%s of %d second%s each\n", iters,
384 (iters == 1) ? "" : "s", interval,
385 (interval == 1) ? "" : "s");
386 }
387
388 signal(SIGWINCH, winch_handler);
389 signal(SIGINT, sigint_handler);
390
391 lastMeasure = system_time();
392 if (iters < 0) {
393 // You will only have to wait half a second for the first iteration.
394 uinterval = 1 * 1000000 / 2;
395 baseline = gather(NULL, refresh);
396 elapsed = system_time() - lastMeasure;
397 if (elapsed < uinterval)
398 snooze(uinterval - elapsed);
399 // then = system_time();
400 baseline = gather(&baseline, refresh);
401
402 } else
403 baseline = gather(NULL, refresh);
404
405 uinterval = interval * 1000000;
406 for (i = 0; iters < 0 || i < iters; i++) {
407 elapsed = system_time() - lastMeasure;
408 if (elapsed < uinterval)
409 snooze(uinterval - elapsed);
410 baseline = gather(&baseline, refresh);
411 }
412
413 tputs(exit_ca_mode, 1, putchar);
414 tputs(restore_cursor, 1, putchar);
415
416 exit(0);
417 }
418