diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2008-10-21 07:00:00 -0700 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2008-10-21 07:00:00 -0700 |
commit | 4f6e8d7a00cbeda1e70cc15be9c4af1018bdad53 (patch) | |
tree | 54fd1b2695a591d2306d41264df67c53077b752c /toolbox/schedtop.c | |
download | system_core-4f6e8d7a00cbeda1e70cc15be9c4af1018bdad53.tar.gz system_core-4f6e8d7a00cbeda1e70cc15be9c4af1018bdad53.tar.bz2 system_core-4f6e8d7a00cbeda1e70cc15be9c4af1018bdad53.zip |
Initial Contribution
Diffstat (limited to 'toolbox/schedtop.c')
-rw-r--r-- | toolbox/schedtop.c | 335 |
1 files changed, 335 insertions, 0 deletions
diff --git a/toolbox/schedtop.c b/toolbox/schedtop.c new file mode 100644 index 000000000..c0e01412d --- /dev/null +++ b/toolbox/schedtop.c @@ -0,0 +1,335 @@ +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <fcntl.h> + +#include <string.h> + +#include <sys/stat.h> +#include <sys/types.h> +#include <dirent.h> +#include <signal.h> + +#include <pwd.h> + +struct thread_info { + int pid; + int tid; + char name[64]; + uint64_t exec_time; + uint64_t delay_time; + uint32_t run_count; +}; + +struct thread_table { + size_t allocated; + size_t active; + struct thread_info *data; +}; + +enum { + FLAG_BATCH = 1U << 0, + FLAG_HIDE_IDLE = 1U << 1, + FLAG_SHOW_THREADS = 1U << 2, + FLAG_USE_ALTERNATE_SCREEN = 1U << 3, +}; + +static int time_dp = 9; +static int time_div = 1; +#define NS_TO_S_D(ns) \ + (uint32_t)((ns) / 1000000000), time_dp, ((uint32_t)((ns) % 1000000000) / time_div) + +struct thread_table processes; +struct thread_table last_processes; +struct thread_table threads; +struct thread_table last_threads; + +static void grow_table(struct thread_table *table) +{ + size_t size = table->allocated; + struct thread_info *new_table; + if (size < 128) + size = 128; + else + size *= 2; + + new_table = realloc(table->data, size * sizeof(*table->data)); + if (new_table == NULL) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + table->data = new_table; + table->allocated = size; +} + +static struct thread_info *get_item(struct thread_table *table) +{ + if (table->active >= table->allocated) + grow_table(table); + return table->data + table->active; +} + +static void commit_item(struct thread_table *table) +{ + table->active++; +} + +static int read_line(char *line, size_t line_size) +{ + int fd; + int len; + fd = open(line, O_RDONLY); + if(fd == 0) + return -1; + len = read(fd, line, line_size - 1); + close(fd); + if (len <= 0) + return -1; + line[len] = '\0'; + return 0; +} + +static void add_thread(int pid, int tid, struct thread_info *proc_info) +{ + char line[1024]; + char *name, *name_end; + size_t name_len; + struct thread_info *info; + if(tid == 0) + info = get_item(&processes); + else + info = get_item(&threads); + info->pid = pid; + info->tid = tid; + + if(tid) + sprintf(line, "/proc/%d/task/%d/schedstat", pid, tid); + else + sprintf(line, "/proc/%d/schedstat", pid); + if (read_line(line, sizeof(line))) + return; + if(sscanf(line, "%llu %llu %u", &info->exec_time, &info->delay_time, &info->run_count) != 3) + return; + if (proc_info) { + proc_info->exec_time += info->exec_time; + proc_info->delay_time += info->delay_time; + proc_info->run_count += info->run_count; + } + + name = NULL; + if (!tid) { + sprintf(line, "/proc/%d/cmdline", pid); + if (read_line(line, sizeof(line)) == 0 && line[0]) { + name = line; + name_len = strlen(name); + } + } + if (!name) { + if (tid) + sprintf(line, "/proc/%d/task/%d/stat", pid, tid); + else + sprintf(line, "/proc/%d/stat", pid); + if (read_line(line, sizeof(line))) + return; + name = strchr(line, '('); + if (name == NULL) + return; + name_end = strchr(name, ')'); + if (name_end == NULL) + return; + name++; + name_len = name_end - name; + } + if (name_len >= sizeof(info->name)) + name_len = sizeof(info->name) - 1; + memcpy(info->name, name, name_len); + info->name[name_len] = '\0'; + if(tid == 0) + commit_item(&processes); + else + commit_item(&threads); +} + +static void add_threads(int pid, struct thread_info *proc_info) +{ + char path[1024]; + DIR *d; + struct dirent *de; + sprintf(path, "/proc/%d/task", pid); + d = opendir(path); + if(d == 0) return; + while((de = readdir(d)) != 0){ + if(isdigit(de->d_name[0])){ + int tid = atoi(de->d_name); + add_thread(pid, tid, proc_info); + } + } + closedir(d); +} + +static void print_threads(int pid, uint32_t flags) +{ + size_t i, j; + for (i = 0; i < last_threads.active; i++) { + int epid = last_threads.data[i].pid; + int tid = last_threads.data[i].tid; + if (epid != pid) + continue; + for (j = 0; j < threads.active; j++) + if (tid == threads.data[j].tid) + break; + if (j == threads.active) + printf(" %5u died\n", tid); + else if (!(flags & FLAG_HIDE_IDLE) || threads.data[j].run_count - last_threads.data[i].run_count) + printf(" %5u %2u.%0*u %2u.%0*u %5u %5u.%0*u %5u.%0*u %7u %s\n", tid, + NS_TO_S_D(threads.data[j].exec_time - last_threads.data[i].exec_time), + NS_TO_S_D(threads.data[j].delay_time - last_threads.data[i].delay_time), + threads.data[j].run_count - last_threads.data[i].run_count, + NS_TO_S_D(threads.data[j].exec_time), NS_TO_S_D(threads.data[j].delay_time), + threads.data[j].run_count, threads.data[j].name); + } +} + +static void update_table(DIR *d, uint32_t flags) +{ + size_t i, j; + struct dirent *de; + + rewinddir(d); + while((de = readdir(d)) != 0){ + if(isdigit(de->d_name[0])){ + int pid = atoi(de->d_name); + struct thread_info *proc_info; + add_thread(pid, 0, NULL); + proc_info = &processes.data[processes.active - 1]; + proc_info->exec_time = 0; + proc_info->delay_time = 0; + proc_info->run_count = 0; + add_threads(pid, proc_info); + } + } + if (!(flags & FLAG_BATCH)) + printf("\e[H\e[0J"); + printf("Processes: %d, Threads %d\n", processes.active, threads.active); + switch (time_dp) { + case 3: + printf(" TID --- SINCE LAST ---- ---------- TOTAL ----------\n"); + printf(" PID EXEC_T DELAY SCHED EXEC_TIME DELAY_TIM SCHED NAME\n"); + break; + case 6: + printf(" TID ------ SINCE LAST ------- ------------ TOTAL -----------\n"); + printf(" PID EXEC_TIME DELAY_TIM SCHED EXEC_TIME DELAY_TIME SCHED NAME\n"); + break; + default: + printf(" TID -------- SINCE LAST -------- ------------- TOTAL -------------\n"); + printf(" PID EXEC_TIME DELAY_TIME SCHED EXEC_TIME DELAY_TIME SCHED NAME\n"); + break; + } + for (i = 0; i < last_processes.active; i++) { + int pid = last_processes.data[i].pid; + int tid = last_processes.data[i].tid; + for (j = 0; j < processes.active; j++) + if (pid == processes.data[j].pid) + break; + if (j == processes.active) + printf("%5u died\n", pid); + else if (!(flags & FLAG_HIDE_IDLE) || processes.data[j].run_count - last_processes.data[i].run_count) { + printf("%5u %2u.%0*u %2u.%0*u %5u %5u.%0*u %5u.%0*u %7u %s\n", pid, + NS_TO_S_D(processes.data[j].exec_time - last_processes.data[i].exec_time), + NS_TO_S_D(processes.data[j].delay_time - last_processes.data[i].delay_time), + processes.data[j].run_count - last_processes.data[i].run_count, + NS_TO_S_D(processes.data[j].exec_time), NS_TO_S_D(processes.data[j].delay_time), + processes.data[j].run_count, processes.data[j].name); + if (flags & FLAG_SHOW_THREADS) + print_threads(pid, flags); + } + } + + { + struct thread_table tmp; + tmp = last_processes; + last_processes = processes; + processes = tmp; + processes.active = 0; + tmp = last_threads; + last_threads = threads; + threads = tmp; + threads.active = 0; + } +} + +void +sig_abort(int signum) +{ + printf("\e[?47l"); + exit(0); +} + + +int schedtop_main(int argc, char **argv) +{ + int c; + DIR *d; + struct dirent *de; + char *namefilter = 0; + int pidfilter = 0; + uint32_t flags = 0; + int delay = 3000000; + float delay_f; + + while(1) { + c = getopt(argc, argv, "d:ibtamun"); + if (c == EOF) + break; + switch (c) { + case 'd': + delay_f = atof(optarg); + delay = delay_f * 1000000; + break; + case 'b': + flags |= FLAG_BATCH; + break; + case 'i': + flags |= FLAG_HIDE_IDLE; + break; + case 't': + flags |= FLAG_SHOW_THREADS; + break; + case 'a': + flags |= FLAG_USE_ALTERNATE_SCREEN; + break; + case 'm': + time_dp = 3; + time_div = 1000000; + break; + case 'u': + time_dp = 6; + time_div = 1000; + break; + case 'n': + time_dp = 9; + time_div = 1; + break; + } + } + + d = opendir("/proc"); + if(d == 0) return -1; + + if (!(flags & FLAG_BATCH)) { + if(flags & FLAG_USE_ALTERNATE_SCREEN) { + signal(SIGINT, sig_abort); + signal(SIGPIPE, sig_abort); + signal(SIGTERM, sig_abort); + printf("\e7\e[?47h"); + } + printf("\e[2J"); + } + while (1) { + update_table(d, flags); + usleep(delay); + } + closedir(d); + return 0; +} + |