You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

704 lines
14 KiB

преди 3 години
преди 3 години
преди 3 години
преди 3 години
преди 3 години
преди 3 години
преди 3 години
преди 3 години
преди 3 години
преди 3 години
преди 3 години
преди 3 години
преди 3 години
преди 3 години
преди 3 години
преди 3 години
преди 3 години
преди 3 години
преди 3 години
преди 3 години
преди 3 години
преди 3 години
преди 3 години
преди 3 години
преди 3 години
преди 3 години
преди 3 години
преди 3 години
преди 3 години
преди 3 години
преди 3 години
преди 3 години
преди 3 години
преди 3 години
преди 3 години
преди 3 години
преди 3 години
преди 3 години
преди 3 години
преди 3 години
преди 3 години
преди 3 години
преди 3 години
преди 3 години
преди 3 години
преди 3 години
преди 3 години
преди 3 години
преди 3 години
  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <pwd.h>
  4. #include <curses.h>
  5. #include <timers.h>
  6. #include <stdlib.h>
  7. #include <limits.h>
  8. #include <termcap.h>
  9. #include <termios.h>
  10. #include <time.h>
  11. #include <string.h>
  12. #include <signal.h>
  13. #include <fcntl.h>
  14. #include <errno.h>
  15. #include <dirent.h>
  16. #include <assert.h>
  17. #include <sys/ioc_tty.h>
  18. #include <sys/times.h>
  19. #include <sys/types.h>
  20. #include <sys/time.h>
  21. #include <sys/select.h>
  22. #include <minix/com.h>
  23. #include <minix/config.h>
  24. #include <minix/type.h>
  25. #include <minix/endpoint.h>
  26. #include <minix/const.h>
  27. #include <minix/u64.h>
  28. #include <paths.h>
  29. #include <minix/procfs.h>
  30. #define TIMECYCLEKEY 't'
  31. #define ORDERKEY 'o'
  32. #define ORDER_CPU 0
  33. #define ORDER_MEMORY 1
  34. #define ORDER_HIGHEST ORDER_MEMORY
  35. int order = ORDER_CPU;
  36. u32_t system_hz;
  37. /* name of cpu cycle types, in the order they appear in /psinfo. */
  38. const char *cputimenames[] = { "user", "ipc", "kernelcall" };
  39. #define CPUTIMENAMES (sizeof(cputimenames)/sizeof(cputimenames[0]))
  40. #define CPUTIME(m, i) (m & (1L << (i)))
  41. unsigned int nr_procs, nr_tasks;
  42. int nr_total;
  43. #define SLOT_NR(e) (_ENDPOINT_P(e) + nr_tasks)
  44. #define TC_BUFFER 1024 /* Size of termcap(3) buffer */
  45. #define TC_STRINGS 200 /* Enough room for cm,cl,so,se */
  46. char *Tclr_all;
  47. int blockedverbose = 0;
  48. #define USED 0x1
  49. #define IS_TASK 0x2
  50. #define IS_SYSTEM 0x4
  51. #define BLOCKED 0x8
  52. struct proc {
  53. int p_flags;
  54. endpoint_t p_endpoint;
  55. pid_t p_pid;
  56. u64_t p_cpucycles[CPUTIMENAMES];
  57. int p_priority;
  58. endpoint_t p_blocked;
  59. time_t p_user_time;
  60. vir_bytes p_memory;
  61. uid_t p_effuid;
  62. int p_nice;
  63. char p_name[PROC_NAME_LEN+1];
  64. };
  65. struct proc *proc = NULL, *prev_proc = NULL;
  66. void parse_file(pid_t pid)
  67. {
  68. char path[PATH_MAX], name[256], type, state;
  69. int version, endpt, effuid;
  70. unsigned long cycles_hi, cycles_lo;
  71. FILE *fp;
  72. struct proc *p;
  73. int slot;
  74. int i;
  75. sprintf(path, "%d/psinfo", pid);
  76. if ((fp = fopen(path, "r")) == NULL)
  77. return;
  78. if (fscanf(fp, "%d", &version) != 1) {
  79. fclose(fp);
  80. return;
  81. }
  82. if (version != PSINFO_VERSION) {
  83. fputs("procfs version mismatch!\n", stderr);
  84. exit(1);
  85. }
  86. if (fscanf(fp, " %c %d", &type, &endpt) != 2) {
  87. fclose(fp);
  88. return;
  89. }
  90. slot = SLOT_NR(endpt);
  91. if(slot < 0 || slot >= nr_total) {
  92. fprintf(stderr, "top: unreasonable endpoint number %d\n", endpt);
  93. fclose(fp);
  94. return;
  95. }
  96. p = &proc[slot];
  97. if (type == TYPE_TASK)
  98. p->p_flags |= IS_TASK;
  99. else if (type == TYPE_SYSTEM)
  100. p->p_flags |= IS_SYSTEM;
  101. p->p_endpoint = endpt;
  102. p->p_pid = pid;
  103. if (fscanf(fp, " %255s %c %d %d %lu %*u %lu %lu",
  104. name, &state, &p->p_blocked, &p->p_priority,
  105. &p->p_user_time, &cycles_hi, &cycles_lo) != 7) {
  106. fclose(fp);
  107. return;
  108. }
  109. strncpy(p->p_name, name, sizeof(p->p_name)-1);
  110. p->p_name[sizeof(p->p_name)-1] = 0;
  111. if (state != STATE_RUN)
  112. p->p_flags |= BLOCKED;
  113. p->p_cpucycles[0] = make64(cycles_lo, cycles_hi);
  114. p->p_memory = 0L;
  115. if (!(p->p_flags & IS_TASK)) {
  116. int j;
  117. if ((j=fscanf(fp, " %lu %*u %*u %*c %*d %*u %u %*u %d %*c %*d %*u",
  118. &p->p_memory, &effuid, &p->p_nice)) != 3) {
  119. fclose(fp);
  120. return;
  121. }
  122. p->p_effuid = effuid;
  123. } else p->p_effuid = 0;
  124. for(i = 1; i < CPUTIMENAMES; i++) {
  125. if(fscanf(fp, " %lu %lu",
  126. &cycles_hi, &cycles_lo) == 2) {
  127. p->p_cpucycles[i] = make64(cycles_lo, cycles_hi);
  128. } else {
  129. p->p_cpucycles[i] = 0;
  130. }
  131. }
  132. if ((p->p_flags & IS_TASK)) {
  133. if(fscanf(fp, " %lu", &p->p_memory) != 1) {
  134. p->p_memory = 0;
  135. }
  136. }
  137. p->p_flags |= USED;
  138. fclose(fp);
  139. }
  140. void parse_dir(void)
  141. {
  142. DIR *p_dir;
  143. struct dirent *p_ent;
  144. pid_t pid;
  145. char *end;
  146. if ((p_dir = opendir(".")) == NULL) {
  147. perror("opendir on " _PATH_PROC);
  148. exit(1);
  149. }
  150. for (p_ent = readdir(p_dir); p_ent != NULL; p_ent = readdir(p_dir)) {
  151. pid = strtol(p_ent->d_name, &end, 10);
  152. if (!end[0] && pid != 0)
  153. parse_file(pid);
  154. }
  155. closedir(p_dir);
  156. }
  157. void get_procs(void)
  158. {
  159. struct proc *p;
  160. int i;
  161. p = prev_proc;
  162. prev_proc = proc;
  163. proc = p;
  164. if (proc == NULL) {
  165. proc = malloc(nr_total * sizeof(proc[0]));
  166. if (proc == NULL) {
  167. fprintf(stderr, "Out of memory!\n");
  168. exit(1);
  169. }
  170. }
  171. for (i = 0; i < nr_total; i++)
  172. proc[i].p_flags = 0;
  173. parse_dir();
  174. }
  175. int print_memory(void)
  176. {
  177. FILE *fp;
  178. unsigned int pagesize;
  179. unsigned long total, free, largest, cached;
  180. if ((fp = fopen("meminfo", "r")) == NULL)
  181. return 0;
  182. if (fscanf(fp, "%u %lu %lu %lu %lu", &pagesize, &total, &free,
  183. &largest, &cached) != 5) {
  184. fclose(fp);
  185. return 0;
  186. }
  187. fclose(fp);
  188. printf("main memory: %ldK total, %ldK free, %ldK contig free, "
  189. "%ldK cached\n",
  190. (pagesize * total)/1024, (pagesize * free)/1024,
  191. (pagesize * largest)/1024, (pagesize * cached)/1024);
  192. return 1;
  193. }
  194. int print_load(double *loads, int nloads)
  195. {
  196. int i;
  197. printf("load averages: ");
  198. for(i = 0; i < nloads; i++)
  199. printf("%s %.2f", (i > 0) ? "," : "", loads[i]);
  200. printf("\n");
  201. return 1;
  202. }
  203. int print_proc_summary(struct proc *proc)
  204. {
  205. int p, alive, running, sleeping;
  206. alive = running = sleeping = 0;
  207. for(p = 0; p < nr_total; p++) {
  208. if (proc[p].p_endpoint == IDLE)
  209. continue;
  210. if(!(proc[p].p_flags & USED))
  211. continue;
  212. alive++;
  213. if(proc[p].p_flags & BLOCKED)
  214. sleeping++;
  215. else
  216. running++;
  217. }
  218. printf("%d processes: %d running, %d sleeping\n",
  219. alive, running, sleeping);
  220. return 1;
  221. }
  222. struct tp {
  223. struct proc *p;
  224. u64_t ticks;
  225. };
  226. int cmp_procs(const void *v1, const void *v2)
  227. {
  228. struct tp *p1 = (struct tp *) v1, *p2 = (struct tp *) v2;
  229. int p1blocked, p2blocked;
  230. if(order == ORDER_MEMORY) {
  231. if(p1->p->p_memory < p2->p->p_memory) return 1;
  232. if(p1->p->p_memory > p2->p->p_memory) return -1;
  233. return 0;
  234. }
  235. p1blocked = !!(p1->p->p_flags & BLOCKED);
  236. p2blocked = !!(p2->p->p_flags & BLOCKED);
  237. /* Primarily order by used number of cpu cycles.
  238. *
  239. * Exception: if in blockedverbose mode, a blocked
  240. * process is always printed after an unblocked
  241. * process, and used cpu cycles don't matter.
  242. *
  243. * In both cases, process slot number is a tie breaker.
  244. */
  245. if(blockedverbose && (p1blocked || p2blocked)) {
  246. if(!p1blocked && p2blocked)
  247. return -1;
  248. if(!p2blocked && p1blocked)
  249. return 1;
  250. } else if(p1->ticks != p2->ticks) {
  251. return (p2->ticks - p1->ticks);
  252. }
  253. /* Process slot number is a tie breaker. */
  254. return (int) (p1->p - p2->p);
  255. }
  256. struct tp *lookup(endpoint_t who, struct tp *tptab, int np)
  257. {
  258. int t;
  259. for(t = 0; t < np; t++)
  260. if(who == tptab[t].p->p_endpoint)
  261. return &tptab[t];
  262. fprintf(stderr, "lookup: tp %d (0x%x) not found.\n", who, who);
  263. abort();
  264. return NULL;
  265. }
  266. double ktotal = 0;
  267. void print_proc(struct tp *tp, u64_t total_ticks)
  268. {
  269. int euid = 0;
  270. static struct passwd *who = NULL;
  271. static int last_who = -1;
  272. char *name = "";
  273. int ticks;
  274. struct proc *pr = tp->p;
  275. printf("%5d ", pr->p_pid);
  276. euid = pr->p_effuid;
  277. name = pr->p_name;
  278. if(last_who != euid || !who) {
  279. who = getpwuid(euid);
  280. last_who = euid;
  281. }
  282. if(who && who->pw_name) printf("%-8s ", who->pw_name);
  283. else if(!(pr->p_flags & IS_TASK)) printf("%8d ", pr->p_effuid);
  284. else printf(" ");
  285. printf(" %2d ", pr->p_priority);
  286. if(!(pr->p_flags & IS_TASK)) {
  287. printf(" %3d ", pr->p_nice);
  288. } else printf(" ");
  289. printf("%6ldK", (pr->p_memory + 512) / 1024);
  290. printf("%6s", (pr->p_flags & BLOCKED) ? "" : "RUN");
  291. ticks = pr->p_user_time;
  292. printf(" %3u:%02u ", (ticks/system_hz/60), (ticks/system_hz)%60);
  293. printf("%6.2f%% %s", 100.0 * tp->ticks / total_ticks, name);
  294. }
  295. char *cputimemodename(int cputimemode)
  296. {
  297. static char name[100];
  298. int i;
  299. name[0] = '\0';
  300. for(i = 0; i < CPUTIMENAMES; i++) {
  301. if(CPUTIME(cputimemode, i)) {
  302. assert(strlen(name) +
  303. strlen(cputimenames[i]) < sizeof(name));
  304. strcat(name, cputimenames[i]);
  305. strcat(name, " ");
  306. }
  307. }
  308. return name;
  309. }
  310. u64_t cputicks(struct proc *p1, struct proc *p2, int timemode)
  311. {
  312. int i;
  313. u64_t t = 0;
  314. for(i = 0; i < CPUTIMENAMES; i++) {
  315. if(!CPUTIME(timemode, i))
  316. continue;
  317. if(p1->p_endpoint == p2->p_endpoint) {
  318. t = t + p2->p_cpucycles[i] - p1->p_cpucycles[i];
  319. } else {
  320. t = t + p2->p_cpucycles[i];
  321. }
  322. }
  323. return t;
  324. }
  325. char *ordername(int orderno)
  326. {
  327. switch(orderno) {
  328. case ORDER_CPU: return "cpu";
  329. case ORDER_MEMORY: return "memory";
  330. }
  331. return "invalid order";
  332. }
  333. void print_procs(int maxlines,
  334. struct proc *proc1, struct proc *proc2, int cputimemode)
  335. {
  336. int p, nprocs;
  337. u64_t idleticks = 0;
  338. u64_t kernelticks = 0;
  339. u64_t systemticks = 0;
  340. u64_t userticks = 0;
  341. u64_t total_ticks = 0;
  342. int blockedseen = 0;
  343. static struct tp *tick_procs = NULL;
  344. if (tick_procs == NULL) {
  345. tick_procs = malloc(nr_total * sizeof(tick_procs[0]));
  346. if (tick_procs == NULL) {
  347. fprintf(stderr, "Out of memory!\n");
  348. exit(1);
  349. }
  350. }
  351. for(p = nprocs = 0; p < nr_total; p++) {
  352. u64_t uticks;
  353. if(!(proc2[p].p_flags & USED))
  354. continue;
  355. tick_procs[nprocs].p = proc2 + p;
  356. tick_procs[nprocs].ticks = cputicks(&proc1[p], &proc2[p], cputimemode);
  357. uticks = cputicks(&proc1[p], &proc2[p], 1);
  358. total_ticks = total_ticks + uticks;
  359. if(p-NR_TASKS == IDLE) {
  360. idleticks = uticks;
  361. continue;
  362. }
  363. if(p-NR_TASKS == KERNEL) {
  364. kernelticks = uticks;
  365. }
  366. if(!(proc2[p].p_flags & IS_TASK)) {
  367. if(proc2[p].p_flags & IS_SYSTEM)
  368. systemticks = systemticks + tick_procs[nprocs].ticks;
  369. else
  370. userticks = userticks + tick_procs[nprocs].ticks;
  371. }
  372. nprocs++;
  373. }
  374. if (total_ticks == 0)
  375. return;
  376. qsort(tick_procs, nprocs, sizeof(tick_procs[0]), cmp_procs);
  377. printf("CPU states: %6.2f%% user, ", 100.0 * userticks / total_ticks);
  378. printf("%6.2f%% system, ", 100.0 * systemticks / total_ticks);
  379. printf("%6.2f%% kernel, ", 100.0 * kernelticks/ total_ticks);
  380. printf("%6.2f%% idle", 100.0 * idleticks / total_ticks);
  381. #define NEWLINE do { printf("\n"); if(--maxlines <= 0) { return; } } while(0)
  382. NEWLINE;
  383. printf("CPU time displayed ('%c' to cycle): %s; ",
  384. TIMECYCLEKEY, cputimemodename(cputimemode));
  385. printf(" sort order ('%c' to cycle): %s", ORDERKEY, ordername(order));
  386. NEWLINE;
  387. NEWLINE;
  388. printf(" PID USERNAME PRI NICE SIZE STATE TIME CPU COMMAND");
  389. NEWLINE;
  390. for(p = 0; p < nprocs; p++) {
  391. struct proc *pr;
  392. int level = 0;
  393. pr = tick_procs[p].p;
  394. if((pr->p_flags & IS_TASK) && pr->p_pid != KERNEL) {
  395. /* skip old kernel tasks as they don't run anymore */
  396. continue;
  397. }
  398. /* If we're in blocked verbose mode, indicate start of
  399. * blocked processes.
  400. */
  401. if(blockedverbose && (pr->p_flags & BLOCKED) && !blockedseen) {
  402. NEWLINE;
  403. printf("Blocked processes:");
  404. NEWLINE;
  405. blockedseen = 1;
  406. }
  407. print_proc(&tick_procs[p], total_ticks);
  408. NEWLINE;
  409. if(!blockedverbose)
  410. continue;
  411. /* Traverse dependency chain if blocked. */
  412. while(pr->p_flags & BLOCKED) {
  413. endpoint_t dep = NONE;
  414. struct tp *tpdep;
  415. level += 5;
  416. if((dep = pr->p_blocked) == NONE) {
  417. printf("not blocked on a process");
  418. NEWLINE;
  419. break;
  420. }
  421. if(dep == ANY)
  422. break;
  423. tpdep = lookup(dep, tick_procs, nprocs);
  424. pr = tpdep->p;
  425. printf("%*s> ", level, "");
  426. print_proc(tpdep, total_ticks);
  427. NEWLINE;
  428. }
  429. }
  430. }
  431. void showtop(int cputimemode, int r)
  432. {
  433. #define NLOADS 3
  434. double loads[NLOADS];
  435. int nloads, lines = 0;
  436. struct winsize winsize;
  437. if(ioctl(STDIN_FILENO, TIOCGWINSZ, &winsize) != 0) {
  438. perror("TIOCGWINSZ");
  439. fprintf(stderr, "TIOCGWINSZ failed\n");
  440. exit(1);
  441. }
  442. get_procs();
  443. if (prev_proc == NULL)
  444. get_procs();
  445. if((nloads = getloadavg(loads, NLOADS)) != NLOADS) {
  446. fprintf(stderr, "getloadavg() failed - %d loads\n", nloads);
  447. exit(1);
  448. }
  449. printf("%s", Tclr_all);
  450. lines += print_load(loads, NLOADS);
  451. lines += print_proc_summary(proc);
  452. lines += print_memory();
  453. if(winsize.ws_row > 0) r = winsize.ws_row;
  454. print_procs(r - lines - 2, prev_proc, proc, cputimemode);
  455. fflush(NULL);
  456. }
  457. void init(int *rows)
  458. {
  459. char *term;
  460. static char buffer[TC_BUFFER], strings[TC_STRINGS];
  461. char *s = strings, *v;
  462. *rows = 0;
  463. if(!(term = getenv("TERM"))) {
  464. fprintf(stderr, "No TERM set\n");
  465. exit(1);
  466. }
  467. if ( tgetent( buffer, term ) != 1 ) {
  468. fprintf(stderr, "tgetent failed for term %s\n", term);
  469. exit(1);
  470. }
  471. initscr();
  472. cbreak();
  473. if ( (Tclr_all = tgetstr( "cl", &s )) == NULL )
  474. Tclr_all = "\f";
  475. if((v = tgetstr ("li", &s)) != NULL)
  476. sscanf(v, "%d", rows);
  477. if(*rows < 1) *rows = 24;
  478. }
  479. void sigwinch(int sig) { }
  480. void getkinfo(void)
  481. {
  482. FILE *fp;
  483. if ((fp = fopen("kinfo", "r")) == NULL) {
  484. fprintf(stderr, "opening " _PATH_PROC "kinfo failed\n");
  485. exit(1);
  486. }
  487. if (fscanf(fp, "%u %u", &nr_procs, &nr_tasks) != 2) {
  488. fprintf(stderr, "reading from " _PATH_PROC "kinfo failed\n");
  489. exit(1);
  490. }
  491. fclose(fp);
  492. nr_total = (int) (nr_procs + nr_tasks);
  493. }
  494. int mytop()
  495. {
  496. int argc, char *argv[];
  497. int r, c, s = 0;
  498. int cputimemode = 1; /* bitmap. */
  499. if (chdir(_PATH_PROC) != 0) {
  500. perror("chdir to " _PATH_PROC);
  501. return 1;
  502. }
  503. system_hz = (u32_t) sysconf(_SC_CLK_TCK);
  504. getkinfo();
  505. init(&r);
  506. while((c=getopt(argc, argv, "s:B")) != EOF) {
  507. switch(c) {
  508. case 's':
  509. s = atoi(optarg);
  510. break;
  511. case 'B':
  512. blockedverbose = 1;
  513. break;
  514. default:
  515. fprintf(stderr,
  516. "Usage: %s [-s<secdelay>] [-B]\n",
  517. argv[0]);
  518. return 1;
  519. }
  520. }
  521. if(s < 1)
  522. s = 2;
  523. /* Catch window size changes so display is updated properly
  524. * right away.
  525. */
  526. signal(SIGWINCH, sigwinch);
  527. while(1) {
  528. fd_set fds;
  529. int ns;
  530. struct timeval tv;
  531. showtop(cputimemode, r);
  532. tv.tv_sec = s;
  533. tv.tv_usec = 0;
  534. FD_ZERO(&fds);
  535. FD_SET(STDIN_FILENO, &fds);
  536. if((ns=select(STDIN_FILENO+1, &fds, NULL, NULL, &tv)) < 0
  537. && errno != EINTR) {
  538. perror("select");
  539. sleep(1);
  540. }
  541. if(ns > 0 && FD_ISSET(STDIN_FILENO, &fds)) {
  542. char c;
  543. if(read(STDIN_FILENO, &c, 1) == 1) {
  544. switch(c) {
  545. case 'q':
  546. putchar('\r');
  547. return 0;
  548. break;
  549. case 'o':
  550. order++;
  551. if(order > ORDER_HIGHEST)
  552. order = 0;
  553. break;
  554. case TIMECYCLEKEY:
  555. cputimemode++;
  556. if(cputimemode >= (1L << CPUTIMENAMES))
  557. cputimemode = 1;
  558. break;
  559. }
  560. }
  561. }
  562. }
  563. return 0;
  564. }