void *_sbrk(ptrdiff_t increment) { extern char _end; static char *heap_end = 0; char *prev_heap_end; char register *stack_ptr asm("sp"); if (heap_end == 0) { heap_end = &_end; } prev_heap_end = heap_end; if ((heap_end + increment) > stack_ptr) { PUTS("sp="); PUTP(stack_ptr); PUTS("\n"); PUTS("heap="); PUTP(heap_end); PUTS("\n"); PUTS("sbrk: no more heap\n"); errno = ENOMEM; return (void*)-1; } heap_end += increment; return prev_heap_end; }
/* * Reads the memory info and displays it. Returns the total memory * available, for use in percent memory usage calculations. */ unsigned show_meminfo(void) { unsigned long long **mem = get_meminfo(); /* read+parse /proc/meminfo */ if (!mem || mem[meminfo_main][meminfo_total] == 0) { /* cannot normalize mem usage */ fprintf(stderr, "Cannot get size of memory from /proc/meminfo\n"); error_end(1); } if (show_memory) { printf("Mem: %7LdK av, %7LdK used, %7LdK free, %7LdK shrd, %7LdK buff", mem[meminfo_main][meminfo_total] >> 10, mem[meminfo_main][meminfo_used] >> 10, mem[meminfo_main][meminfo_free] >> 10, mem[meminfo_main][meminfo_shared] >> 10, mem[meminfo_main][meminfo_buffers] >> 10); PUTP(top_clrtoeol); putchar('\n'); printf("Swap: %7LdK av, %7LdK used, %7LdK free %7LdK cached", mem[meminfo_swap][meminfo_total] >> 10, mem[meminfo_swap][meminfo_used] >> 10, mem[meminfo_swap][meminfo_free] >> 10, mem[meminfo_total][meminfo_cached] >> 10); PUTP(top_clrtoeol); putchar('\n'); } PUTP(me); PUTP(top_clrtoeol); putchar('\n'); return mem[meminfo_main][meminfo_total] >> 10; }
/* * Display the specification line of all fields. Upper case indicates * a displayed field, display order is according to the order of the * letters. A short description of each field is shown as well. * The description of a displayed field is marked by a leading * asterisk (*). */ void show_fields(void) { int i, row, col; char *p; clear_screen(); PUTP(tgoto(cm, 3, 0)); printf("Current Field Order: %s\n", Fields); for (i = 0; i < sizeof headers / sizeof headers[0]; ++i) { row = i % (Lines - 3) + 3; col = i / (Lines - 3) * 40; PUTP(tgoto(cm, col, row)); for (p = headers[i]; *p == ' '; ++p); printf("%c %c: %-10s = %s", (strchr(Fields, i + 'A') != NULL) ? '*' : ' ', i + 'A', p, headers2[i]); } }
/********************************************************** * delete_from_list * Place the free block ptr bp on the appropriated * segregated free list * Insert at the first element of the list **********************************************************/ void delete_from_list(void *bp) { // If the block is allocated, cannot be freed if (GET_ALLOC(HDRP(bp))) { return; } // If it does not have next element if (*(char*) bp == 0) { // Set previous block ptr in the previous element to 0 PUT(PREVB(bp), 0); } else { // If it has next element // Set next block ptr in the prev element to current's next element PUTP(PREVB(bp), NEXTB(bp)); // Set prev block ptr in the next element to current's prev element PUTP(PREVP(NEXTB(bp)), PREVB(bp)); } }
/* * Normal end of execution. */ void sig_end(int signo) { if (psdbsucc) close_psdb(); if (!Batch) tcsetattr(0, TCSAFLUSH, &Savetty); PUTP(tgoto(cm, 0, Lines - 1)); fputs("\r\n", stdout); exit(0); }
/********************************************************** * insert_freelist * Place the free block ptr bp on the appropriated * segregated free list * Insert at the first element of the list **********************************************************/ void insert_freelist(intptr_t * bp) { // Find out which list should the free block insert to int index = size_to_index(GET_SIZE(HDRP(bp))); // Head of the list intptr_t * listp = seg_free_list[index]; // Set the prev_ptr and next_ptr of the current block PUT(bp, GET(seg_free_list[index])); // next PUTP(PREVP(bp), listp); // prev // If the list previously has elements inside if (GET(listp) != 0) { // Set the next element to point to the current bp PUTP(PREVP(*listp), bp); } //Set head to point to the current bp PUTP(listp, bp); }
/* * SIGTSTP catcher. */ void sig_stop(int signo) { /* Reset terminal. */ if (!Batch) tcsetattr(0, TCSAFLUSH, &Savetty); PUTP(tgoto(cm, 0, Lines - 3)); fflush(stdout); raise(SIGSTOP); /* Later... */ if (!Batch) tcsetattr (0, TCSAFLUSH, &Rawtty); }
/* * toggle displayed fields */ void change_fields(void) { int i, changed = 0; int row, col; char c, *p; char tmp[2] = " "; show_fields(); for (;;) { PUTP(tgoto(cm, 0, 0)); PUTP(top_clrtoeol); PUTP(tgoto(cm, 3, 0)); PUTP(mr); printf("Current Field Order: %s", Fields); PUTP(me); putchar('\n'); PUTP(tgoto(cm, 0, 1)); printf("Toggle fields with a-x, any other key to return: "); fflush(stdout); if (!Batch) { /* should always be true, but... */ tcsetattr(0, TCSAFLUSH, &Rawtty); read(0, &c, 1); tcsetattr(0, TCSAFLUSH, &Savetty); } i = toupper(c) - 'A'; if (i >= 0 && i < sizeof headers / sizeof headers[0]) { row = i % (Lines - 3) + 3; col = i / (Lines - 3) * 40; PUTP(tgoto(cm, col, row)); if ((p = strchr(Fields, i + 'A')) != NULL) { /* deselect Field */ *p = i + 'a'; putchar(' '); } else if ((p = strchr(Fields, i + 'a')) != NULL) { /* select previously */ *p = i + 'A'; /* deselected field */ putchar('*'); } else { /* select new field */ tmp[0] = i + 'A'; strcat(Fields, tmp); putchar('*'); } changed = 1; fflush(stdout); } else break; } if (changed) Numfields = make_header(); }
/* * change order of displayed fields */ void change_order(void) { char c, ch, *p; int i; show_fields(); for (;;) { PUTP(tgoto(cm, 0, 0)); PUTP(top_clrtoeol); PUTP(tgoto(cm, 3, 0)); PUTP(mr); printf("Current Field Order: %s", Fields); PUTP(me); putchar('\n'); PUTP(tgoto(cm, 0, 1)); printf("Upper case characters move a field to the left, lower case to the right"); fflush(stdout); if (!Batch) { /* should always be true, but... */ tcsetattr(0, TCSAFLUSH, &Rawtty); read(0, &c, 1); tcsetattr(0, TCSAFLUSH, &Savetty); } i = toupper(c) - 'A'; if ((p = strchr(Fields, i + 'A')) != NULL) { if (isupper(c)) p--; if ((p[1] != '\0') && (p >= Fields)) { ch = p[0]; p[0] = p[1]; p[1] = ch; } } else if ((p = strchr(Fields, i + 'a')) != NULL) { if (isupper(c)) p--; if ((p[1] != '\0') && (p >= Fields)) { ch = p[0]; p[0] = p[1]; p[1] = ch; } } else { break; } } Numfields = make_header(); }
/* * Process keyboard input during the main loop */ void do_key(char c) { int numinput, i; char rcfile[MAXNAMELEN]; FILE *fp; /* * First the commands which don't require a terminal mode switch. */ if (c == 'q') sig_end(0); else if (c == ' ') return; else if (c == 12) { clear_screen(); return; } else if (c == 'I') { Irixmode=(Irixmode) ? 0 : 1; return; } /* * Switch the terminal to normal mode. (Will the original * attributes always be normal? Does it matter? I suppose the * shell will be set up the way the user wants it.) */ if (!Batch) tcsetattr(0, TCSANOW, &Savetty); /* * Handle the rest of the commands. */ switch (c) { case '?': case 'h': PUTP(cl); PUTP(ho); putchar('\n'); PUTP(mr); printf("Proc-Top Revision 1.2"); PUTP(me); putchar('\n'); printf("Secure mode "); PUTP(md); fputs(Secure ? "on" : "off", stdout); PUTP(me); fputs("; cumulative mode ", stdout); PUTP(md); fputs(Cumulative ? "on" : "off", stdout); PUTP(me); fputs("; noidle mode ", stdout); PUTP(md); fputs(Noidle ? "on" : "off", stdout); PUTP(me); fputs("\n\n", stdout); printf("%s\n\nPress any key to continue", Secure ? SECURE_HELP_SCREEN : HELP_SCREEN); if (!Batch) tcsetattr(0, TCSANOW, &Rawtty); (void) getchar(); break; case 'i': Noidle = !Noidle; SHOWMESSAGE(("No-idle mode %s", Noidle ? "on" : "off")); break; case 'u': SHOWMESSAGE(("Which User (Blank for All): ")); strcpy(CurrUser,getstr()); break; case 'k': if (Secure) SHOWMESSAGE(("\aCan't kill in secure mode")); else { int pid, signal; PUTP(md); SHOWMESSAGE(("PID to kill: ")); pid = getint(); if (pid == BAD_INPUT) break; PUTP(top_clrtoeol); SHOWMESSAGE(("Kill PID %d with signal [15]: ", pid)); PUTP(me); signal = getsig(); if (signal == -1) signal = SIGTERM; if (kill(pid, signal)) SHOWMESSAGE(("\aKill of PID %d with %d failed: %s", pid, signal, strerror(errno))); } break; case 'l': SHOWMESSAGE(("Display load average %s", !show_loadav ? "on" : "off")); if (show_loadav) { show_loadav = 0; header_lines--; } else { show_loadav = 1; header_lines++; } Numfields = make_header(); break; case 'm': SHOWMESSAGE(("Display memory information %s", !show_memory ? "on" : "off")); if (show_memory) { show_memory = 0; header_lines -= 2; } else { show_memory = 1; header_lines += 2; } Numfields = make_header(); break; case 'M': SHOWMESSAGE(("Sort by memory usage")); sort_type = S_MEM; reset_sort_options(); register_sort_function(-1, (cmp_t)mem_sort); break; case 'n': case '#': printf("Processes to display (0 for unlimited): "); numinput = getint(); if (numinput != BAD_INPUT) { Display_procs = numinput; window_size(0); } break; case 'r': if (Secure) SHOWMESSAGE(("\aCan't renice in secure mode")); else { int pid, val; printf("PID to renice: "); pid = getint(); if (pid == BAD_INPUT) break; PUTP(tgoto(cm, 0, header_lines - 2)); PUTP(top_clrtoeol); printf("Renice PID %d to value: ", pid); val = getint(); if (val == BAD_INPUT) val = 10; if (setpriority(PRIO_PROCESS, pid, val)) SHOWMESSAGE(("\aRenice of PID %d to %d failed: %s", pid, val, strerror(errno))); } break; case 'P': SHOWMESSAGE(("Sort by CPU usage")); sort_type = S_PCPU; reset_sort_options(); register_sort_function(-1, (cmp_t)pcpu_sort); break; case 'A': SHOWMESSAGE(("Sort by age")); sort_type = S_AGE; reset_sort_options(); register_sort_function(-1, (cmp_t)age_sort); break; case 'N': SHOWMESSAGE(("Sort numerically by pid")); sort_type = S_NONE; reset_sort_options(); break; case 'c': show_cmd = !show_cmd; SHOWMESSAGE(("Show %s", show_cmd ? "command names" : "command line")); break; case 'S': Cumulative = !Cumulative; SHOWMESSAGE(("Cumulative mode %s", Cumulative ? "on" : "off")); if (Cumulative) headers[22][1] = 'C'; else headers[22][1] = ' '; Numfields = make_header(); break; case 's': if (Secure) SHOWMESSAGE(("\aCan't change delay in secure mode")); else { double tmp; printf("Delay between updates: "); tmp = getfloat(); if (!(tmp < 0)) Sleeptime = tmp; } break; case 't': SHOWMESSAGE(("Display summary information %s", !show_stats ? "on" : "off")); if (show_stats) { show_stats = 0; header_lines -= 2; } else { show_stats = 1; header_lines += 2; } Numfields = make_header(); break; case 'T': SHOWMESSAGE(("Sort by %stime", Cumulative ? "cumulative " : "")); sort_type = S_TIME; reset_sort_options(); register_sort_function( -1, (cmp_t)time_sort); break; case 'f': case 'F': change_fields(); break; case 'o': case 'O': change_order(); break; case 'W': if (getenv("HOME")) { strcpy(rcfile, getenv("HOME")); strcat(rcfile, "/"); strcat(rcfile, RCFILE); fp = fopen(rcfile, "w"); if (fp != NULL) { fprintf(fp, "%s\n", Fields); i = (int) Sleeptime; if (i < 2) i = 2; if (i > 9) i = 9; fprintf(fp, "%d", i); if (Secure) fprintf(fp, "%c", 's'); if (Cumulative) fprintf(fp, "%c", 'S'); if (!show_cmd) fprintf(fp, "%c", 'c'); if (Noidle) fprintf(fp, "%c", 'i'); if (!show_memory) fprintf(fp, "%c", 'm'); if (!show_loadav) fprintf(fp, "%c", 'l'); if (!show_stats) fprintf(fp, "%c", 't'); if (!Irixmode) fprintf(fp, "%c", 'I'); fprintf(fp, "\n"); fclose(fp); SHOWMESSAGE(("Wrote configuration to %s", rcfile)); } else { SHOWMESSAGE(("Couldn't open %s", rcfile)); } } else { SHOWMESSAGE(("Couldn't get $HOME -- not saving")); } break; default: SHOWMESSAGE(("\aUnknown command `%c' -- hit `h' for help", c)); } /* * Return to raw mode. */ if (!Batch) tcsetattr(0, TCSANOW, &Rawtty); return; }
/* * Calculates the number of tasks in each state (running, sleeping, etc.). * Calculates the CPU time in each state (system, user, nice, etc). * Calculates percent cpu usage for each task. */ void do_stats(proc_t** p, float elapsed_time, int pass) { proc_t *this; int index, total_time, cpumap, i, n = 0; int sleeping = 0, stopped = 0, zombie = 0, running = 0; unsigned long system_ticks = 0, user_ticks = 0, nice_ticks = 0, idle_ticks; static int prev_count = 0; int stime, utime; /* start with one 4K page as a reasonable allocate size */ static int save_history_size = sizeof(struct save_hist) * 204; static struct save_hist *save_history; struct save_hist *New_save_hist; static int *s_ticks_o = NULL, *u_ticks_o = NULL, *n_ticks_o = NULL, *i_ticks_o = NULL; int s_ticks, u_ticks, n_ticks, i_ticks, t_ticks; char str[128]; FILE *file; if (!save_history) save_history = xcalloc(NULL, save_history_size); New_save_hist = xcalloc(NULL, save_history_size); if(s_ticks_o == NULL) { s_ticks_o = (int *)malloc(nr_cpu * sizeof(int)); u_ticks_o = (int *)malloc(nr_cpu * sizeof(int)); n_ticks_o = (int *)malloc(nr_cpu * sizeof(int)); i_ticks_o = (int *)malloc(nr_cpu * sizeof(int)); } idle_ticks = 1000 * nr_cpu; /* * Make a pass through the data to get stats. */ index = 0; while (p[n]->pid != -1) { this = p[n]; switch (this->state) { case 'S': case 'D': sleeping++; break; case 'T': stopped++; break; case 'Z': zombie++; break; case 'R': running++; break; default: /* Don't know how to handle this one. */ break; } /* * Calculate time in this process. Time is sum of user time * (utime) plus system time (stime). */ total_time = this->utime + this->stime; if (index * sizeof(struct save_hist) >= save_history_size) { save_history_size *= 2; save_history = xrealloc(save_history, save_history_size); New_save_hist = xrealloc(New_save_hist, save_history_size); } New_save_hist[index].ticks = total_time; New_save_hist[index].pid = this->pid; stime = this->stime; utime = this->utime; New_save_hist[index].stime = stime; New_save_hist[index].utime = utime; /* find matching entry from previous pass */ for (i = 0; i < prev_count; i++) { if (save_history[i].pid == this->pid) { total_time -= save_history[i].ticks; stime -= save_history[i].stime; utime -= save_history[i].utime; i = prev_count; } } /* * Calculate percent cpu time for this task. */ this->pcpu = (total_time * 10 * 100/Hertz) / elapsed_time; if (this->pcpu > 999) this->pcpu = 999; /* * Calculate time in idle, system, user and niced tasks. */ idle_ticks -= this->pcpu; system_ticks += stime; user_ticks += utime; if (this->nice > 0) nice_ticks += this->pcpu; /* * If in Sun-mode, adjust cpu percentage not only for * the cpu the process is running on, but for all cpu together. */ if(!Irixmode) { this->pcpu /= nr_cpu; } index++; n++; } if (idle_ticks < 0) idle_ticks = 0; system_ticks = (system_ticks * 10 * 100/Hertz) / elapsed_time; user_ticks = (user_ticks * 10 * 100/Hertz) / elapsed_time; /* * Display stats. */ if (pass > 0 && show_stats) { printf("%d processes: %d sleeping, %d running, %d zombie, " "%d stopped", n, sleeping, running, zombie, stopped); PUTP(top_clrtoeol); putchar('\n'); if (nr_cpu == 1 || CPU_states) { /* BEGIN EXPERIMENTAL CODE */ /* Throw out the calculation above... TODO: remove calculation. */ four_cpu_numbers(&user_ticks,&nice_ticks,&system_ticks,&idle_ticks); do{ unsigned long sum; sum = user_ticks+nice_ticks+system_ticks+idle_ticks; user_ticks = (user_ticks * 1000) / sum; system_ticks = (system_ticks * 1000) / sum; nice_ticks = (nice_ticks * 1000) / sum; idle_ticks = (idle_ticks * 1000) / sum; }while(0); /* END EXPERIMENTAL CODE */ if (Irixmode) { user_ticks *= nr_cpu; system_ticks *= nr_cpu; nice_ticks *= nr_cpu; idle_ticks *= nr_cpu; } printf("CPU states:" " %2ld.%ld%% user, %2ld.%ld%% system," " %2ld.%ld%% nice, %2ld.%ld%% idle", user_ticks / 10UL, user_ticks % 10UL, system_ticks / 10UL, system_ticks % 10UL, nice_ticks / 10UL, nice_ticks % 10UL, idle_ticks / 10UL, idle_ticks % 10UL); PUTP(top_clrtoeol); putchar('\n'); } /* * Calculate stats for all cpus */ if(nr_cpu > 1) { file = fopen("/proc/stat", "r"); if(file == NULL) { fprintf(stderr, "fopen failed on /proc/stat\n"); } else { /* Skip the total CPU states. */ if(fgets(str, 128, file) == NULL) { fprintf(stderr, "fgets failed on /proc/stat\n"); } else { for(i = 0; i < nr_cpu; i++) { if(fscanf(file, "cpu%*d %d %d %d %d\n", &u_ticks, &n_ticks, &s_ticks, &i_ticks) != 4) { fprintf(stderr, "fscanf failed on /proc/stat for cpu %d\n", i); break; } else { t_ticks = (u_ticks + s_ticks + i_ticks + n_ticks) - (u_ticks_o[i] + s_ticks_o[i] + i_ticks_o[i] + n_ticks_o[i]); if (Irixmode) cpumap=i; else cpumap=cpu_mapping[i]; printf ("CPU%d states: %2d.%-d%% user, %2d.%-d%% system," " %2d.%-d%% nice, %2d.%-d%% idle", cpumap, (u_ticks - u_ticks_o[i] + n_ticks - n_ticks_o[i]) * 100 / t_ticks, (u_ticks - u_ticks_o[i]) * 100 % t_ticks / 100, (s_ticks - s_ticks_o[i]) * 100 / t_ticks, (s_ticks - s_ticks_o[i]) * 100 % t_ticks / 100, (n_ticks - n_ticks_o[i]) * 100 / t_ticks, (n_ticks - n_ticks_o[i]) * 100 % t_ticks / 100, (i_ticks - i_ticks_o[i]) * 100 / t_ticks, (i_ticks - i_ticks_o[i]) * 100 % t_ticks / 100); s_ticks_o[i] = s_ticks; u_ticks_o[i] = u_ticks; n_ticks_o[i] = n_ticks; i_ticks_o[i] = i_ticks; PUTP (top_clrtoeol); putchar ('\n'); } } } fclose(file); } } } else { /* * During the first pass, get the information for the * different CPUs. */ if(nr_cpu > 1) { file = fopen("/proc/stat", "r"); if(file == NULL) { puts("open"); } if(fgets(str, 128, file) == NULL) { fprintf(stderr, "fgets failed on /proc/stat\n"); } for(i = 0; i < nr_cpu; i++) { if(fscanf(file, "cpu%*d %d %d %d %d\n", &u_ticks_o[i], &n_ticks_o[i], &s_ticks_o[i], &i_ticks_o[i]) != 4) { fprintf(stderr, "fscanf failed on /proc/stat for cpu %d\n", i); } } fclose(file); } } /* * Save this frame's information. */ for (i = 0; i < n; i++) { /* copy the relevant info for the next pass */ save_history[i].pid = New_save_hist[i].pid; save_history[i].ticks = New_save_hist[i].ticks; save_history[i].stime = New_save_hist[i].stime; save_history[i].utime = New_save_hist[i].utime; } free(New_save_hist); prev_count = n; qsort(p, n, sizeof(proc_t*), (void*)mult_lvl_cmp); }
/* * This is the real program! Read process info and display it. * One could differentiate options of readproctable2, perhaps it * would be useful to support the PROC_UID and PROC_TTY * as command line options. */ void show_procs(void) { static proc_t **p_table=NULL; static int proc_flags; int count; int ActualLines; float elapsed_time; unsigned int main_mem; static int first=0; if (first==0) { proc_flags=PROC_FILLMEM|PROC_FILLCMD|PROC_FILLUSR; if (monpids_index) proc_flags |= PROC_PID; p_table=readproctab2(proc_flags, p_table, monpids); elapsed_time = get_elapsed_time(); do_stats(p_table, elapsed_time, 0); sleep(1); first=1; } if (first && Batch) fputs("\n\n",stdout); /* Display the load averages. */ PUTP(ho); PUTP(md); if (show_loadav) { printf("%s", sprint_uptime()); PUTP(top_clrtoeol); putchar('\n'); } p_table=readproctab2(proc_flags, p_table, monpids); /* Immediately find out the elapsed time for the frame. */ elapsed_time = get_elapsed_time(); /* Display the system stats, calculate percent CPU time * and sort the list. */ do_stats(p_table, elapsed_time,1); /* Display the memory and swap space usage. */ main_mem = show_meminfo(); if (strlen(Header) + 2 > Cols) Header[Cols - 2] = 0; PUTP(mr); fputs(Header, stdout); PUTP(top_clrtoeol); PUTP(me); /* * Finally! Loop through to find the top task, and display it. * Lather, rinse, repeat. */ count = 0; ActualLines = 0; while ((ActualLines < Maxlines) && (p_table[count]->pid!=-1)) { int pmem; char stat; stat = p_table[count]->state; if ( (!Noidle || (stat != 'S' && stat != 'Z')) && ( (CurrUser[0] == '\0') || (!strcmp((char *)CurrUser,p_table[count]->euser) ) ) ) { /* * Show task info. */ pmem = p_table[count]->resident * 1000 / (main_mem / 4); show_task_info(p_table[count], pmem); if (!Batch) ActualLines++; } count++; } PUTP(top_clrtobot); PUTP(tgoto(cm, 0, header_lines - 2)); fflush(stdout); }
/* * Displays infos for a single task */ void show_task_info(proc_t *task, int pmem) { int i,j; unsigned int t; char *cmdptr; char tmp[2048], tmp2[2048] = "", tmp3[2048] = "", *p; for (i = 0; i < Numfields; i++) { tmp[0] = 0; switch (pflags[i]) { case P_PID: sprintf(tmp, "%5d ", task->pid); break; case P_PPID: sprintf(tmp, "%5d ", task->ppid); break; case P_EUID: sprintf(tmp, "%4d ", task->euid); break; case P_EUSER: sprintf(tmp, "%-8.8s ", task->euser); break; case P_PCPU: sprintf(tmp, "%4.1f ", (float)task->pcpu / 10); break; case P_LCPU: sprintf(tmp, "%2d ", task->lproc); break; case P_PMEM: sprintf(tmp, "%4.1f ", (float)pmem / 10); break; case P_TTY: { char outbuf[9]; dev_to_tty(outbuf, 8, task->tty, task->pid, ABBREV_DEV); sprintf(tmp, "%-8.8s ", outbuf); } break; case P_PRI: sprintf(tmp, "%3.3s ", scale_k(task->priority, 3, 0)); break; case P_NICE: sprintf(tmp, "%3.3s ", scale_k(task->nice, 3, 0)); break; case P_PAGEIN: sprintf(tmp, "%6.6s ", scale_k(task->maj_flt, 6, 0)); break; case P_TSIZ: sprintf(tmp, "%5.5s ", scale_k(((task->end_code - task->start_code) / 1024), 5, 1)); break; case P_DSIZ: sprintf(tmp, "%5.5s ", scale_k(((task->vsize - task->end_code) / 1024), 5, 1)); break; case P_SIZE: sprintf(tmp, "%5.5s ", scale_k((task->size << CL_pg_shift), 5, 1)); break; case P_TRS: sprintf(tmp, "%4.4s ", scale_k((task->trs << CL_pg_shift), 4, 1)); break; case P_SWAP: sprintf(tmp, "%4.4s ", scale_k(((task->size - task->resident) << CL_pg_shift), 4, 1)); break; case P_SHARE: sprintf(tmp, "%5.5s ", scale_k((task->share << CL_pg_shift), 5, 1)); break; case P_A: sprintf(tmp, "%3.3s ", "NYI"); break; case P_WP: sprintf(tmp, "%3.3s ", "NYI"); break; case P_DT: sprintf(tmp, "%3.3s ", scale_k(task->dt, 3, 0)); break; case P_RSS: /* resident not rss, it seems to be more correct. */ sprintf(tmp, "%4.4s ", scale_k((task->resident << CL_pg_shift), 4, 1)); break; case P_WCHAN: if (!CL_wchan_nout) sprintf(tmp, "%-9.9s ", wchan(task->wchan)); else sprintf(tmp, "%-9lx", task->wchan); break; case P_STAT: sprintf(tmp, "%-4.4s ", status(task)); break; case P_TIME: t = (task->utime + task->stime) / Hertz; if (Cumulative) t += (task->cutime + task->cstime) / Hertz; sprintf(tmp, "%6.6s ", scale_time(t,6)); break; case P_COMMAND: if (!show_cmd && task->cmdline && *(task->cmdline)) { j=0; while(((task->cmdline)[j] != NULL) && (strlen(tmp3)<1020)){ /* #if 0 */ /* This is useless? FIXME */ if (j > 0) strcat(tmp3, " "); /* #endif */ strncat(tmp3, (task->cmdline)[j], 1000); j++; } cmdptr = tmp3; } else { cmdptr = task->cmd; } if (strlen(cmdptr) > Maxcmd) cmdptr[Maxcmd - 1] = 0; sprintf(tmp, "%s", cmdptr); tmp3[0]=0; break; case P_FLAGS: sprintf(tmp, "%8lx ", task->flags); break; } strcat(tmp2, tmp); } if (strlen(tmp2) > Cols - 1) tmp2[Cols - 1] = 0; /* take care of cases like: perl -e 'foo bar foo bar foo # end of perl script' */ for (p=tmp2;*p;++p) if (!isgraph(*p)) *p=' '; printf("\n%s", tmp2); PUTP(top_clrtoeol); }
/* * Displays infos for a single task */ void show_task_info(proc_t *task, int pmem) { int i,j; unsigned int t; char *cmdptr; char tmp[2048], tmp2[2048] = "", tmp3[2048] = ""; for (i = 0; i < Numfields; i++) { tmp[0] = 0; switch (pflags[i]) { case P_PID: sprintf(tmp, "%5d ", task->pid); break; case P_PPID: sprintf(tmp, "%5d ", task->ppid); break; case P_UID: sprintf(tmp, "%4d ", task->uid); break; case P_USER: sprintf(tmp, "%-8.8s ", task->user); break; case P_PCPU: sprintf(tmp, "%2d.%1d ", task->pcpu / 10, task->pcpu % 10); break; case P_PMEM: sprintf(tmp, "%2d.%1d ", pmem / 10, pmem % 10); break; case P_TTY: sprintf(tmp, "%-3.3s ", task->ttyc); break; case P_PRI: sprintf(tmp, "%3d ", task->priority); break; case P_NICE: sprintf(tmp, "%3d ", task->nice); break; case P_PAGEIN: sprintf(tmp, "%6d ", task->maj_flt); break; case P_TSIZ: sprintf(tmp, "%5d ", (task->end_code - task->start_code) / 1024); break; case P_DSIZ: sprintf(tmp, "%5d ", (task->vsize - task->end_code) / 1024); break; case P_SIZE: sprintf(tmp, "%5d ", task->size << CL_pg_shift); break; case P_TRS: sprintf(tmp, "%4d ", task->trs << CL_pg_shift); break; case P_SWAP: sprintf(tmp, "%4d ", (task->size - task->resident) << CL_pg_shift); break; case P_SHARE: sprintf(tmp, "%5d ", task->share << CL_pg_shift); break; case P_A: sprintf(tmp, "%3.3s ", "NYI"); break; case P_WP: sprintf(tmp, "%3.3s ", "NYI"); break; case P_DT: sprintf(tmp, "%3d ", task->dt); break; case P_RSS: /* resident not rss, it seems to be more correct. */ sprintf(tmp, "%4d ", task->resident << CL_pg_shift); break; case P_WCHAN: if (!CL_wchan_nout) sprintf(tmp, "%-9.9s ", wchan(task->wchan)); else sprintf(tmp, "%-9x", task->wchan); break; case P_STAT: sprintf(tmp, "%-4.4s ", status(task)); break; case P_TIME: t = (task->utime + task->stime) / HZ; if (Cumulative) t += (task->cutime + task->cstime) / HZ; sprintf(tmp, "%3d:%02d ", t / 60, t % 60); break; case P_COMMAND: if (!show_cmd && task->cmdline && *(task->cmdline)) { j=0; while(((task->cmdline)[j] != NULL) && (strlen(tmp3)<1024)){ strcat(tmp3,(task->cmdline)[j]); j++; } cmdptr = tmp3; } else { cmdptr = task->cmd; } if (strlen(cmdptr) > Maxcmd) cmdptr[Maxcmd - 1] = 0; sprintf(tmp, "%s", cmdptr); tmp3[0]=0; break; case P_LTR: sprintf(tmp, "%4d ", task->lrs << CL_pg_shift); break; case P_FLAGS: sprintf(tmp, "%8x ", task->flags); break; } strcat(tmp2, tmp); } if (strlen(tmp2) > Cols - 1) tmp2[Cols - 1] = 0; printf("\n%s", tmp2); PUTP(top_clrtoeol); }
int main(int argc, char **argv) { /* For select(2). */ struct timeval tv; fd_set in; /* For parsing arguments. */ char *cp; /* The key read in. */ char c; get_options(); /* * Parse arguments. */ argv++; while (*argv) { cp = *argv++; while (*cp) { switch (*cp) { case 'd': if (cp[1]) { if (sscanf(++cp, "%f", &Sleeptime) != 1) { fprintf(stderr, PROGNAME ": Bad delay time `%s'\n", cp); exit(1); } goto breakargv; } else if (*argv) { /* last char in an argv, use next as arg */ if (sscanf(cp = *argv++, "%f", &Sleeptime) != 1) { fprintf(stderr, PROGNAME ": Bad delay time `%s'\n", cp); exit(1); } goto breakargv; } else { fprintf(stderr, "-d requires an argument\n"); exit(1); } break; case 'q': if (!getuid()) /* set priority to -10 in order to stay above kswapd */ if (setpriority(PRIO_PROCESS, getpid(), -10)) { /* We check this just for paranoia. It's not fatal, and shouldn't happen. */ perror(PROGNAME ": setpriority() failed"); } Sleeptime = 0; break; case 'c': show_cmd = !show_cmd; break; case 'S': Cumulative = 1; break; case 'i': Noidle = 1; break; case 's': Secure = 1; break; case '-': break; /* Just ignore it */ #if defined (SHOWFLAG) case '/': showall++; #endif default: fprintf(stderr, PROGNAME ": Unknown argument `%c'\n", *cp); exit(1); } cp++; } breakargv: } /* set to PCPU sorting */ register_sort_function( -1, (cmp_t)pcpu_sort); /* for correct handling of some fields, we have to do distinguish * between kernel versions */ set_linux_version(); /* get kernel symbol table, if needed */ if (!CL_wchan_nout) { if (open_psdb()) { CL_wchan_nout = 1; } else { psdbsucc = 1; } } setup_terminal(); window_size(); /* * calculate header size, length of cmdline field ... */ Numfields = make_header(); /* * Set up signal handlers. */ signal(SIGHUP, (void *) (int) end); signal(SIGINT, (void *) (int) end); signal(SIGQUIT, (void *) (int) end); signal(SIGTSTP, (void *) (int) stop); signal(SIGWINCH, (void *) (int) window_size); /* loop, collecting process info and sleeping */ while (1) { if (setjmp(redraw_jmp)) clear_screen(); /* display the tasks */ show_procs(); /* sleep & wait for keyboard input */ tv.tv_sec = Sleeptime; tv.tv_usec = (Sleeptime - (int) Sleeptime) * 1000000; FD_ZERO(&in); FD_SET(0, &in); if (select(16, &in, 0, 0, &tv) > 0 && read(0, &c, 1) == 1) do_key(c); } } /*####################################################################### *#### Signal handled routines: error_end, end, stop, window_size ### *#### Small utilities: make_header, getstr, getint, getfloat, getsig ### *####################################################################### */ /* * end when exiting with an error. */ void error_end(int rno) { if (psdbsucc) close_psdb(); ioctl(0, TCSETAF, &Savetty); PUTP(tgoto(cm, 0, Lines - 1)); fputs("\r\n", stdout); exit(rno); } /* * Normal end of execution. */ void end(void) { if (psdbsucc) close_psdb(); ioctl(0, TCSETAF, &Savetty); PUTP(tgoto(cm, 0, Lines - 1)); fputs("\r\n", stdout); exit(0); } /* * SIGTSTP catcher. */ void stop(void) { /* Reset terminal. */ if (psdbsucc) close_psdb(); ioctl(0, TCSETAF, &Savetty); PUTP(tgoto(cm, 0, Lines - 3)); fflush(stdout); raise(SIGTSTP); /* Later... */ ioctl(0, TCSETAF, &Rawtty); signal(SIGTSTP, (void *) (int) stop); longjmp(redraw_jmp, 1); } /* * Reads the window size and clear the window. This is called on setup, * and also catches SIGWINCHs, and adjusts Maxlines. Basically, this is * the central place for window size stuff. */ void window_size(void) { struct winsize ws; if (ioctl(1, TIOCGWINSZ, &ws) != -1) { Cols = ws.ws_col; Lines = ws.ws_row; } else { Cols = tgetnum("co"); Lines = tgetnum("li"); } clear_screen(); } /* * this adjusts the lines needed for the header to the current value */ int make_header(void) { int i, j; j = 0; for (i = 0; i < strlen(Fields); i++) { if (isupper(Fields[i])) { pflags[j++] = Fields[i] - 'A'; } } strcpy(Header, ""); for (i = 0; i < j; i++) strcat(Header, headers[pflags[i]]); /* readjust window size ... */ Maxcmd = Cols - strlen(Header) + 7; Maxlines = Display_procs ? Display_procs : Lines - header_lines; if (Maxlines > Lines - header_lines) Maxlines = Lines - header_lines; return (j); }
/* * Calculates the number of tasks in each state (running, sleeping, etc.). * Calculates the CPU time in each state (system, user, nice, etc). * Calculates percent cpu usage for each task. */ void do_stats(proc_t** p, float elapsed_time, int pass) { proc_t *this; int index, total_time, i, n = 0; int sleeping = 0, stopped = 0, zombie = 0, running = 0; int system_ticks = 0, user_ticks = 0, nice_ticks = 0, idle_ticks = 1000; static int prev_count = 0; int stime, utime; static struct save_hist save_history[NR_TASKS]; struct save_hist New_save_hist[NR_TASKS]; /* * Make a pass through the data to get stats. */ index = 0; while (p[n]->pid != -1) { this = p[n]; switch (this->state) { case 'S': case 'D': sleeping++; break; case 'T': stopped++; break; case 'Z': zombie++; break; case 'R': running++; break; default: /* Don't know how to handle this one. */ break; } /* * Calculate time in this process. Time is sum of user time * (utime) plus system time (stime). */ total_time = this->utime + this->stime; New_save_hist[index].ticks = total_time; New_save_hist[index].pid = this->pid; stime = this->stime; utime = this->utime; New_save_hist[index].stime = stime; New_save_hist[index].utime = utime; /* find matching entry from previous pass */ i = 0; while (i < prev_count) { if (save_history[i].pid == this->pid) { total_time -= save_history[i].ticks; stime -= save_history[i].stime; utime -= save_history[i].utime; i = NR_TASKS; } i++; } /* * Calculate percent cpu time for this task. */ this->pcpu = (total_time * 10 * 100/HZ) / elapsed_time; if (this->pcpu > 999) this->pcpu = 999; /* * Calculate time in idle, system, user and niced tasks. */ idle_ticks -= this->pcpu; system_ticks += stime; user_ticks += utime; if (this->priority > 0) nice_ticks += this->pcpu; index++; n++; if (n > NR_TASKS) { printf(PROGNAME ": Help! Too many tasks!\n"); end(); } } if (idle_ticks < 0) idle_ticks = 0; system_ticks = (system_ticks * 10 * 100/HZ) / elapsed_time; user_ticks = (user_ticks * 10 * 100/HZ) / elapsed_time; /* * Display stats. */ if (pass > 0 && show_stats) { printf("%d processes: %d sleeping, %d running, %d zombie, " "%d stopped", n, sleeping, running, zombie, stopped); PUTP(top_clrtoeol); putchar('\n'); printf("CPU states: %2d.%d%% user, %2d.%d%% system," " %2d.%d%% nice, %2d.%d%% idle", user_ticks / 10, user_ticks % 10, system_ticks / 10, system_ticks % 10, nice_ticks / 10, nice_ticks % 10, idle_ticks / 10, idle_ticks % 10); PUTP(top_clrtoeol); putchar('\n'); } /* * Save this frame's information. */ for (i = 0; i < n; i++) { /* copy the relevant info for the next pass */ save_history[i].pid = New_save_hist[i].pid; save_history[i].ticks = New_save_hist[i].ticks; save_history[i].stime = New_save_hist[i].stime; save_history[i].utime = New_save_hist[i].utime; } prev_count = n; qsort(p, n, sizeof(proc_t*), (void*)mult_lvl_cmp); }