| /* top.c - Provide a view of process activity in real time. |
| * |
| * Copyright 2013 Bilal Qureshi <bilal.jmi@gmail.com> |
| * Copyright 2013 Ashwini Kumar <ak.ashwini@gmail.com> |
| * Copyright 2013 Kyungwan Han <asura321@gmail.com> |
| * |
| * No Standard |
| |
| USE_TOP(NEWTOY(top, ">0d#=3n#<1mb", TOYFLAG_USR|TOYFLAG_BIN)) |
| |
| config TOP |
| bool "top" |
| default n |
| help |
| |
| usage: top [-mb] [ -d seconds ] [ -n iterations ] |
| |
| Provide a view of process activity in real time. |
| Keys |
| N/M/P/T show CPU usage, sort by pid/mem/cpu/time |
| S show memory |
| R reverse sort |
| H toggle threads |
| C,1 toggle SMP |
| Q,^C exit |
| |
| Options |
| -n Iterations before exiting |
| -d Delay between updates |
| -m Same as 's' key |
| -b Batch mode |
| */ |
| |
| #define FOR_top |
| #include "toys.h" |
| #include <signal.h> |
| #include <poll.h> |
| |
| GLOBALS( |
| long iterations; |
| long delay; |
| |
| long cmp_field; |
| long reverse; |
| long rows; |
| long smp; |
| long threads; |
| long m_flag; |
| long num_new_procs; |
| long scroll_offset; |
| struct termios inf; |
| ) |
| |
| #define PROC_NAME_LEN 512 //For long cmdline. |
| #define INIT_PROCS 50 |
| |
| struct cpu_info { |
| long unsigned utime, ntime, stime, itime; |
| long unsigned iowtime, irqtime, sirqtime, steal; |
| unsigned long long total; |
| }; |
| |
| struct keycode_map_s { |
| char *key; |
| int code; |
| }; |
| |
| struct proc_info { |
| struct proc_info *next; |
| pid_t pid, ppid; |
| uid_t uid; |
| char name[PROC_NAME_LEN]; |
| char tname[PROC_NAME_LEN]; |
| char state[4]; |
| int prs; |
| unsigned long utime, stime, delta_utime, delta_stime, delta_time; |
| unsigned long vss, vssrw, rss, rss_shr, drt, drt_shr, stack; |
| }; |
| |
| static struct proc_info *free_procs, **old_procs, **new_procs; |
| static struct cpu_info old_cpu[10], new_cpu[10]; //1 total, 8 cores, 1 null |
| static int (*proc_cmp)(const void *a, const void *b); |
| |
| static struct proc_info *find_old_proc(pid_t pid) |
| { |
| int i; |
| |
| for (i = 0; old_procs && old_procs[i]; i++) |
| if (old_procs[i]->pid == pid) return old_procs[i]; |
| |
| return NULL; |
| } |
| |
| static void read_stat(char *filename, struct proc_info *proc) |
| { |
| int nice; |
| FILE *file; |
| char *open_paren, *close_paren; |
| |
| if (!(file = fopen(filename, "r"))) return; |
| fgets(toybuf, sizeof(toybuf), file); |
| fclose(file); |
| |
| // Split at first '(' and last ')' to get process name. |
| open_paren = strchr(toybuf, '('); |
| close_paren = strrchr(toybuf, ')'); |
| if (!open_paren || !close_paren) return; |
| |
| *open_paren = *close_paren = '\0'; |
| snprintf(proc->tname, PROC_NAME_LEN, "[%s]",open_paren + 1); |
| |
| // Scan rest of string. |
| sscanf(close_paren + 1, " %c %d %*d %*d %*d %*d %*d %*d %*d %*d %*d " |
| "%lu %lu %*d %*d %*d %d %*d %*d %*d %lu %ld " |
| "%*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %d", |
| &proc->state[0], &proc->ppid, &proc->utime, &proc->stime, &nice, |
| &proc->vss, &proc->rss, &proc->prs); |
| if (!proc->vss && proc->state[0] != 'Z') proc->state[1] = 'W'; |
| else proc->state[1] = ' '; |
| if (nice < 0 ) proc->state[2] = '<'; |
| else if (nice) proc->state[2] = 'N'; |
| else proc->state[2] = ' '; |
| } |
| |
| static void read_status(char *filename, struct proc_info *proc) |
| { |
| FILE *file; |
| |
| if (!(file = fopen(filename, "r"))) return; |
| while (fgets(toybuf, sizeof(toybuf), file)) |
| if (sscanf(toybuf, "Uid: %u", &(proc->uid)) == 1) break; |
| |
| fclose(file); |
| } |
| |
| static void read_cmdline(char *filename, struct proc_info *proc) |
| { |
| int fd, len, rbytes = 0; |
| char *ch, *base, tname[PROC_NAME_LEN]; |
| |
| if ((fd = open(filename, O_RDONLY)) == -1) return; |
| rbytes = readall(fd, toybuf, sizeof(toybuf)); |
| close(fd); |
| if (rbytes <= 0) { |
| strcpy(proc->name, proc->tname); |
| return; |
| } |
| toybuf[rbytes] = '\0'; |
| while (--rbytes >= 0 && toybuf[rbytes] == '\0') continue; |
| |
| snprintf(tname, PROC_NAME_LEN, "%s", proc->tname+1); |
| tname[strlen(tname) - 1] = '\0'; |
| ch = strchr(toybuf, ' '); |
| if (ch) *ch = '\0'; |
| base = strrchr(toybuf, '/'); |
| if (base) base++; |
| else base = toybuf; |
| |
| for (; rbytes >= 0; rbytes--) |
| if ((unsigned char)toybuf[rbytes] < ' ') toybuf[rbytes] = ' '; |
| |
| if (*base == '-') base++; |
| len = strlen(tname); |
| if (strncmp(base, tname, len)) { |
| len +=3; //{,}, \0 |
| rbytes = strlen(toybuf); |
| memmove(toybuf+ len, toybuf, rbytes+1); |
| snprintf(toybuf, sizeof(toybuf), "{%s}", tname); |
| toybuf[len-1] = ' '; |
| } |
| snprintf(proc->name, PROC_NAME_LEN, "%s", toybuf); |
| } |
| |
| static void add_proc(int proc_num, struct proc_info *proc) |
| { |
| int i; |
| |
| if (proc_num >= TT.num_new_procs-1) { |
| new_procs = xrealloc(new_procs, (INIT_PROCS + TT.num_new_procs) |
| * sizeof(struct proc_info *)); |
| for (i = TT.num_new_procs; i < (INIT_PROCS + TT.num_new_procs); i++) |
| new_procs[i] = NULL; |
| TT.num_new_procs += INIT_PROCS; |
| } |
| new_procs[proc_num] = proc; |
| } |
| |
| void signal_handler(int sig) |
| { |
| tcsetattr(STDIN_FILENO, TCSANOW, &TT.inf); |
| xputc('\n'); |
| signal(sig, SIG_DFL); |
| raise(sig); |
| _exit(sig | 128); |
| } |
| |
| static int get_key_code(char *ch, int i) |
| { |
| static struct keycode_map_s type2[] = { |
| {"OA",KEY_UP}, {"OB",KEY_DOWN}, {"OH",KEY_HOME}, |
| {"OF",KEY_END}, {"[A",KEY_UP}, {"[B",KEY_DOWN}, |
| {"[H",KEY_HOME}, {"[F",KEY_END}, {NULL, 0} |
| }; |
| |
| static struct keycode_map_s type3[] = { |
| {"[1~", KEY_HOME}, {"[4~", KEY_END}, {"[5~", KEY_PGUP}, |
| {"[6~", KEY_PGDN}, {"[7~", KEY_HOME}, {"[8~", KEY_END}, |
| {NULL, 0} |
| }; |
| struct keycode_map_s *table, *keytable[3] = {type2, type3, NULL}; |
| int j; |
| |
| if ( i > 3 || i < 1) return -1; |
| |
| for (j=0; (table = keytable[j]); j++) { |
| while (table->key) { |
| if (!strncmp(ch, table->key, i)) break; |
| table++; |
| } |
| if (table->key) { |
| if (i == 1 || (i == 2 && j)) return 1; |
| return table->code; |
| } |
| } |
| return -1; |
| } |
| |
| static int read_input(int delay) |
| { |
| struct pollfd pfd[1]; |
| int ret, fret = 0, cnt = 0, escproc = 0, timeout = delay * 1000; |
| char ch, seq[4] = {0,}; |
| struct termios newf; |
| |
| tcgetattr(0, &TT.inf); |
| if (toys.optflags & FLAG_b) { |
| sleep(delay); |
| return 0; |
| } |
| pfd[0].fd = 0; |
| pfd[0].events = POLLIN; |
| |
| //prepare terminal for input, without Enter of Carriage return |
| memcpy(&newf, &TT.inf, sizeof(struct termios)); |
| newf.c_lflag &= ~(ICANON | ECHO | ECHONL); |
| newf.c_cc[VMIN] = 1; |
| newf.c_cc[VTIME] = 0; |
| tcsetattr(0, TCSANOW, &newf); |
| |
| while (1) { |
| if ((ret = poll(pfd, 1, timeout)) >= 0) break; |
| else { |
| if (timeout > 0) timeout--; |
| if (errno == EINTR) continue; |
| perror_exit("poll"); |
| } |
| } |
| |
| while (ret) { |
| if (read(STDIN_FILENO, &ch, 1) != 1) toys.optflags |= FLAG_b; |
| else if (ch == '\033' || escproc) { |
| int code; |
| //process ESC keys |
| if (!escproc) { |
| if (!poll(pfd, 1, 50)) break; //no more chars |
| escproc = 1; |
| continue; |
| } |
| seq[cnt++] = ch; |
| code = get_key_code(seq, cnt); |
| switch(code) { |
| case -1: //no match |
| fret = 0; |
| break; |
| case 1: //read more |
| continue; |
| default: // got the key |
| fret = code; |
| break; |
| } |
| } else if ((ch == TT.inf.c_cc[VINTR]) |
| || (ch == TT.inf.c_cc[VEOF])) |
| fret = 'q'; |
| else fret = ch | 0x20; |
| break; |
| } |
| tcsetattr(0, TCSANOW, &TT.inf); |
| return fret; |
| } |
| |
| // Allocation for Processes |
| static struct proc_info *alloc_proc(void) |
| { |
| struct proc_info *proc; |
| |
| if (free_procs) { |
| proc = free_procs; |
| free_procs = free_procs->next; |
| memset(proc, 0, sizeof(*proc)); |
| } else proc = xzalloc(sizeof(*proc)); |
| |
| return proc; |
| } |
| |
| static void free_proc_list(struct proc_info *procs) |
| { |
| struct proc_info *tmp = procs; |
| |
| for (;tmp; tmp = procs) { |
| procs = procs->next; |
| free(tmp); |
| } |
| } |
| |
| // Free allocated Processes in order to avoid memory leaks |
| static void free_proc(struct proc_info *proc) |
| { |
| proc->next = free_procs; |
| free_procs = proc; |
| } |
| |
| static struct proc_info *add_new_proc(pid_t pid, pid_t tid) |
| { |
| char filename[64]; |
| struct proc_info *proc = alloc_proc(); |
| |
| proc->pid = (tid)? tid : pid; |
| if (!tid) { |
| sprintf(filename, "/proc/%d/stat", pid); |
| read_stat(filename, proc); |
| sprintf(filename, "/proc/%d/cmdline", pid); |
| read_cmdline(filename, proc); |
| sprintf(filename, "/proc/%d/status", pid); |
| read_status(filename, proc); |
| } else{ |
| sprintf(filename, "/proc/%d/task/%d/stat", pid,tid); |
| read_stat(filename, proc); |
| sprintf(filename, "/proc/%d/task/%d/cmdline", pid, tid); |
| read_cmdline(filename, proc); |
| } |
| return proc; |
| } |
| |
| static void read_smaps(pid_t pid, struct proc_info *p) |
| { |
| FILE *fp; |
| char *line; |
| size_t len; |
| long long start, end, val, prvcl, prvdr, shrdr, shrcl; |
| int count; |
| |
| p->vss = p->rss = 0; |
| start = end = val = prvcl = prvdr = shrdr = shrcl = 0; |
| sprintf(toybuf, "/proc/%u/smaps", pid); |
| if (!(fp = fopen(toybuf, "r"))) { |
| error_msg("No %ld\n", (long)pid); |
| return; |
| } |
| for (;;) { |
| int off; |
| |
| line = 0; |
| if (0 >= getline(&line, &len, fp)) break; |
| count = sscanf(line, "%llx-%llx %s %*s %*s %*s %n", |
| &start, &end, toybuf, &off); |
| |
| if (count == 3) { |
| end = end - start; |
| if (strncmp(line+off, "/dev/", 5) || !strcmp(line+off, "/dev/zero\n")) { |
| p->vss += end; |
| if (toybuf[1] == 'w') p->vssrw += end; |
| } |
| if (line[off] && !strncmp(line+off, "[stack]",7)) p->stack += end; |
| } else { |
| if (0<sscanf(line, "Private_Clean: %lld", &val)) prvcl += val; |
| if (0<sscanf(line, "Private_Dirty: %lld", &val)) prvdr += val; |
| if (0<sscanf(line, "Shared_Dirty: %lld", &val)) shrdr += val; |
| if (0<sscanf(line, "Shared_Clean: %lld", &val)) shrcl += val; |
| } |
| free(line); |
| } |
| free(line); //incase it broke out. |
| p->rss_shr = shrdr + shrcl; |
| p->drt = prvdr + shrdr; |
| p->drt_shr = shrdr; |
| p->rss = p->rss_shr + prvdr + prvcl; |
| fclose(fp); |
| } |
| |
| static void read_procs(void) // Read Processes |
| { |
| DIR *proc_dir, *thr_dir; |
| struct dirent *pid_dir, *t_dir; |
| struct proc_info *proc; |
| pid_t pid, tid; |
| int proc_num = 0; |
| |
| proc_dir = opendir("/proc"); |
| if (!proc_dir) perror_exit("Could not open /proc"); |
| |
| new_procs = xzalloc(INIT_PROCS * sizeof(struct proc_info *)); |
| TT.num_new_procs = INIT_PROCS; |
| |
| while ((pid_dir = readdir(proc_dir))) { |
| if (!isdigit(pid_dir->d_name[0])) continue; |
| |
| pid = atoi(pid_dir->d_name); |
| proc = add_new_proc(pid, 0); |
| if (TT.m_flag) { |
| read_smaps(pid, proc); |
| if (!proc->vss) { |
| free(proc); |
| continue; |
| } |
| } |
| add_proc(proc_num++, proc); |
| |
| if (TT.threads) { |
| char filename[64]; |
| uid_t uid = proc->uid; |
| |
| sprintf(filename,"/proc/%d/task",pid); |
| if ((thr_dir = opendir(filename))) { |
| while ((t_dir = readdir(thr_dir))) { |
| if (!isdigit(t_dir->d_name[0])) continue; |
| |
| tid = atoi(t_dir->d_name); |
| if (pid == tid) continue; |
| proc = add_new_proc(pid, tid); |
| proc->uid = uid; //child will have same uid as parent. |
| add_proc(proc_num++, proc); |
| } |
| closedir(thr_dir); |
| } |
| } |
| } |
| |
| closedir(proc_dir); |
| TT.num_new_procs = proc_num; |
| } |
| |
| //calculate percentage. |
| static char* show_percent(long unsigned num, long unsigned den) |
| { |
| long res; |
| static char ch, buff[12]={'\0'}; |
| |
| if(num > den) num = den; |
| res = (num * 100)/den; |
| sprintf(buff,"%ld", (num * 100)% den); |
| ch = *buff; |
| sprintf(buff, "%ld.%c",res, ch); |
| return buff; |
| } |
| |
| static int print_header(struct sysinfo *info, unsigned int cols) |
| { |
| int fd, j, k, rows =0; |
| long unsigned total, meminfo_cached, anon, meminfo_mapped, |
| meminfo_slab, meminfo_dirty, meminfo_writeback, swapT, swapF; |
| char *buff; |
| |
| fd = xopen("/proc/meminfo", O_RDONLY); |
| while ((buff = get_line(fd))) { |
| if (!strncmp(buff, "Cached", 6)) |
| sscanf(buff,"%*s %lu\n",&meminfo_cached); |
| else if (!strncmp(buff, "AnonPages", 9)) |
| sscanf(buff,"%*s %lu\n",&anon); |
| else if (!strncmp(buff, "Mapped", 6)) |
| sscanf(buff,"%*s %lu\n",&meminfo_mapped); |
| else if (!strncmp(buff, "Slab", 4)) |
| sscanf(buff,"%*s %lu\n",&meminfo_slab); |
| else if (!strncmp(buff, "Dirty", 5)) |
| sscanf(buff,"%*s %lu\n",&meminfo_dirty); |
| else if (!strncmp(buff, "Writeback", 9)) |
| sscanf(buff,"%*s %lu\n",&meminfo_writeback); |
| else if (!strncmp(buff, "SwapTotal", 9)) |
| sscanf(buff,"%*s %lu\n",&swapT); |
| else if (!strncmp(buff, "SwapFree", 8)) |
| sscanf(buff,"%*s %lu\n",&swapF); |
| free(buff); |
| } |
| close(fd); |
| |
| if (!(toys.optflags & FLAG_b)) printf("\033[H\033[J"); |
| |
| if (TT.m_flag){ |
| sprintf(toybuf, "Mem total:%lu anon:%lu map:%lu free:%lu", |
| ((info->totalram) >> 10), anon, meminfo_mapped, |
| ((info->freeram) >> 10)); |
| printf("%.*s\n", cols, toybuf); |
| |
| sprintf(toybuf, "slab:%lu buf:%lu cache:%lu dirty:%lu write:%lu", |
| meminfo_slab, ((info->bufferram) >>10), meminfo_cached, |
| meminfo_dirty,meminfo_writeback); |
| printf("%.*s\n", cols, toybuf); |
| |
| sprintf(toybuf, "Swap total:%lu free:%lu",swapT, swapF); |
| printf("%.*s\n", cols, toybuf); |
| rows += 3; |
| } else { |
| sprintf(toybuf,"Mem: %luK used, %luK free, %luK shrd, %luK buff, %luK cached", |
| (info->totalram-info->freeram) >>10, (info->freeram) >>10, |
| (info->sharedram) >>10, (info->bufferram) >>10, meminfo_cached); |
| printf("%.*s\n", cols, toybuf); |
| |
| for (k = 1; new_cpu[k].total; k++) { |
| j = 0; |
| if (!TT.smp) { |
| k = 0; |
| j = sprintf(toybuf,"CPU:"); |
| } else j = sprintf(toybuf,"CPU%d:", k-1); |
| |
| total = (new_cpu[k].total) - (old_cpu[k].total); |
| if (!total) total = 1; //avoid denominator as 0, FPE |
| j += sprintf(toybuf + j," %s%% usr", |
| show_percent((new_cpu[k].utime - old_cpu[k].utime), total)); |
| j += sprintf(toybuf+j," %s%% sys", |
| show_percent((new_cpu[k].stime - old_cpu[k].stime), total)); |
| j += sprintf(toybuf+j," %s%% nic", |
| show_percent(new_cpu[k].ntime - old_cpu[k].ntime, total)); |
| j += sprintf(toybuf+j," %s%% idle", |
| show_percent(new_cpu[k].itime - old_cpu[k].itime, total)); |
| j += sprintf(toybuf+j," %s%% io", |
| show_percent((new_cpu[k].iowtime - old_cpu[k].iowtime), total)); |
| j += sprintf(toybuf+j," %s%% irq", |
| show_percent(new_cpu[k].irqtime - old_cpu[k].irqtime, total)); |
| j += sprintf(toybuf+j," %s%% sirq", |
| show_percent(new_cpu[k].sirqtime - old_cpu[k].sirqtime, total)); |
| printf("%.*s\n", cols, toybuf); |
| if (!TT.smp) break; |
| } |
| |
| if ((buff = readfile("/proc/loadavg", NULL, 0))) { |
| buff[strlen(buff) -1] = '\0'; //removing '\n' at end |
| sprintf(toybuf, "Load average: %s", buff); |
| printf("%.*s\n", cols, toybuf); |
| free(buff); |
| } |
| rows += 2 + ((TT.smp) ? k-1 : 1); |
| } |
| return rows; |
| } |
| |
| static void print_procs(void) |
| { |
| int i, j = 0; |
| struct proc_info *old_proc, *proc; |
| long unsigned total_delta_time; |
| struct passwd *user; |
| char *user_str, user_buf[20]; |
| struct sysinfo info; |
| unsigned int cols=0, rows =0; |
| |
| terminal_size(&cols, &rows); |
| if (!rows){ |
| rows = 24; //on serial consoles setting default |
| cols = 79; |
| } |
| if (toys.optflags & FLAG_b) rows = INT_MAX; |
| TT.rows = rows; |
| |
| for (i = 0; i < TT.num_new_procs; i++) { |
| if (new_procs[i]) { |
| old_proc = find_old_proc(new_procs[i]->pid); |
| if (old_proc) { |
| new_procs[i]->delta_utime = new_procs[i]->utime - old_proc->utime; |
| new_procs[i]->delta_stime = new_procs[i]->stime - old_proc->stime; |
| } else { |
| new_procs[i]->delta_utime = 0; |
| new_procs[i]->delta_stime = 0; |
| } |
| new_procs[i]->delta_time = new_procs[i]->delta_utime |
| + new_procs[i]->delta_stime; |
| } |
| } |
| |
| total_delta_time = new_cpu[0].total - old_cpu[0].total; |
| if (!total_delta_time) total_delta_time = 1; |
| |
| qsort(new_procs, TT.num_new_procs, sizeof(struct proc_info *), proc_cmp); |
| |
| //Memory details |
| sysinfo(&info); |
| info.totalram *= info.mem_unit; |
| info.freeram *= info.mem_unit; |
| info.sharedram *= info.mem_unit; |
| info.bufferram *= info.mem_unit; |
| |
| rows -= print_header(&info, cols); |
| |
| if (TT.m_flag) { |
| sprintf(toybuf, "%5s %5s %5s %5s %5s %5s %5s %5s %s", "PID", "VSZ", "VSZRW", |
| "RSS", "(SHR)", "DIRTY", "(SHR)", "STACK", "COMMAND"); |
| toybuf[11 + TT.cmp_field*6] = (TT.reverse)?'_':'^'; //11 for PID,VSZ fields |
| } else sprintf(toybuf, "%5s %5s %-8s %4s %5s %5s %4s %5s %s", "PID", "PPID", |
| "USER", "STAT", "VSZ", "%VSZ", "CPU" , "%CPU", "COMMAND"); |
| |
| printf((toys.optflags & FLAG_b)?"%.*s\n":"\033[7m%.*s\033[0m\n",cols, toybuf); |
| rows--; |
| for (i = TT.scroll_offset; i < TT.num_new_procs; i++) { |
| j = 0; |
| proc = new_procs[i]; |
| |
| user = getpwuid(proc->uid); |
| if (user && user->pw_name) { |
| user_str = user->pw_name; |
| } else { |
| snprintf(user_buf, 20, "%d", proc->uid); |
| user_str = user_buf; |
| } |
| |
| if (!TT.m_flag ) |
| { |
| float vss_percentage = (float)(proc->vss)/info.totalram * 100; |
| |
| j = sprintf(toybuf, "%5d %5d %-8.8s %-4s",proc->pid, proc->ppid, user_str, |
| proc->state); |
| |
| if ((proc->vss >> 10) >= 100000) |
| j += sprintf(toybuf + j, " %4lum", ((proc->vss >> 10) >> 10)); |
| else j += sprintf(toybuf+j, " %5lu", (proc->vss >> 10)); |
| |
| sprintf(toybuf + j," %5.1f %4d %5s %s", vss_percentage, proc->prs, |
| show_percent(proc->delta_time, total_delta_time), |
| ((proc->name[0])? proc->name : proc->tname)); |
| printf("%.*s", cols, toybuf); |
| } else { |
| j = sprintf(toybuf, "%5d",proc->pid); |
| |
| if ((proc->vss >> 10) >= 100000) |
| j += sprintf(toybuf + j, " %4lum", ((proc->vss >> 10) >> 10)); |
| else j += sprintf(toybuf+j, " %5lu", (proc->vss >> 10)); |
| if ((proc->vssrw >>10) >= 100000) |
| j += sprintf(toybuf + j, " %4lum", ((proc->vssrw >> 10) >> 10)); |
| else j += sprintf(toybuf+j, " %5lu", (proc->vssrw >> 10)); |
| if (proc->rss >= 100000) |
| j += sprintf(toybuf + j, " %4lum", ((proc->rss >> 10))); |
| else j += sprintf(toybuf+j, " %5lu", proc->rss); |
| if (proc->rss_shr >= 100000) |
| j += sprintf(toybuf + j, " %4lum", (proc->rss_shr >> 10)); |
| else j += sprintf(toybuf+j, " %5lu", proc->rss_shr); |
| if (proc->drt >= 100000) |
| j += sprintf(toybuf + j, " %4lum", (proc->drt >> 10)); |
| else j += sprintf(toybuf+j, " %5lu", proc->drt); |
| if (proc->drt_shr >= 100000) |
| j += sprintf(toybuf + j, " %4lum", (proc->drt_shr >> 10)); |
| else j += sprintf(toybuf+j, " %5lu", proc->drt_shr); |
| if ((proc->stack >>10) >= 100000) |
| j += sprintf(toybuf + j, " %4lum", ((proc->stack >> 10) >> 10)); |
| else j += sprintf(toybuf+j, " %5lu", (proc->stack >> 10)); |
| |
| sprintf(toybuf + j," %s",((proc->name[0])? proc->name : proc->tname)); |
| printf("%.*s", cols, toybuf); |
| } |
| rows--; |
| if (!rows) { |
| xputc('\r'); |
| break; //don't print any more process details. |
| } else xputc('\n'); |
| } |
| } |
| |
| /* |
| * Free old processes(displayed in old iteration) in order to |
| * avoid memory leaks |
| */ |
| static void free_procs_arr(struct proc_info **procs) |
| { |
| int i; |
| for (i = 0; procs && procs[i]; i++) |
| free_proc(procs[i]); |
| |
| free(procs); |
| } |
| |
| static int numcmp(long long a, long long b) |
| { |
| if (a < b) return (TT.reverse)?-1 : 1; |
| if (a > b) return (TT.reverse)?1 : -1; |
| return 0; |
| } |
| |
| static int top_mem_cmp(const void *a, const void *b) |
| { |
| char *pa, *pb; |
| |
| int n = offsetof(struct proc_info, vss) + TT.cmp_field * sizeof(unsigned long); |
| pa = *((char **)a); pb = *((char **)b); |
| return numcmp(*(unsigned long*)(pa+n), *(unsigned long*)(pb+n)); |
| } |
| |
| static int proc_time_cmp(const void *a, const void *b) |
| { |
| struct proc_info *pa, *pb; |
| |
| pa = *((struct proc_info **)a); pb = *((struct proc_info **)b); |
| return numcmp(pa->utime + pa->stime, pb->utime+pa->stime); |
| } |
| |
| /* |
| * Function to compare CPU usgae % while displaying processes |
| * according to CPU usage |
| */ |
| static int proc_cpu_cmp(const void *a, const void *b) |
| { |
| struct proc_info *pa, *pb; |
| |
| pa = *((struct proc_info **)a); pb = *((struct proc_info **)b); |
| return numcmp(pa->delta_time, pb->delta_time); |
| } |
| |
| /* |
| * Function to compare memory taking by a process at the time of |
| * displaying processes according to Memory usage |
| */ |
| static int proc_vss_cmp(const void *a, const void *b) |
| { |
| struct proc_info *pa, *pb; |
| |
| pa = *((struct proc_info **)a); pb = *((struct proc_info **)b); |
| return numcmp(pa->vss, pb->vss); |
| } |
| |
| static int proc_pid_cmp(const void *a, const void *b) |
| { |
| struct proc_info *pa, *pb; |
| |
| pa = *((struct proc_info **)a); pb = *((struct proc_info **)b); |
| return numcmp(pa->pid, pb->pid); |
| } |
| |
| /* Read CPU stats for all the cores, assuming max 8 cores |
| * to be present here. |
| */ |
| static void read_cpu_stat() |
| { |
| int i; |
| size_t len; |
| char *line = 0, *params = "%lu %lu %lu %lu %lu %lu %lu %lu"; |
| FILE *fp = xfopen("/proc/stat", "r"); |
| |
| for (i = 0; i<=8 && getline(&line, &len, fp) > 0; i++) { |
| if (i) sprintf(toybuf, "cpu%d %s", i-1, params); |
| else sprintf(toybuf, "cpu %s", params); |
| len = sscanf(line, toybuf, &new_cpu[i].utime, &new_cpu[i].ntime, |
| &new_cpu[i].stime, &new_cpu[i].itime, &new_cpu[i].iowtime, |
| &new_cpu[i].irqtime, &new_cpu[i].sirqtime, &new_cpu[i].steal); |
| if (len == 8) |
| new_cpu[i].total = new_cpu[i].utime + new_cpu[i].ntime + new_cpu[i].stime |
| + new_cpu[i].itime + new_cpu[i].iowtime + new_cpu[i].irqtime |
| + new_cpu[i].sirqtime + new_cpu[i].steal; |
| |
| free(line); |
| line = 0; |
| } |
| fclose(fp); |
| } |
| |
| void top_main(void ) |
| { |
| int get_key; |
| |
| proc_cmp = &proc_cpu_cmp; |
| if ( TT.delay < 0) TT.delay = 3; |
| if (toys.optflags & FLAG_m) { |
| proc_cmp = &top_mem_cmp; |
| TT.m_flag = 1; |
| } |
| |
| sigatexit(signal_handler); |
| read_cpu_stat(); |
| get_key = read_input(0); |
| |
| while (!(toys.optflags & FLAG_n) || TT.iterations--) { |
| old_procs = new_procs; |
| memcpy(old_cpu, new_cpu, sizeof(old_cpu)); |
| read_procs(); |
| read_cpu_stat(); |
| print_procs(); |
| free_procs_arr(old_procs); |
| if ((toys.optflags & FLAG_n) && !TT.iterations) break; |
| |
| get_key = read_input(TT.delay); |
| if (get_key == 'q') break; |
| |
| switch(get_key) { |
| case 'n': |
| proc_cmp = &proc_pid_cmp; |
| TT.m_flag = 0; |
| break; |
| case 'h': |
| if (!TT.m_flag) TT.threads ^= 1; |
| break; |
| case 'm': |
| proc_cmp = &proc_vss_cmp; |
| TT.m_flag = 0; |
| break; |
| case 'r': |
| TT.reverse ^= 1; |
| break; |
| case 'c': |
| case '1': |
| TT.smp ^= 1; |
| break; |
| case 's': |
| TT.m_flag = 1; |
| TT.cmp_field = (TT.cmp_field + 1) % 7;//7 sort fields, vss,vssrw... |
| proc_cmp = &top_mem_cmp; |
| break; |
| case 'p': |
| proc_cmp = &proc_cpu_cmp; |
| TT.m_flag = 0; |
| break; |
| case 't': |
| proc_cmp = &proc_time_cmp; |
| TT.m_flag = 0; |
| break; |
| case KEY_UP: |
| TT.scroll_offset--; |
| break; |
| case KEY_DOWN: |
| TT.scroll_offset++; |
| break; |
| case KEY_HOME: |
| TT.scroll_offset = 0; |
| break; |
| case KEY_END: |
| TT.scroll_offset = TT.num_new_procs - TT.rows/2; |
| break; |
| case KEY_PGUP: |
| TT.scroll_offset -= TT.rows/2; |
| break; |
| case KEY_PGDN: |
| TT.scroll_offset += TT.rows/2; |
| break; |
| } |
| if (TT.scroll_offset >= TT.num_new_procs) TT.scroll_offset = TT.num_new_procs-1; |
| if (TT.scroll_offset < 0) TT.scroll_offset = 0; |
| } |
| xputc('\n'); |
| if (CFG_TOYBOX_FREE) { |
| free_proc_list(free_procs); |
| free_procs = NULL; |
| free_procs_arr(new_procs); |
| free_proc_list(free_procs); |
| } |
| } |