331 lines
9.0 KiB
C
331 lines
9.0 KiB
C
#include <ctype.h>
|
|
#include <dirent.h>
|
|
#include <fcntl.h>
|
|
#include <pwd.h>
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
|
|
struct thread_info {
|
|
int pid;
|
|
int tid;
|
|
char name[64];
|
|
unsigned long long exec_time;
|
|
unsigned long long delay_time;
|
|
unsigned long long 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 %llu",
|
|
&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 %5llu %5u.%0*u %5u.%0*u %7llu %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: %zu, Threads %zu\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;
|
|
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 %5llu %5u.%0*u %5u.%0*u %7llu %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;
|
|
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;
|
|
}
|