ACLOCAL_AMFLAGS = -I m4
-SUBDIRS = lttngtop
+SUBDIRS = src
AC_CONFIG_FILES([
Makefile
- lttngtop/Makefile
+ src/Makefile
])
AC_OUTPUT
+++ /dev/null
-AM_CFLAGS = $(PACKAGE_CFLAGS)
-
-bin_PROGRAMS = lttngtop
-
-lttngtop_SOURCES = \
- lttngtop.c \
- common.c \
- cursesdisplay.c \
- cputop.c \
- iostreamtop.c
-
-lttngtop_LDADD = -lbabeltrace -lctf
+++ /dev/null
-/*
- * Copyright (C) 2011 Julien Desfossez
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License Version 2 as
- * published by the Free Software Foundation;
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
- * MA 02111-1307, USA.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include "common.h"
-
-struct processtop *find_process_tid(struct lttngtop *ctx, int tid, char *comm)
-{
- gint i;
- struct processtop *tmp;
-
- for (i = 0; i < ctx->process_table->len; i++) {
- tmp = g_ptr_array_index(ctx->process_table, i);
- if (tmp && tmp->tid == tid)
- return tmp;
- }
- return NULL;
-}
-
-struct processtop* add_proc(struct lttngtop *ctx, int tid, char *comm,
- unsigned long timestamp)
-{
- struct processtop *newproc;
-
- /* if the PID already exists, we just rename the process */
- /* FIXME : need to integrate with clone/fork/exit to be accurate */
- newproc = find_process_tid(ctx, tid, comm);
- if (!newproc) {
- newproc = malloc(sizeof(struct processtop));
- memset(newproc, 0, sizeof(struct processtop));
- newproc->tid = tid;
- newproc->birth = timestamp;
- newproc->process_files_table = g_ptr_array_new();
- newproc->threads = g_ptr_array_new();
- newproc->perf = g_hash_table_new(g_direct_hash, g_direct_equal);
- newproc->iostream = malloc(sizeof(struct iostream));
- newproc->iostream->ret_read = 0;
- newproc->iostream->ret_write = 0;
- newproc->iostream->ret_total = 0;
- newproc->iostream->syscall_info = NULL;
- g_ptr_array_add(ctx->process_table, newproc);
- }
- newproc->comm = strdup(comm);
-
- return newproc;
-}
-
-struct processtop* update_proc(struct processtop* proc, int pid, int tid,
- int ppid, char *comm)
-{
- if (proc) {
- proc->pid = pid;
- proc->tid = tid;
- proc->ppid = ppid;
- if (strcmp(proc->comm, comm) != 0) {
- free(proc->comm);
- proc->comm = strdup(comm);
- }
- }
- return proc;
-}
-
-/*
- * This function just sets the time of death of a process.
- * When we rotate the cputime we remove it from the process list.
- */
-void death_proc(struct lttngtop *ctx, int tid, char *comm,
- unsigned long timestamp)
-{
- struct processtop *tmp;
- tmp = find_process_tid(ctx, tid, comm);
- if (tmp && strcmp(tmp->comm, comm) == 0)
- tmp->death = timestamp;
-}
-
-struct processtop* get_proc(struct lttngtop *ctx, int tid, char *comm,
- unsigned long timestamp)
-{
- struct processtop *tmp;
- tmp = find_process_tid(ctx, tid, comm);
- if (tmp && strcmp(tmp->comm, comm) == 0)
- return tmp;
- return add_proc(ctx, tid, comm, timestamp);
-}
-
-void add_thread(struct processtop *parent, struct processtop *thread)
-{
- gint i;
- struct processtop *tmp;
-
- for (i = 0; i < parent->threads->len; i++) {
- tmp = g_ptr_array_index(parent->threads, i);
- if (tmp == thread)
- return;
- }
- g_ptr_array_add(parent->threads, thread);
-}
-
-struct cputime* add_cpu(int cpu)
-{
- struct cputime *newcpu;
-
- newcpu = malloc(sizeof(struct cputime));
- newcpu->id = cpu;
- newcpu->current_task = NULL;
- newcpu->perf = g_hash_table_new(g_direct_hash, g_direct_equal);
-
- g_ptr_array_add(lttngtop.cpu_table, newcpu);
-
- return newcpu;
-}
-struct cputime* get_cpu(int cpu)
-{
- gint i;
- struct cputime *tmp;
-
- for (i = 0; i < lttngtop.cpu_table->len; i++) {
- tmp = g_ptr_array_index(lttngtop.cpu_table, i);
- if (tmp->id == cpu)
- return tmp;
- }
-
- return add_cpu(cpu);
-}
-
-/*
- * At the end of a sampling period, we need to display the cpu time for each
- * process and to reset it to zero for the next period
- */
-void rotate_cputime(unsigned long end)
-{
- gint i;
- struct cputime *tmp;
- unsigned long elapsed;
-
- for (i = 0; i < lttngtop.cpu_table->len; i++) {
- tmp = g_ptr_array_index(lttngtop.cpu_table, i);
- elapsed = end - tmp->task_start;
- if (tmp->current_task) {
- tmp->current_task->totalcpunsec += elapsed;
- tmp->current_task->threadstotalcpunsec += elapsed;
- if (tmp->current_task->pid != tmp->current_task->tid &&
- tmp->current_task->threadparent) {
- tmp->current_task->threadparent->threadstotalcpunsec += elapsed;
- }
- }
- tmp->task_start = end;
- }
-}
-
-void reset_perf_counter(gpointer key, gpointer value, gpointer user_data)
-{
- ((struct perfcounter*) value)->count = 0;
-}
-
-void copy_perf_counter(gpointer key, gpointer value, gpointer new_table)
-{
- struct perfcounter *newperf;
-
- newperf = malloc(sizeof(struct perfcounter));
- newperf->count = ((struct perfcounter *) value)->count;
- newperf->visible = ((struct perfcounter *) value)->visible;
- newperf->sort = ((struct perfcounter *) value)->sort;
- g_hash_table_insert((GHashTable *) new_table, key, newperf);
-}
-
-void rotate_perfcounter() {
- int i;
- struct processtop *tmp;
- for (i = 0; i < lttngtop.process_table->len; i++) {
- tmp = g_ptr_array_index(lttngtop.process_table, i);
- g_hash_table_foreach(tmp->perf, reset_perf_counter, NULL);
- }
-}
-
-void cleanup_processtop()
-{
- gint i;
- struct processtop *tmp;
-
- for (i = 0; i < lttngtop.process_table->len; i++) {
- tmp = g_ptr_array_index(lttngtop.process_table, i);
- tmp->totalcpunsec = 0;
- tmp->threadstotalcpunsec = 0;
- tmp->iostream->ret_read = 0;
- tmp->iostream->ret_write = 0;
- }
-}
-
-struct lttngtop* get_copy_lttngtop(unsigned long start, unsigned long end)
-{
- gint i, j;
- unsigned long time;
- struct lttngtop *dst;
- struct processtop *tmp, *tmp2, *new;
- struct cputime *tmpcpu, *newcpu;
- struct files *tmpfile, *newfile;
-
- dst = malloc(sizeof(struct lttngtop));
- dst = memset(dst, 0, sizeof(struct lttngtop));
- dst->start = start;
- dst->end = end;
- dst->process_table = g_ptr_array_new();
- dst->files_table = g_ptr_array_new();
- dst->cpu_table = g_ptr_array_new();
- dst->perf_list = g_hash_table_new(g_direct_hash, g_direct_equal);
-
- rotate_cputime(end);
-
- g_hash_table_foreach(lttngtop.perf_list, copy_perf_counter, dst->perf_list);
- for (i = 0; i < lttngtop.process_table->len; i++) {
- tmp = g_ptr_array_index(lttngtop.process_table, i);
- new = malloc(sizeof(struct processtop));
-
- memcpy(new, tmp, sizeof(struct processtop));
- new->threads = g_ptr_array_new();
- new->comm = strdup(tmp->comm);
- new->process_files_table = g_ptr_array_new();
- new->perf = g_hash_table_new(g_direct_hash, g_direct_equal);
- g_hash_table_foreach(tmp->perf, copy_perf_counter, new->perf);
-
- new->iostream = malloc(sizeof(struct iostream));
- memcpy(new->iostream, tmp->iostream, sizeof(struct iostream));
- /* compute the stream speed */
- if (end - start != 0)
- {
- time = (end - start)/NSEC_PER_SEC;
- new->iostream->ret_read = new->iostream->ret_read/(time);
- new->iostream->ret_write = new->iostream->ret_write/(time);
- }
-
- for (j = 0; j < tmp->process_files_table->len; j++) {
- tmpfile = g_ptr_array_index(tmp->process_files_table, j);
- newfile = malloc(sizeof(struct files));
-
- memcpy(newfile, tmpfile, sizeof(struct files));
-
- newfile->name = strdup(tmpfile->name);
- newfile->ref = new;
-
- g_ptr_array_add(new->process_files_table, newfile);
- g_ptr_array_add(dst->files_table, newfile);
-
- /*
- * if the process died during the last period, we remove all
- * files associated with if after the copy
- */
- if (tmp->death > 0 && tmp->death < end) {
- g_ptr_array_remove(tmp->process_files_table, tmpfile);
- free(tmpfile);
- }
- }
- g_ptr_array_add(dst->process_table, new);
-
- /*
- * if the process died during the last period, we remove it from
- * the current process list after the copy
- */
- if (tmp->death > 0 && tmp->death < end) {
- g_ptr_array_remove(lttngtop.process_table, tmp);
- g_ptr_array_free(tmp->threads, TRUE);
- free(tmp->comm);
- g_ptr_array_free(tmp->process_files_table, TRUE);
- g_hash_table_destroy(tmp->perf);
- free(tmp);
- }
- }
- rotate_perfcounter();
-
- for (i = 0; i < lttngtop.cpu_table->len; i++) {
- tmpcpu = g_ptr_array_index(lttngtop.cpu_table, i);
- newcpu = malloc(sizeof(struct cputime));
- memcpy(newcpu, tmpcpu, sizeof(struct cputime));
- newcpu->perf = g_hash_table_new(g_direct_hash, g_direct_equal);
- g_hash_table_foreach(tmpcpu->perf, copy_perf_counter, newcpu->perf);
- /*
- * note : we don't care about the current process pointer in the copy
- * so the reference is invalid after the memcpy
- */
- g_ptr_array_add(dst->cpu_table, newcpu);
- }
- /* create the threads index if required */
- for (i = 0; i < dst->process_table->len; i++) {
- tmp = g_ptr_array_index(dst->process_table, i);
- if (tmp->pid == tmp->tid) {
- for (j = 0; j < dst->process_table->len; j++) {
- tmp2 = g_ptr_array_index(dst->process_table, j);
- if (tmp2->pid == tmp->pid) {
- tmp2->threadparent = tmp;
- g_ptr_array_add(tmp->threads, tmp2);
- }
- }
- }
- }
-
- // update_global_stats(dst);
- cleanup_processtop();
-
- return dst;
-}
-
+++ /dev/null
-/*
- * Copyright (C) 2011 Julien Desfossez
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License Version 2 as
- * published by the Free Software Foundation;
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
- * MA 02111-1307, USA.
- */
-
-#ifndef _COMMON_H
-#define _COMMON_H
-
-#include <semaphore.h>
-#include <babeltrace/ctf/events.h>
-#include "lttngtoptypes.h"
-#include "cputop.h"
-
-#define NSEC_PER_USEC 1000
-#define NSEC_PER_SEC 1000000000L
-
-sem_t goodtodisplay, goodtoupdate, timer, pause_sem, end_trace_sem, bootstrap;
-
-GPtrArray *copies; /* struct lttngtop */
-pthread_mutex_t perf_list_mutex;
-
-struct lttngtop *data;
-
-struct processtop *find_process_tid(struct lttngtop *ctx, int pid, char *comm);
-struct processtop* add_proc(struct lttngtop *ctx, int pid, char *comm,
- unsigned long timestamp);
-struct processtop* update_proc(struct processtop* proc, int pid, int tid,
- int ppid, char *comm);
-void add_thread(struct processtop *parent, struct processtop *thread);
-struct processtop* get_proc(struct lttngtop *ctx, int tid, char *comm,
- unsigned long timestamp);
-void death_proc(struct lttngtop *ctx, int tid, char *comm,
- unsigned long timestamp);
-struct cputime* add_cpu(int cpu);
-struct cputime* get_cpu(int cpu);
-struct lttngtop* get_copy_lttngtop(unsigned long start, unsigned long end);
-struct perfcounter *add_perf_counter(GPtrArray *perf, GQuark quark,
- unsigned long count);
-struct perfcounter *get_perf_counter(const char *name, struct processtop *proc,
- struct cputime *cpu);
-
-#endif /* _COMMON_H */
+++ /dev/null
-/*
- * Copyright (C) 2011 Julien Desfossez
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License Version 2 as
- * published by the Free Software Foundation;
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
- * MA 02111-1307, USA.
- */
-
-#include <babeltrace/babeltrace.h>
-
-#include "lttngtoptypes.h"
-#include "common.h"
-#include "cputop.h"
-
-void update_cputop_data(unsigned long timestamp, int64_t cpu, int prev_pid,
- int next_pid, char *prev_comm, char *next_comm)
-{
- struct cputime *tmpcpu;
- unsigned long elapsed;
-
- tmpcpu = get_cpu(cpu);
-
- if (tmpcpu->current_task && tmpcpu->current_task->pid == prev_pid) {
- elapsed = timestamp - tmpcpu->task_start;
- tmpcpu->current_task->totalcpunsec += elapsed;
- tmpcpu->current_task->threadstotalcpunsec += elapsed;
- if (tmpcpu->current_task->pid != tmpcpu->current_task->tid)
- tmpcpu->current_task->threadparent->threadstotalcpunsec += elapsed;
- }
-
- if (next_pid != 0)
- tmpcpu->current_task = get_proc(<tngtop, next_pid, next_comm, timestamp);
- else
- tmpcpu->current_task = NULL;
-
- tmpcpu->task_start = timestamp;
-}
-
-enum bt_cb_ret handle_sched_switch(struct bt_ctf_event *call_data,
- void *private_data)
-{
- struct definition *scope;
- unsigned long timestamp;
- uint64_t cpu_id;
- char *prev_comm, *next_comm;
- int prev_tid, next_tid;
-
- timestamp = bt_ctf_get_timestamp(call_data);
- if (timestamp == -1ULL)
- goto error;
-
- scope = bt_ctf_get_top_level_scope(call_data,
- BT_EVENT_FIELDS);
- prev_comm = bt_ctf_get_char_array(bt_ctf_get_field(call_data,
- scope, "_prev_comm"));
- if (bt_ctf_field_get_error()) {
- fprintf(stderr, "Missing prev_comm context info\n");
- goto error;
- }
-
- next_comm = bt_ctf_get_char_array(bt_ctf_get_field(call_data,
- scope, "_next_comm"));
- if (bt_ctf_field_get_error()) {
- fprintf(stderr, "Missing next_comm context info\n");
- goto error;
- }
-
- prev_tid = bt_ctf_get_int64(bt_ctf_get_field(call_data,
- scope, "_prev_tid"));
- if (bt_ctf_field_get_error()) {
- fprintf(stderr, "Missing prev_tid context info\n");
- goto error;
- }
-
- next_tid = bt_ctf_get_int64(bt_ctf_get_field(call_data,
- scope, "_next_tid"));
- if (bt_ctf_field_get_error()) {
- fprintf(stderr, "Missing next_tid context info\n");
- goto error;
- }
-
- scope = bt_ctf_get_top_level_scope(call_data,
- BT_STREAM_PACKET_CONTEXT);
- cpu_id = bt_ctf_get_uint64(bt_ctf_get_field(call_data,
- scope, "cpu_id"));
- if (bt_ctf_field_get_error()) {
- fprintf(stderr, "Missing cpu_id context info\n");
- goto error;
- }
-
- update_cputop_data(timestamp, cpu_id, prev_tid, next_tid,
- prev_comm, next_comm);
-
- return BT_CB_OK;
-
-error:
- return BT_CB_ERROR_STOP;
-}
-
-enum bt_cb_ret handle_sched_process_free(struct bt_ctf_event *call_data,
- void *private_data)
-{
- struct definition *scope;
- unsigned long timestamp;
- char *comm;
- int tid;
-
- timestamp = bt_ctf_get_timestamp(call_data);
- if (timestamp == -1ULL)
- goto error;
-
- scope = bt_ctf_get_top_level_scope(call_data,
- BT_EVENT_FIELDS);
- comm = bt_ctf_get_char_array(bt_ctf_get_field(call_data,
- scope, "_comm"));
- if (bt_ctf_field_get_error()) {
- fprintf(stderr, "Missing procname context info\n");
- goto error;
- }
-
- tid = bt_ctf_get_int64(bt_ctf_get_field(call_data,
- scope, "_tid"));
- if (bt_ctf_field_get_error()) {
- fprintf(stderr, "Missing tid context info\n");
- goto error;
- }
-
- death_proc(<tngtop, tid, comm, timestamp);
-
- return BT_CB_OK;
-
-error:
- return BT_CB_ERROR_STOP;
-
-}
-
+++ /dev/null
-/*
- * Copyright (C) 2011 Julien Desfossez
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License Version 2 as
- * published by the Free Software Foundation;
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
- * MA 02111-1307, USA.
- */
-
-#ifndef _LTTNGTOP_H
-#define _LTTNGTOP_H
-
-#include <babeltrace/babeltrace.h>
-#include <babeltrace/ctf/callbacks.h>
-#include <inttypes.h>
-#include <glib.h>
-
-enum bt_cb_ret handle_sched_switch(struct bt_ctf_event *hook_data,
- void *call_data);
-
-enum bt_cb_ret handle_sched_process_free(struct bt_ctf_event *call_data,
- void *private_data);
-
-#endif /* _LTTNGTOP_H */
+++ /dev/null
-/*
- * Copyright (C) 2011 Julien Desfossez
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License Version 2 as
- * published by the Free Software Foundation;
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
- * MA 02111-1307, USA.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <signal.h>
-#include <string.h>
-#include <ncurses.h>
-#include <panel.h>
-#include <pthread.h>
-#include <semaphore.h>
-
-#include "cursesdisplay.h"
-#include "lttngtoptypes.h"
-#include "common.h"
-
-#define DEFAULT_DELAY 15
-#define MAX_LINE_LENGTH 50
-#define MAX_LOG_LINES 4
-
-/* to prevent concurrent updates of the different windows */
-sem_t update_display_sem;
-
-char *termtype;
-WINDOW *footer, *header, *center, *status;
-WINDOW *perf_panel_window = NULL;
-PANEL *perf_panel, *main_panel;
-
-int perf_panel_visible = 0;
-int perf_line_selected = 0;
-
-int last_display_index, currently_displayed_index;
-
-struct processtop *selected_process = NULL;
-int selected_tid;
-char *selected_comm;
-int selected_ret;
-
-int selected_line = 0; /* select bar position */
-int selected_in_list = 0; /* selection relative to the whole list */
-int list_offset = 0; /* first index in the list to display (scroll) */
-int nb_log_lines = 0;
-char log_lines[MAX_LINE_LENGTH * MAX_LOG_LINES + MAX_LOG_LINES];
-
-int max_elements = 80;
-
-int toggle_threads = -1;
-int toggle_pause = -1;
-int toggle_tree = -1;
-
-int max_center_lines;
-
-pthread_t keyboard_thread;
-
-void reset_ncurses()
-{
- curs_set(1);
- endwin();
- exit(0);
-}
-
-static void handle_sigterm(int signal)
-{
- reset_ncurses();
-}
-
-void init_screen()
-{
- initscr();
- noecho();
- halfdelay(DEFAULT_DELAY);
- nonl();
- intrflush(stdscr, false);
- keypad(stdscr, true);
- curs_set(0);
-
- if (has_colors()) {
- start_color();
- init_pair(1, COLOR_RED, COLOR_BLACK); /* - */
- init_pair(2, COLOR_GREEN, COLOR_BLACK); /* + */
- init_pair(3, COLOR_BLACK, COLOR_WHITE); /* keys */
- init_pair(4, COLOR_WHITE, COLOR_GREEN); /* keys activated */
- init_pair(5, COLOR_WHITE, COLOR_BLUE); /* select line */
- }
- termtype = getenv("TERM");
- if (!strcmp(termtype, "xterm") || !strcmp(termtype, "xterm-color") ||
- !strcmp(termtype, "vt220")) {
- define_key("\033[H", KEY_HOME);
- define_key("\033[F", KEY_END);
- define_key("\033OP", KEY_F(1));
- define_key("\033OQ", KEY_F(2));
- define_key("\033OR", KEY_F(3));
- define_key("\033OS", KEY_F(4));
- define_key("\0330U", KEY_F(6));
- define_key("\033[11~", KEY_F(1));
- define_key("\033[12~", KEY_F(2));
- define_key("\033[13~", KEY_F(3));
- define_key("\033[14~", KEY_F(4));
- define_key("\033[16~", KEY_F(6));
- define_key("\033[17;2~", KEY_F(18));
- }
- signal(SIGTERM, handle_sigterm);
- mousemask(BUTTON1_CLICKED, NULL);
- refresh();
-}
-
-WINDOW *create_window(int height, int width, int startx, int starty)
-{
- WINDOW *win;
- win = newwin(height, width, startx, starty);
- box(win, 0 , 0);
- wrefresh(win);
- return win;
-}
-
-WINDOW *create_window_no_border(int height, int width, int startx, int starty)
-{
- WINDOW *win;
- win = newwin(height, width, startx, starty);
- wrefresh(win);
- return win;
-}
-
-void print_digit(WINDOW *win, int digit)
-{
- if (digit < 0) {
- wattron(win, COLOR_PAIR(1));
- wprintw(win, "%d", digit);
- wattroff(win, COLOR_PAIR(1));
- } else if (digit > 0) {
- wattron(win, COLOR_PAIR(2));
- wprintw(win, "+%d", digit);
- wattroff(win, COLOR_PAIR(2));
- } else {
- wprintw(win, "0");
- }
-}
-
-void print_digits(WINDOW *win, int first, int second)
-{
- wprintw(win, "(");
- print_digit(win, first);
- wprintw(win, ", ");
- print_digit(win, second);
- wprintw(win, ")");
-}
-
-void print_headers(int line, char *desc, int value, int first, int second)
-{
- wattron(header, A_BOLD);
- mvwprintw(header, line, 4, "%s", desc);
- wattroff(header, A_BOLD);
- mvwprintw(header, line, 16, "N/A", value);
- wmove(header, line, 24);
- print_digits(header, first, second);
- wmove(header, line, 40);
-}
-
-void set_window_title(WINDOW *win, char *title)
-{
- wattron(win, A_BOLD);
- mvwprintw(win, 0, 1, title);
- wattroff(win, A_BOLD);
-}
-
-void print_log(char *str)
-{
- int i;
- int current_line = 1;
- int current_char = 1;
- char *tmp, *tmp2;
- /* rotate the line buffer */
- if (nb_log_lines >= MAX_LOG_LINES) {
- tmp = strndup(log_lines, MAX_LINE_LENGTH * MAX_LOG_LINES + MAX_LOG_LINES);
- tmp2 = strchr(tmp, '\n');
- memset(log_lines, '\0', strlen(log_lines));
- strncat(log_lines, tmp2 + 1, strlen(tmp2) - 1);
- log_lines[strlen(log_lines)] = '\n';
- log_lines[strlen(log_lines)] = '\0';
- free(tmp);
- }
- nb_log_lines++;
-
- strncat(log_lines, str, MAX_LINE_LENGTH - 1);
-
- if (nb_log_lines < MAX_LOG_LINES)
- log_lines[strlen(log_lines)] = '\n';
- log_lines[strlen(log_lines)] = '\0';
-
- werase(status);
- box(status, 0 , 0);
- set_window_title(status, "Status");
- for (i = 0; i < strlen(log_lines); i++) {
- if (log_lines[i] == '\n') {
- wmove(status, ++current_line, 1);
- current_char = 1;
- } else {
- mvwprintw(status, current_line, current_char++, "%c", log_lines[i]);
- }
- }
- wrefresh(status);
-}
-
-void print_key(WINDOW *win, char *key, char *desc, int toggle)
-{
- int pair;
- if (toggle > 0)
- pair = 4;
- else
- pair = 3;
- wattron(win, COLOR_PAIR(pair));
- wprintw(footer, "%s", key);
- wattroff(win, COLOR_PAIR(pair));
- wprintw(footer, ":%s", desc);
-}
-
-void update_footer()
-{
- sem_wait(&update_display_sem);
- werase(footer);
- wmove(footer, 1, 1);
- print_key(footer, "F2", "CPUtop ", current_view == cpu);
- print_key(footer, "F3", "PerfTop ", current_view == perf);
- print_key(footer, "F6", "IOTop ", current_view == iostream);
- print_key(footer, "Enter", "Details ", current_view == process_details);
- print_key(footer, "q", "Quit | ", 0);
- print_key(footer, "P", "Perf Pref ", 0);
- print_key(footer, "p", "Pause ", toggle_pause);
-
- wrefresh(footer);
- sem_post(&update_display_sem);
-}
-
-void basic_header()
-{
- werase(header);
- box(header, 0 , 0);
- set_window_title(header, "Statistics for interval [gathering data...[");
- wattron(header, A_BOLD);
- mvwprintw(header, 1, 4, "CPUs");
- mvwprintw(header, 2, 4, "Processes");
- mvwprintw(header, 3, 4, "Threads");
- mvwprintw(header, 4, 4, "Files");
- mvwprintw(header, 5, 4, "Network");
- mvwprintw(header, 6, 4, "IO");
- wattroff(header, A_BOLD);
- wrefresh(header);
-}
-
-void update_header()
-{
- werase(header);
- box(header, 0 , 0);
- set_window_title(header, "Statistics for interval ");
- wattron(header, A_BOLD);
- /*
- wprintw(header, "[%lu.%lu, %lu.%lu[",
- data->start.tv_sec, data->start.tv_nsec,
- data->end.tv_sec, data->end.tv_nsec);
- */
- wprintw(header, "[%lu, %lu[",
- data->start,
- data->end);
- mvwprintw(header, 1, 4, "CPUs");
- wattroff(header, A_BOLD);
- wprintw(header, "\t%d\t(max/cpu : %0.2f%)", data->cpu_table->len,
- 100.0/data->cpu_table->len);
- print_headers(2, "Processes", data->nbproc, data->nbnewproc,
- -1*(data->nbdeadproc));
- print_headers(3, "Threads", data->nbthreads, data->nbnewthreads,
- -1*(data->nbdeadthreads));
- print_headers(4, "Files", data->nbfiles, data->nbnewfiles,
- -1*(data->nbclosedfiles));
- mvwprintw(header, 4, 43, "N/A kbytes/sec");
- print_headers(5, "Network", 114, 0, 0);
- mvwprintw(header, 5, 43, "N/A Mbytes/sec");
- wrefresh(header);
-}
-
-gint sort_by_cpu_desc(gconstpointer p1, gconstpointer p2)
-{
- struct processtop *n1 = *(struct processtop **)p1;
- struct processtop *n2 = *(struct processtop **)p2;
- unsigned long totaln1 = n1->totalcpunsec;
- unsigned long totaln2 = n2->totalcpunsec;
-
- if (totaln1 < totaln2)
- return 1;
- if (totaln1 == totaln2)
- return 0;
- return -1;
-}
-
-gint sort_by_cpu_group_by_threads_desc(gconstpointer p1, gconstpointer p2)
-{
- struct processtop *n1 = *(struct processtop **)p1;
- struct processtop *n2 = *(struct processtop **)p2;
- unsigned long totaln1 = n1->threadstotalcpunsec;
- unsigned long totaln2 = n2->threadstotalcpunsec;
-
- if (totaln1 < totaln2)
- return 1;
- if (totaln1 == totaln2)
- return 0;
- return -1;
-}
-
-void update_cputop_display()
-{
- int i;
- int header_offset = 2;
- struct processtop *tmp;
- unsigned long elapsed;
- double maxcputime;
- int nblinedisplayed = 0;
- int current_line = 0;
-
- elapsed = data->end - data->start;
- maxcputime = elapsed * data->cpu_table->len / 100.0;
-
- g_ptr_array_sort(data->process_table, sort_by_cpu_desc);
-
- set_window_title(center, "CPU Top");
- wattron(center, A_BOLD);
- mvwprintw(center, 1, 1, "CPU(%)");
- mvwprintw(center, 1, 12, "TGID");
- mvwprintw(center, 1, 22, "PID");
- mvwprintw(center, 1, 32, "NAME");
- wattroff(center, A_BOLD);
-
- max_center_lines = LINES - 7 - 7 - 1 - header_offset;
-
- /* iterate the process (thread) list */
- for (i = list_offset; i < data->process_table->len &&
- nblinedisplayed < max_center_lines; i++) {
- tmp = g_ptr_array_index(data->process_table, i);
-
- if (current_line == selected_line) {
- selected_process = tmp;
- selected_tid = tmp->tid;
- selected_comm = tmp->comm;
- wattron(center, COLOR_PAIR(5));
- mvwhline(center, current_line + header_offset, 1, ' ', COLS-3);
- }
- /* CPU(%) */
- mvwprintw(center, current_line + header_offset, 1, "%1.2f",
- tmp->totalcpunsec / maxcputime);
- /* TGID */
- mvwprintw(center, current_line + header_offset, 12, "%d", tmp->pid);
- /* PID */
- mvwprintw(center, current_line + header_offset, 22, "%d", tmp->tid);
- /* NAME */
- mvwprintw(center, current_line + header_offset, 32, "%s", tmp->comm);
- wattroff(center, COLOR_PAIR(5));
- nblinedisplayed++;
- current_line++;
- }
-}
-
-gint sort_perf(gconstpointer p1, gconstpointer p2, gpointer key)
-{
- struct processtop *n1 = *(struct processtop **) p1;
- struct processtop *n2 = *(struct processtop **) p2;
-
- struct perfcounter *tmp1, *tmp2;
- unsigned long totaln2 = 0;
- unsigned long totaln1 = 0;
-
- if (!key)
- return 0;
-
- tmp1 = g_hash_table_lookup(n1->perf, key);
- if (!tmp1)
- totaln1 = 0;
- else
- totaln1 = tmp1->count;
-
- tmp2 = g_hash_table_lookup(n2->perf, key);
- if (!tmp2)
- totaln2 = 0;
- else
- totaln2 = tmp2->count;
-
- if (totaln1 < totaln2)
- return 1;
- if (totaln1 == totaln2) {
- totaln1 = n1->tid;
- totaln2 = n2->tid;
- if (totaln1 < totaln2)
- return 1;
- return -1;
- }
- return -1;
-}
-
-void print_key_title(char *key, int line)
-{
- wattron(center, A_BOLD);
- mvwprintw(center, line, 1, "%s\t", key);
- wattroff(center, A_BOLD);
-}
-
-void update_process_details()
-{
- unsigned long elapsed;
- double maxcputime;
- struct processtop *tmp = find_process_tid(data, selected_tid, selected_comm);
-
- set_window_title(center, "Process details");
-
-
- elapsed = data->end - data->start;
- maxcputime = elapsed * data->cpu_table->len / 100.0;
-
- print_key_title("Name", 1);
- wprintw(center, "%s", selected_comm);
- print_key_title("TID", 2);
- wprintw(center, "%d", selected_tid);
- if (!tmp) {
- print_key_title("Does not exit at this time", 3);
- return;
- }
-
- print_key_title("PID", 3);
- wprintw(center, "%d", tmp->pid);
- print_key_title("PPID", 4);
- wprintw(center, "%d", tmp->ppid);
- print_key_title("CPU", 5);
- wprintw(center, "%1.2f %%", tmp->totalcpunsec/maxcputime);
-}
-
-void update_perf()
-{
- int i, j;
- int nblinedisplayed = 0;
- int current_line = 0;
- struct processtop *tmp;
- int header_offset = 2;
- int perf_row = 40;
- struct perfcounter *perfn1, *perfn2;
- GList *perflist;
- char *perf_key = NULL;
- int value;
-
- set_window_title(center, "Perf Top");
- wattron(center, A_BOLD);
- mvwprintw(center, 1, 1, "PID");
- mvwprintw(center, 1, 11, "TID");
- mvwprintw(center, 1, 22, "NAME");
-
- perf_row = 40;
- perflist = g_list_first(g_hash_table_get_keys(data->perf_list));
- while (perflist) {
- perfn1 = g_hash_table_lookup(data->perf_list, perflist->data);
- /* + 6 to strip the "_perf_" prefix */
- if (perfn1->visible) {
- mvwprintw(center, 1, perf_row, "%s",
- (char *) perflist->data + 6);
- perf_row += 20;
- }
- if (perfn1->sort) {
- perf_key = (char *) perflist->data;
- }
- perflist = g_list_next(perflist);
- }
- wattroff(center, A_BOLD);
-
- g_ptr_array_sort_with_data(data->process_table, sort_perf, perf_key);
- for (i = 0; i < data->process_table->len &&
- nblinedisplayed < max_center_lines; i++) {
- GList *perf_keys;
- tmp = g_ptr_array_index(data->process_table, i);
-
- if (current_line == selected_line) {
- selected_process = tmp;
- wattron(center, COLOR_PAIR(5));
- mvwhline(center, current_line + header_offset, 1, ' ', COLS-3);
- }
-
- mvwprintw(center, current_line + header_offset, 1, "%d", tmp->pid);
- mvwprintw(center, current_line + header_offset, 11, "%d", tmp->tid);
- mvwprintw(center, current_line + header_offset, 22, "%s", tmp->comm);
-
- /* FIXME : sometimes there is a segfault here, I have no idea why :-( */
- perf_keys = g_hash_table_get_keys(data->perf_list);
- if (perf_keys)
- perflist = g_list_first(perf_keys);
- else
- perflist = NULL;
-
- perf_row = 40;
- j = 0;
- while (perflist) {
- j++;
- perfn1 = g_hash_table_lookup(data->perf_list, perflist->data);
- if (!perfn1) {
- perflist = g_list_next(perflist);
- continue;
- }
- if (perfn1->visible) {
- perfn2 = g_hash_table_lookup(tmp->perf, perflist->data);
- if (perfn2)
- value = perfn2->count;
- else
- value = 0;
- mvwprintw(center, current_line + header_offset, perf_row, "%d", value);
- perf_row += 20;
- }
- perflist = g_list_next(perflist);
- }
-
- wattroff(center, COLOR_PAIR(5));
- nblinedisplayed++;
- current_line++;
- }
-}
-
-void update_fileio()
-{
- int i;
- int offset;
-
- set_window_title(center, "IO Top");
- wattron(center, A_BOLD);
- mvwprintw(center, 1, 10, "READ");
- mvwprintw(center, 2, 1, "bytes");
- mvwprintw(center, 2, 15, "bytes/sec");
-
- mvwprintw(center, 1, 39, "WRITE");
- mvwprintw(center, 2, 33, "bytes");
- mvwprintw(center, 2, 45, "bytes/sec");
-
- if (toggle_threads > 0) {
- mvwprintw(center, 1, 60, "TGID");
- mvwprintw(center, 1, 70, "PID");
- offset = 8;
- } else {
- mvwprintw(center, 1, 60, "PID(TGID)");
- offset = 0;
- }
- mvwprintw(center, 1, 72 + offset, "NAME");
- wattroff(center, A_BOLD);
-
- for (i = 3; i < LINES - 3 - 8 - 1; i++) {
- mvwprintw(center, i, 1, "%d", i*1000);
- mvwprintw(center, i, 15, "%dk", i);
- mvwprintw(center, i, 28, "| %d", i*2000);
- mvwprintw(center, i, 45, "%dk", i*2);
- if (toggle_threads > 0) {
- mvwprintw(center, i, 57, "| %d", i);
- mvwprintw(center, i, 70, "%d", i);
- } else {
- mvwprintw(center, i, 57, "| %d", i);
- }
- mvwprintw(center, i, 72 + offset, "process_%d", i);
- }
-}
-
-gint sort_by_ret_desc(gconstpointer p1, gconstpointer p2)
-{
- struct processtop *n1 = *(struct processtop **)p1;
- struct processtop *n2 = *(struct processtop **)p2;
- unsigned long totaln1 = n1->iostream->ret_total;
- unsigned long totaln2 = n2->iostream->ret_total;
-
- if (totaln1 < totaln2)
- return 1;
- if (totaln1 == totaln2)
- return 0;
- return -1;
-}
-
-void update_iostream()
-{
- int i;
- int header_offset = 2;
- struct processtop *tmp;
- int nblinedisplayed = 0;
- int current_line = 0;
-
- set_window_title(center, "IO Top");
- wattron(center, A_BOLD);
- mvwprintw(center, 1, 1, "READ (B/s)");
- mvwprintw(center, 1, 20, "WRITE (B/s)");
-
- mvwprintw(center, 1, 40, "TOTAL STREAM");
-
- mvwprintw(center, 1, 60, "TGID");
- mvwprintw(center, 1, 80, "PID");
-
- mvwprintw(center, 1, 92, "NAME");
- wattroff(center, A_BOLD);
-
- g_ptr_array_sort(data->process_table, sort_by_ret_desc);
-
- for (i = list_offset; i < data->process_table->len &&
- nblinedisplayed < max_center_lines; i++) {
- tmp = g_ptr_array_index(data->process_table, i);
-
- if (current_line == selected_line) {
- selected_process = tmp;
- selected_tid = tmp->tid;
- selected_comm = tmp->comm;
- wattron(center, COLOR_PAIR(5));
- mvwhline(center, current_line + header_offset, 1, ' ', COLS-3);
- }
- /* READ (bytes/sec) */
- mvwprintw(center, current_line + header_offset, 1, "%lu",
- tmp->iostream->ret_read);
-
- /* WRITE (bytes/sec) */
- mvwprintw(center, current_line + header_offset, 20, "%lu",
- tmp->iostream->ret_write);
-
- /* TOTAL STREAM */
- if(tmp->iostream->ret_total >= 1000000)
- mvwprintw(center, current_line + header_offset, 40, "%lu MB",
- tmp->iostream->ret_total/1000000);
- else if(tmp->iostream->ret_total >=1000)
- mvwprintw(center, current_line + header_offset, 40, "%lu KB",
- tmp->iostream->ret_total/1000);
- else
- mvwprintw(center, current_line + header_offset, 40, "%lu B",
- tmp->iostream->ret_total);
- /* TGID */
- mvwprintw(center, current_line + header_offset, 60, "%d", tmp->pid);
- /* PID */
- mvwprintw(center, current_line + header_offset, 80, "%d", tmp->tid);
- /* NAME */
- mvwprintw(center, current_line + header_offset, 92, "%s", tmp->comm);
- wattroff(center, COLOR_PAIR(5));
- nblinedisplayed++;
- current_line++;
- }
-}
-
-void update_current_view()
-{
- sem_wait(&update_display_sem);
- if (!data)
- return;
- update_header();
-
- werase(center);
- box(center, 0, 0);
- switch (current_view) {
- case cpu:
- update_cputop_display();
- break;
- case perf:
- update_perf();
- break;
- case process_details:
- update_process_details();
- break;
- case fileio:
- update_fileio();
- break;
- case iostream:
- update_iostream();
- break;
- case tree:
- update_cputop_display();
- break;
- default:
- break;
- }
- update_panels();
- doupdate();
- sem_post(&update_display_sem);
-}
-
-void setup_perf_panel()
-{
- int size;
- if (!data)
- return;
- if (perf_panel_window) {
- del_panel(perf_panel);
- delwin(perf_panel_window);
- }
- size = g_hash_table_size(data->perf_list);
- perf_panel_window = create_window(size + 2, 30, 10, 10);
- perf_panel = new_panel(perf_panel_window);
- perf_panel_visible = 0;
- hide_panel(perf_panel);
-}
-
-void update_perf_panel(int line_selected, int toggle_view, int toggle_sort)
-{
- int i;
- struct perfcounter *perf;
- GList *perflist;
-
- if (!data)
- return;
-
- werase(perf_panel_window);
- box(perf_panel_window, 0 , 0);
- set_window_title(perf_panel_window, "Perf Preferences ");
- wattron(perf_panel_window, A_BOLD);
- mvwprintw(perf_panel_window, g_hash_table_size(data->perf_list) + 1, 1, " 's' to sort");
- wattroff(perf_panel_window, A_BOLD);
-
- if (toggle_sort == 1) {
- i = 0;
- perflist = g_list_first(g_hash_table_get_keys(data->perf_list));
- while (perflist) {
- perf = g_hash_table_lookup(data->perf_list, perflist->data);
- if (i != line_selected)
- perf->sort = 0;
- else
- perf->sort = 1;
- i++;
- perflist = g_list_next(perflist);
- }
- update_current_view();
- }
-
- i = 0;
- perflist = g_list_first(g_hash_table_get_keys(data->perf_list));
- while (perflist) {
- perf = g_hash_table_lookup(data->perf_list, perflist->data);
- if (i == line_selected && toggle_view == 1) {
- perf->visible = perf->visible == 1 ? 0:1;
- update_current_view();
- }
- if (i == line_selected) {
- wattron(perf_panel_window, COLOR_PAIR(5));
- mvwhline(perf_panel_window, i + 1, 1, ' ', 30 - 2);
- }
- if (perf->sort == 1)
- wattron(perf_panel_window, A_BOLD);
- mvwprintw(perf_panel_window, i + 1, 1, "[%c] %s",
- perf->visible == 1 ? 'x' : ' ',
- (char *) perflist->data + 6);
- wattroff(perf_panel_window, A_BOLD);
- wattroff(perf_panel_window, COLOR_PAIR(5));
- i++;
- perflist = g_list_next(perflist);
- }
- update_panels();
- doupdate();
-}
-
-
-void toggle_perf_panel(void)
-{
- if (perf_panel_visible) {
- hide_panel(perf_panel);
- perf_panel_visible = 0;
- } else {
- setup_perf_panel();
- update_perf_panel(perf_line_selected, 0, 0);
- show_panel(perf_panel);
- perf_panel_visible = 1;
- }
- update_panels();
- doupdate();
-}
-
-void display(unsigned int index)
-{
- last_display_index = index;
- currently_displayed_index = index;
- data = g_ptr_array_index(copies, index);
- if (!data)
- return;
- max_elements = data->process_table->len;
- update_current_view();
- update_footer();
- update_panels();
- doupdate();
-}
-
-void pause_display()
-{
- toggle_pause = 1;
- print_log("Pause");
- sem_wait(&pause_sem);
-}
-
-void resume_display()
-{
- toggle_pause = -1;
- print_log("Resume");
- sem_post(&pause_sem);
-}
-
-void *handle_keyboard(void *p)
-{
- int ch;
- while((ch = getch())) {
- switch(ch) {
- /* Move the cursor and scroll */
- case KEY_DOWN:
- if (perf_panel_visible) {
- if (perf_line_selected < g_hash_table_size(data->perf_list) - 1)
- perf_line_selected++;
- update_perf_panel(perf_line_selected, 0, 0);
- } else {
- if (selected_line < (max_center_lines - 1) &&
- selected_line < max_elements - 1) {
- selected_line++;
- selected_in_list++;
- } else if (selected_in_list < (max_elements - 1)
- && (list_offset < (max_elements - max_center_lines))) {
- selected_in_list++;
- list_offset++;
- }
- update_current_view();
- }
- break;
- case KEY_NPAGE:
- if ((selected_line + 10 < max_center_lines - 1) &&
- ((selected_line + 10) < max_elements - 1)) {
- selected_line += 10;
- selected_in_list += 10;
- } else if (max_elements > max_center_lines) {
- selected_line = max_center_lines - 1;
- if (selected_in_list + 10 < max_elements - 1) {
- selected_in_list += 10;
- list_offset += (selected_in_list - max_center_lines + 1);
- }
- } else if (selected_line + 10 > max_elements) {
- selected_line = max_elements - 1;
- }
- update_current_view();
- break;
- case KEY_UP:
- if (perf_panel_visible) {
- if (perf_line_selected > 0)
- perf_line_selected--;
- update_perf_panel(perf_line_selected, 0, 0);
- } else {
- if (selected_line > 0) {
- selected_line--;
- selected_in_list--;
- } else if (selected_in_list > 0 && list_offset > 0) {
- selected_in_list--;
- list_offset--;
- }
- update_current_view();
- }
- break;
- case KEY_PPAGE:
- if (selected_line - 10 > 0)
- selected_line -= 10;
- else
- selected_line = 0;
- update_current_view();
- break;
-
- /* Navigate the history with arrows */
- case KEY_LEFT:
- if (currently_displayed_index > 0) {
- currently_displayed_index--;
- print_log("Going back in time");
- } else {
- print_log("Cannot rewind, last data is already displayed");
- }
- data = g_ptr_array_index(copies, currently_displayed_index);
- max_elements = data->process_table->len;
-
- /* we force to pause the display when moving in time */
- if (toggle_pause < 0)
- pause_display();
-
- update_current_view();
- update_footer();
- break;
- case KEY_RIGHT:
- if (currently_displayed_index < last_display_index) {
- currently_displayed_index++;
- print_log("Going forward in time");
- data = g_ptr_array_index(copies, currently_displayed_index);
- max_elements = data->process_table->len;
- update_current_view();
- update_footer();
- } else {
- print_log("Manually moving forward");
- sem_post(&timer);
- /* we force to resume the refresh when moving forward */
- if (toggle_pause > 0)
- resume_display();
- }
-
- break;
- case ' ':
- if (perf_panel_visible)
- update_perf_panel(perf_line_selected, 1, 0);
- break;
- case 's':
- if (perf_panel_visible)
- update_perf_panel(perf_line_selected, 0, 1);
- break;
-
- case 13: /* FIXME : KEY_ENTER ?? */
- if (current_view == cpu) {
- current_view = process_details;
- }
- update_current_view();
- break;
-
- case KEY_F(1):
- toggle_tree *= -1;
- current_view = cpu;
- update_current_view();
- break;
- case KEY_F(2):
- current_view = cpu;
- update_current_view();
- break;
- case KEY_F(3):
- current_view = perf;
- toggle_tree = -1;
- update_current_view();
- break;
- case KEY_F(4):
- current_view = fileio;
- toggle_tree = -1;
- update_current_view();
- break;
- case KEY_F(5):
- current_view = netio;
- toggle_tree = -1;
- update_current_view();
- break;
- case KEY_F(6):
- current_view = iostream;
- toggle_tree = -1;
- update_current_view();
- break;
- case KEY_F(10):
- case 'q':
- reset_ncurses();
- break;
- case 't':
- toggle_threads *= -1;
- update_current_view();
- break;
- case 'p':
- if (toggle_pause < 0) {
- pause_display();
- } else {
- resume_display();
- }
- break;
- case 'P':
- toggle_perf_panel();
- break;
- default:
- /*
- * commented because it makes the list refresh in different order
- * if we sort and there are equal values
- if (data)
- update_current_view();
- */
- break;
- }
- update_footer();
- }
- return NULL;
-}
-
-void init_ncurses()
-{
- sem_init(&update_display_sem, 0, 1);
- init_screen();
-
- header = create_window(7, COLS - 1, 0, 0);
- center = create_window(LINES - 7 - 7, COLS - 1, 7, 0);
- status = create_window(MAX_LOG_LINES + 2, COLS - 1, LINES - 7, 0);
- footer = create_window(1, COLS - 1, LINES - 1, 0);
-
- print_log("Starting display");
-
- main_panel = new_panel(center);
- setup_perf_panel();
-
- current_view = cpu;
-
- basic_header();
- update_footer();
-
- pthread_create(&keyboard_thread, NULL, handle_keyboard, (void *)NULL);
-}
-
+++ /dev/null
-/*
- * Copyright (C) 2011 Julien Desfossez
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License Version 2 as
- * published by the Free Software Foundation;
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
- * MA 02111-1307, USA.
- */
-
-#ifndef CURSESDISPLAY_H
-#define CURSESDISPLAY_H
-
-#include <glib.h>
-#include <ncurses.h>
-#include "common.h"
-
-enum current_view_list
-{
- cpu = 1,
- perf,
- process_details,
- fileio,
- netio,
- iostream,
- tree,
-} current_view;
-
-void display(unsigned int);
-void init_ncurses();
-void reset_ncurses();
-
-#endif // CURSESDISPLAY_H
+++ /dev/null
-/*
- * Copyright (C) 2011 Mathieu Bain <mathieu.bain@polymtl.ca>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License Version 2 as
- * published by the Free Software Foundation;
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
- * MA 02111-1307, USA.
- */
-
-#include <babeltrace/babeltrace.h>
-
-#include "lttngtoptypes.h"
-#include "common.h"
-#include "iostreamtop.h"
-
-#include <stdlib.h>
-
-int update_iostream_ret(struct lttngtop *ctx, int tid, char *comm,
- unsigned long timestamp, int cpu_id, int ret)
-{
- struct processtop *tmp;
- int err = 0;
-
- tmp = get_proc(ctx, tid, comm, timestamp);
- if ((tmp->iostream->syscall_info != NULL) && (tmp->iostream->syscall_info->cpu_id == cpu_id)) {
- if (tmp->iostream->syscall_info->type == __NR_read && ret > 0) {
- tmp->iostream->ret_read += ret;
- tmp->iostream->ret_total += ret;
- } else if(tmp->iostream->syscall_info->type == __NR_write && ret > 0) {
- tmp->iostream->ret_write += ret;
- tmp->iostream->ret_total += ret;
- } else{
- err = -1;
- }
- free(tmp->iostream->syscall_info);
- tmp->iostream->syscall_info = NULL;
- }
-
- return err;
-}
-
-enum bt_cb_ret handle_exit_syscall(struct bt_ctf_event *call_data,
- void *private_data)
-{
- struct definition *scope;
- unsigned long timestamp;
- char *comm;
- uint64_t ret, tid;
- int64_t cpu_id;
-
- timestamp = bt_ctf_get_timestamp(call_data);
- if (timestamp == -1ULL)
- goto error;
-
- scope = bt_ctf_get_top_level_scope(call_data,
- BT_STREAM_EVENT_CONTEXT);
- comm = bt_ctf_get_char_array(bt_ctf_get_field(call_data,
- scope, "_procname"));
- if (bt_ctf_field_get_error()) {
- fprintf(stderr, "Missing procname context info\n");
- goto error;
- }
-
- tid = bt_ctf_get_int64(bt_ctf_get_field(call_data,
- scope, "_tid"));
- if (bt_ctf_field_get_error()) {
- fprintf(stderr, "Missing tid context info\n");
- goto error;
- }
-
- scope = bt_ctf_get_top_level_scope(call_data,
- BT_EVENT_FIELDS);
- ret = bt_ctf_get_int64(bt_ctf_get_field(call_data,
- scope, "_ret"));
- if (bt_ctf_field_get_error()) {
- fprintf(stderr, "Missing ret context info\n");
- goto error;
- }
-
- scope = bt_ctf_get_top_level_scope(call_data,
- BT_STREAM_PACKET_CONTEXT);
- cpu_id = bt_ctf_get_uint64(bt_ctf_get_field(call_data,
- scope, "cpu_id"));
- if (bt_ctf_field_get_error()) {
- fprintf(stderr, "Missing cpu_id context info\n");
- goto error;
- }
-
- /*
- * if we encounter an exit_syscall and it is not for a syscall read or write
- * we just abort the execution of this callback
- */
- if ((update_iostream_ret(<tngtop, tid, comm, timestamp, cpu_id, ret)) < 0)
- return BT_CB_ERROR_CONTINUE;
-
- return BT_CB_OK;
-
-error:
- return BT_CB_ERROR_STOP;
-}
-
-
-enum bt_cb_ret handle_sys_write(struct bt_ctf_event *call_data,
- void *private_data)
-{
- struct definition *scope;
- struct processtop *tmp;
- struct syscalls *syscall_info;
- unsigned long timestamp;
- uint64_t cpu_id;
- char *comm;
- int64_t tid;
-
- timestamp = bt_ctf_get_timestamp(call_data);
- if (timestamp == -1ULL)
- goto error;
-
- scope = bt_ctf_get_top_level_scope(call_data,
- BT_STREAM_EVENT_CONTEXT);
- comm = bt_ctf_get_char_array(bt_ctf_get_field(call_data,
- scope, "_procname"));
- if (bt_ctf_field_get_error()) {
- fprintf(stderr, "Missing procname context info\n");
- goto error;
- }
-
- tid = bt_ctf_get_int64(bt_ctf_get_field(call_data,
- scope, "_tid"));
- if (bt_ctf_field_get_error()) {
- fprintf(stderr, "Missing tid context info\n");
- goto error;
- }
-
- scope = bt_ctf_get_top_level_scope(call_data,
- BT_STREAM_PACKET_CONTEXT);
- cpu_id = bt_ctf_get_uint64(bt_ctf_get_field(call_data,
- scope, "cpu_id"));
- if (bt_ctf_field_get_error()) {
- fprintf(stderr, "Missing cpu_id context info\n");
- goto error;
- }
-
- syscall_info = malloc(sizeof(struct syscalls));
- syscall_info->cpu_id = cpu_id;
- syscall_info->type = __NR_write;
- syscall_info->tid = tid;
- tmp = get_proc(<tngtop, tid, comm, timestamp);
- tmp->iostream->syscall_info = syscall_info;
-
- return BT_CB_OK;
-
-error:
- return BT_CB_ERROR_STOP;
-}
-
-enum bt_cb_ret handle_sys_read(struct bt_ctf_event *call_data,
- void *private_data)
-{
- struct processtop *tmp;
- struct definition *scope;
- struct syscalls * syscall_info;
- unsigned long timestamp;
- uint64_t cpu_id;
- char *comm;
- int64_t tid;
-
- timestamp = bt_ctf_get_timestamp(call_data);
- if (timestamp == -1ULL)
- goto error;
-
- scope = bt_ctf_get_top_level_scope(call_data,
- BT_STREAM_EVENT_CONTEXT);
- comm = bt_ctf_get_char_array(bt_ctf_get_field(call_data,
- scope, "_procname"));
- if (bt_ctf_field_get_error()) {
- fprintf(stderr, "Missing procname context info\n");
- goto error;
- }
-
- tid = bt_ctf_get_int64(bt_ctf_get_field(call_data,
- scope, "_tid"));
- if (bt_ctf_field_get_error()) {
- fprintf(stderr, "Missing tid context info\n");
- goto error;
- }
-
- scope = bt_ctf_get_top_level_scope(call_data,
- BT_STREAM_PACKET_CONTEXT);
- cpu_id = bt_ctf_get_uint64(bt_ctf_get_field(call_data,
- scope, "cpu_id"));
- if (bt_ctf_field_get_error()) {
- fprintf(stderr, "Missing cpu_id context info\n");
- goto error;
- }
-
- syscall_info = malloc(sizeof(struct syscalls));
- syscall_info->cpu_id = cpu_id;
- syscall_info->type = __NR_read;
- syscall_info->tid = tid;
- tmp = get_proc(<tngtop, tid, comm, timestamp);
- tmp->iostream->syscall_info = syscall_info;
-
- return BT_CB_OK;
-
-error:
- return BT_CB_ERROR_STOP;
-}
-
+++ /dev/null
-/*
- * Copyright (C) 2011 Mathieu Bain <mathieu.bain@polymtl.ca>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License Version 2 as
- * published by the Free Software Foundation;
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
- * MA 02111-1307, USA.
- */
-
-#ifndef _IOSTREANTOP_H
-#define _IOSTREAMTOP_H
-
-#include <babeltrace/babeltrace.h>
-#include <inttypes.h>
-#include <glib.h>
-#include <asm/unistd.h>
-
-/*
-#define SYS_READ 1
-#define SYS_WRITE 2
-*/
-
-enum bt_cb_ret handle_exit_syscall(struct bt_ctf_event *call_data,
- void *private_data);
-
-enum bt_cb_ret handle_sys_write(struct bt_ctf_event *call_data,
- void *private_data);
-
-enum bt_cb_ret handle_sys_read(struct bt_ctf_event *call_data,
- void *private_data);
-
-#endif /* _IOSTREAMTOP_H */
+++ /dev/null
-/*
- * Copyright (C) 2011 Julien Desfossez
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License Version 2 as
- * published by the Free Software Foundation;
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
- * MA 02111-1307, USA.
- */
-
-#define _GNU_SOURCE
-#include <config.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <babeltrace/babeltrace.h>
-#include <babeltrace/ctf/events.h>
-#include <babeltrace/ctf/callbacks.h>
-#include <fcntl.h>
-#include <pthread.h>
-#include <popt.h>
-#include <stdlib.h>
-#include <ftw.h>
-#include <dirent.h>
-#include <ctype.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <string.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <fts.h>
-
-#include "lttngtoptypes.h"
-#include "cputop.h"
-#include "iostreamtop.h"
-#include "cursesdisplay.h"
-#include "common.h"
-
-#define DEFAULT_FILE_ARRAY_SIZE 1
-
-const char *opt_input_path;
-
-struct lttngtop *copy;
-pthread_t display_thread;
-pthread_t timer_thread;
-
-unsigned long refresh_display = 1 * NSEC_PER_SEC;
-unsigned long last_display_update = 0;
-int quit = 0;
-
-enum {
- OPT_NONE = 0,
- OPT_HELP,
- OPT_LIST,
- OPT_VERBOSE,
- OPT_DEBUG,
- OPT_NAMES,
-};
-
-static struct poptOption long_options[] = {
- /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
- { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL },
- { NULL, 0, 0, NULL, 0, NULL, NULL },
-};
-
-void *refresh_thread(void *p)
-{
- while (1) {
- sem_wait(&pause_sem);
- sem_post(&pause_sem);
- sem_post(&timer);
- sleep(refresh_display/NSEC_PER_SEC);
- }
-}
-
-void *ncurses_display(void *p)
-{
- unsigned int current_display_index = 0;
-
- sem_wait(&bootstrap);
- init_ncurses();
-
- while (1) {
- sem_wait(&timer);
- sem_wait(&goodtodisplay);
- sem_wait(&pause_sem);
-
- copy = g_ptr_array_index(copies, current_display_index);
- if (copy)
- display(current_display_index++);
-
- sem_post(&goodtoupdate);
- sem_post(&pause_sem);
-
- if (quit) {
- reset_ncurses();
- pthread_exit(0);
- }
- }
-}
-
-/*
- * hook on each event to check the timestamp and refresh the display if
- * necessary
- */
-enum bt_cb_ret check_timestamp(struct bt_ctf_event *call_data, void *private_data)
-{
- unsigned long timestamp;
-
- timestamp = bt_ctf_get_timestamp(call_data);
- if (timestamp == -1ULL)
- goto error;
-
- if (last_display_update == 0)
- last_display_update = timestamp;
-
- if (timestamp - last_display_update >= refresh_display) {
- sem_wait(&goodtoupdate);
- g_ptr_array_add(copies, get_copy_lttngtop(last_display_update,
- timestamp));
- sem_post(&goodtodisplay);
- sem_post(&bootstrap);
- last_display_update = timestamp;
- }
- return BT_CB_OK;
-
-error:
- fprintf(stderr, "check_timestamp callback error\n");
- return BT_CB_ERROR_STOP;
-}
-
-/*
- * get_perf_counter : get or create and return a perf_counter struct for
- * either a process or a cpu (only one of the 2 parameters mandatory)
- */
-struct perfcounter *get_perf_counter(const char *name, struct processtop *proc,
- struct cputime *cpu)
-{
- struct perfcounter *ret, *global;
- GHashTable *table;
-
- if (proc)
- table = proc->perf;
- else if (cpu)
- table = cpu->perf;
- else
- goto error;
-
- ret = g_hash_table_lookup(table, (gpointer) name);
- if (ret)
- goto end;
-
- ret = malloc(sizeof(struct perfcounter));
- memset(ret, 0, sizeof(struct perfcounter));
- /* by default, make it visible in the UI */
- ret->visible = 1;
- g_hash_table_insert(table, (gpointer) name, ret);
-
- global = g_hash_table_lookup(lttngtop.perf_list, (gpointer) name);
- if (!global) {
- global = malloc(sizeof(struct perfcounter));
- memset(global, 0, sizeof(struct perfcounter));
- memcpy(global, ret, sizeof(struct perfcounter));
- /* by default, sort on the first perf context */
- if (g_hash_table_size(lttngtop.perf_list) == 0)
- global->sort = 1;
- g_hash_table_insert(lttngtop.perf_list, (gpointer) name, global);
- }
-
-end:
- return ret;
-
-error:
- return NULL;
-}
-
-void update_perf_value(struct processtop *proc, struct cputime *cpu,
- const char *name, int value)
-{
- struct perfcounter *cpu_perf, *process_perf;
-
- cpu_perf = get_perf_counter(name, NULL, cpu);
- if (cpu_perf->count < value) {
- process_perf = get_perf_counter(name, proc, NULL);
- process_perf->count += value - cpu_perf->count;
- cpu_perf->count = value;
- }
-}
-
-void extract_perf_counter_scope(struct bt_ctf_event *event,
- struct definition *scope,
- struct processtop *proc,
- struct cputime *cpu)
-{
- struct definition const * const *list = NULL;
- unsigned int count;
- int i, ret;
-
- if (!scope)
- goto end;
-
- ret = bt_ctf_get_field_list(event, scope, &list, &count);
- if (ret < 0)
- goto end;
-
- for (i = 0; i < count; i++) {
- const char *name = bt_ctf_field_name(list[i]);
- if (strncmp(name, "_perf_", 6) == 0) {
- int value = bt_ctf_get_uint64(list[i]);
- if (bt_ctf_field_get_error())
- continue;
- update_perf_value(proc, cpu, name, value);
- }
- }
-
-end:
- return;
-}
-
-void update_perf_counter(struct processtop *proc, struct bt_ctf_event *event)
-{
- struct definition *scope;
- uint64_t cpu_id;
- struct cputime *cpu;
-
- scope = bt_ctf_get_top_level_scope(event, BT_STREAM_PACKET_CONTEXT);
- cpu_id = bt_ctf_get_uint64(bt_ctf_get_field(event, scope, "cpu_id"));
- if (bt_ctf_field_get_error()) {
- fprintf(stderr, "[error] get cpu_id\n");
- goto end;
- }
- cpu = get_cpu(cpu_id);
-
- scope = bt_ctf_get_top_level_scope(event, BT_STREAM_EVENT_CONTEXT);
- extract_perf_counter_scope(event, scope, proc, cpu);
-
- scope = bt_ctf_get_top_level_scope(event, BT_STREAM_PACKET_CONTEXT);
- extract_perf_counter_scope(event, scope, proc, cpu);
-
- scope = bt_ctf_get_top_level_scope(event, BT_EVENT_CONTEXT);
- extract_perf_counter_scope(event, scope, proc, cpu);
-
-end:
- return;
-}
-
-enum bt_cb_ret fix_process_table(struct bt_ctf_event *call_data,
- void *private_data)
-{
- int pid, tid, ppid;
- char *comm;
- struct processtop *parent, *child;
- struct definition *scope;
- unsigned long timestamp;
-
- /* FIXME : check context pid, tid, ppid and comm */
-
- timestamp = bt_ctf_get_timestamp(call_data);
- if (timestamp == -1ULL)
- goto error;
-
- scope = bt_ctf_get_top_level_scope(call_data, BT_STREAM_EVENT_CONTEXT);
-
- pid = bt_ctf_get_int64(bt_ctf_get_field(call_data, scope, "_pid"));
- if (bt_ctf_field_get_error()) {
-// fprintf(stderr, "Missing pid context info\n");
- goto error;
- }
- tid = bt_ctf_get_int64(bt_ctf_get_field(call_data, scope, "_tid"));
- if (bt_ctf_field_get_error()) {
-// fprintf(stderr, "Missing tid context info\n");
- goto error;
- }
- ppid = bt_ctf_get_int64(bt_ctf_get_field(call_data, scope, "_ppid"));
- if (bt_ctf_field_get_error()) {
-// fprintf(stderr, "Missing ppid context info\n");
- goto error;
- }
- comm = bt_ctf_get_char_array(bt_ctf_get_field(call_data, scope, "_procname"));
- if (bt_ctf_field_get_error()) {
-// fprintf(stderr, "Missing procname context info\n");
- goto error;
- }
-
- /* find or create the current process */
- child = find_process_tid(<tngtop, tid, comm);
- if (!child)
- child = add_proc(<tngtop, tid, comm, timestamp);
- update_proc(child, pid, tid, ppid, comm);
-
- if (pid != tid) {
- /* find or create the parent */
- parent = find_process_tid(<tngtop, pid, comm);
- if (!parent) {
- parent = add_proc(<tngtop, pid, comm, timestamp);
- parent->pid = pid;
- }
-
- /* attach the parent to the current process */
- child->threadparent = parent;
- add_thread(parent, child);
- }
-
- update_perf_counter(child, call_data);
-
- return BT_CB_OK;
-
-error:
- return BT_CB_ERROR_STOP;
-}
-
-void init_lttngtop()
-{
- copies = g_ptr_array_new();
- lttngtop.perf_list = g_hash_table_new(g_direct_hash, g_direct_equal);
-
- sem_init(&goodtodisplay, 0, 0);
- sem_init(&goodtoupdate, 0, 1);
- sem_init(&timer, 0, 1);
- sem_init(&bootstrap, 0, 0);
- sem_init(&pause_sem, 0, 1);
- sem_init(&end_trace_sem, 0, 0);
-
- lttngtop.process_table = g_ptr_array_new();
- lttngtop.files_table = g_ptr_array_new();
- lttngtop.cpu_table = g_ptr_array_new();
-}
-
-void usage(FILE *fd)
-{
-
-}
-
-/*
- * Return 0 if caller should continue, < 0 if caller should return
- * error, > 0 if caller should exit without reporting error.
- */
-static int parse_options(int argc, char **argv)
-{
- poptContext pc;
- int opt, ret = 0;
-
- if (argc == 1) {
- usage(stdout);
- return 1; /* exit cleanly */
- }
-
- pc = poptGetContext(NULL, argc, (const char **) argv, long_options, 0);
- poptReadDefaultConfig(pc, 0);
-
- while ((opt = poptGetNextOpt(pc)) != -1) {
- switch (opt) {
- case OPT_HELP:
- usage(stdout);
- ret = 1; /* exit cleanly */
- goto end;
- case OPT_LIST:
- // list_formats(stdout);
- ret = 1;
- goto end;
- case OPT_VERBOSE:
-// babeltrace_verbose = 1;
- break;
- case OPT_DEBUG:
-// babeltrace_debug = 1;
- break;
- case OPT_NAMES:
-// opt_field_names = 1;
- break;
- default:
- ret = -EINVAL;
- goto end;
- }
- }
-
- opt_input_path = poptGetArg(pc);
- if (!opt_input_path) {
- ret = -EINVAL;
- goto end;
- }
-end:
- if (pc) {
- poptFreeContext(pc);
- }
- return ret;
-}
-
-void iter_trace(struct bt_context *bt_ctx)
-{
- struct bt_ctf_iter *iter;
- struct bt_iter_pos begin_pos;
- struct bt_ctf_event *event;
- int ret = 0;
-
- begin_pos.type = BT_SEEK_BEGIN;
- iter = bt_ctf_iter_create(bt_ctx, &begin_pos, NULL);
-
- /* at each event check if we need to refresh */
- bt_ctf_iter_add_callback(iter, 0, NULL, 0,
- check_timestamp,
- NULL, NULL, NULL);
- /* at each event, verify the status of the process table */
- bt_ctf_iter_add_callback(iter, 0, NULL, 0,
- fix_process_table,
- NULL, NULL, NULL);
- /* to handle the scheduling events */
- bt_ctf_iter_add_callback(iter,
- g_quark_from_static_string("sched_switch"),
- NULL, 0, handle_sched_switch, NULL, NULL, NULL);
- /* to clean up the process table */
- bt_ctf_iter_add_callback(iter,
- g_quark_from_static_string("sched_process_free"),
- NULL, 0, handle_sched_process_free, NULL, NULL, NULL);
-
- /* for IO top */
- bt_ctf_iter_add_callback(iter,
- g_quark_from_static_string("exit_syscall"),
- NULL, 0, handle_exit_syscall, NULL, NULL, NULL);
- bt_ctf_iter_add_callback(iter,
- g_quark_from_static_string("sys_write"),
- NULL, 0, handle_sys_write, NULL, NULL, NULL);
- bt_ctf_iter_add_callback(iter,
- g_quark_from_static_string("sys_read"),
- NULL, 0, handle_sys_read, NULL, NULL, NULL);
- while ((event = bt_ctf_iter_read_event(iter)) != NULL) {
- ret = bt_iter_next(bt_ctf_get_iter(iter));
- if (ret < 0)
- goto end_iter;
- }
-
- /* block until quit, we reached the end of the trace */
- sem_wait(&end_trace_sem);
-
-end_iter:
- bt_iter_destroy(bt_ctf_get_iter(iter));
-}
-
-/*
- * bt_context_add_traces_recursive: Open a trace recursively
- * (copied from BSD code in converter/babeltrace.c)
- *
- * Find each trace present in the subdirectory starting from the given
- * path, and add them to the context. The packet_seek parameter can be
- * NULL: this specify to use the default format packet_seek.
- *
- * Return: 0 on success, nonzero on failure.
- * Unable to open toplevel: failure.
- * Unable to open some subdirectory or file: warn and continue;
- */
-int bt_context_add_traces_recursive(struct bt_context *ctx, const char *path,
- const char *format_str,
- void (*packet_seek)(struct stream_pos *pos,
- size_t offset, int whence))
-{
- FTS *tree;
- FTSENT *node;
- GArray *trace_ids;
- char lpath[PATH_MAX];
- char * const paths[2] = { lpath, NULL };
- int ret;
-
- /*
- * Need to copy path, because fts_open can change it.
- * It is the pointer array, not the strings, that are constant.
- */
- strncpy(lpath, path, PATH_MAX);
- lpath[PATH_MAX - 1] = '\0';
-
- tree = fts_open(paths, FTS_NOCHDIR | FTS_LOGICAL, 0);
- if (tree == NULL) {
- fprintf(stderr, "[error] [Context] Cannot traverse \"%s\" for reading.\n",
- path);
- return -EINVAL;
- }
-
- trace_ids = g_array_new(FALSE, TRUE, sizeof(int));
-
- while ((node = fts_read(tree))) {
- int dirfd, metafd;
-
- if (!(node->fts_info & FTS_D))
- continue;
-
- dirfd = open(node->fts_accpath, 0);
- if (dirfd < 0) {
- fprintf(stderr, "[error] [Context] Unable to open trace "
- "directory file descriptor.\n");
- ret = dirfd;
- goto error;
- }
- metafd = openat(dirfd, "metadata", O_RDONLY);
- if (metafd < 0) {
- ret = close(dirfd);
- if (ret < 0) {
- perror("close");
- goto error;
- }
- } else {
- int trace_id;
-
- ret = close(metafd);
- if (ret < 0) {
- perror("close");
- goto error;
- }
- ret = close(dirfd);
- if (ret < 0) {
- perror("close");
- goto error;
- }
-
- trace_id = bt_context_add_trace(ctx,
- node->fts_accpath, format_str,
- packet_seek, NULL, NULL);
- if (trace_id < 0) {
- fprintf(stderr, "[error] [Context] opening trace \"%s\" from %s "
- "for reading.\n", node->fts_accpath, path);
- ret = trace_id;
- goto error;
- }
- g_array_append_val(trace_ids, trace_id);
- }
- }
-
- g_array_free(trace_ids, TRUE);
- return 0;
-
-error:
- return ret;
-}
-
-int main(int argc, char **argv)
-{
- int ret;
- struct bt_context *bt_ctx = NULL;
-
- ret = parse_options(argc, argv);
- if (ret < 0) {
- fprintf(stdout, "Error parsing options.\n\n");
- usage(stdout);
- exit(EXIT_FAILURE);
- } else if (ret > 0) {
- exit(EXIT_SUCCESS);
- }
-
- init_lttngtop();
-
- bt_ctx = bt_context_create();
- ret = bt_context_add_traces_recursive(bt_ctx, opt_input_path, "ctf", NULL);
- if (ret < 0) {
- printf("[error] Opening the trace\n");
- goto end;
- }
-
- pthread_create(&display_thread, NULL, ncurses_display, (void *) NULL);
- pthread_create(&timer_thread, NULL, refresh_thread, (void *) NULL);
-
- iter_trace(bt_ctx);
-
- quit = 1;
- pthread_join(display_thread, NULL);
-
-end:
- return 0;
-}
+++ /dev/null
-/*
- * Copyright (C) 2011 Julien Desfossez
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License Version 2 as
- * published by the Free Software Foundation;
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
- * MA 02111-1307, USA.
- */
-
-#ifndef LTTNGTOPTYPES_H
-#define LTTNGTOPTYPES_H
-
-#include <glib.h>
-
-struct lttngtop {
- GPtrArray *process_table; /* struct processtop */
- GPtrArray *files_table; /* struct files */
- GPtrArray *cpu_table; /* struct cputime */
- GHashTable *perf_list; /* struct perfcounter */
- unsigned long start;
- unsigned long end;
- unsigned int nbproc;
- unsigned int nbnewproc;
- unsigned int nbdeadproc;
- unsigned int nbthreads;
- unsigned int nbnewthreads;
- unsigned int nbdeadthreads;
- unsigned int nbfiles;
- unsigned int nbnewfiles;
- unsigned int nbclosedfiles;
-} lttngtop;
-
-struct processtop {
- unsigned int puuid;
- int pid;
- char *comm;
- int tid;
- int ppid;
- int oldpid;
- int oldtid;
- int oldppid;
- unsigned long birth;
- unsigned long death;
- unsigned long lastactivity;
- GPtrArray *process_files_table;
- GPtrArray *threads;
- GHashTable *perf;
- struct processtop *threadparent;
- unsigned long totalfileread;
- unsigned long totalfilewrite;
- unsigned long totalcpunsec;
- unsigned long threadstotalcpunsec;
- /* IO speed for this process */
- struct iostream *iostream;
-};
-
-struct perfcounter
-{
- unsigned long count;
- int visible;
- int sort;
-};
-
-struct cputime {
- guint id;
- struct processtop *current_task;
- unsigned long task_start;
- GHashTable *perf;
-};
-
-/*
- * used for "relative seeks" (with fd, for example fs.lseek)
- * and for "absolute seeks" (events occuring on a device without
- * any link to a particular process)
- */
-struct seeks {
- unsigned long offset;
- unsigned long count;
-};
-
-struct ioctls {
- unsigned int command;
- unsigned long count;
-};
-
-struct files {
- struct processtop *ref;
- unsigned int fuuid;
- int fd;
- char *name;
- int oldfd;
- int device;
- int openmode;
- unsigned long openedat;
- unsigned long closedat;
- unsigned long lastaccess;
- unsigned long read;
- unsigned long write;
- unsigned long nbpoll;
- unsigned long nbselect;
- unsigned long nbopen;
- unsigned long nbclose;
- //struct *seeks; /* relative seeks inside the file */
- //struct *ioctls;
- /* XXX : average wait time */
-};
-
-struct sockets {
- int fd;
- int parent_fd; /* on accept a new fd is created from the bound socket */
- int family;
- int type;
- int protocol;
- int sock_address;
- unsigned long openedat;
- unsigned long closedat;
- unsigned long bind_address;
- unsigned long remote_address;
- //struct *sock_options;
-};
-
-struct sock_options {
- int name;
- int value;
-};
-
-struct vmas {
- unsigned long start;
- unsigned long end;
- unsigned long flags;
- unsigned long prot;
- char *description; /* filename or description if possible (stack, heap) */
- unsigned long page_faults;
-};
-
-struct syscalls {
- unsigned int id;
- unsigned long count;
- unsigned int cpu_id;
- unsigned int type;
- unsigned int tid;
-};
-
-struct signals {
- int dest_pid;
- int id;
- unsigned long count;
-};
-
-struct iostream {
- struct syscalls *syscall_info; /* NULL if there is no waiting for an exit_syscall */
- unsigned long ret_read; /* value returned by an I/O syscall_exit for a sys_read*/
- unsigned long ret_write; /* value returned by an I/O syscall_exit for a sys_write*/
- unsigned long ret_total;
-};
-
-#endif /* LTTNGTOPTYPES_H */
--- /dev/null
+AM_CFLAGS = $(PACKAGE_CFLAGS)
+
+bin_PROGRAMS = lttngtop
+
+lttngtop_SOURCES = \
+ lttngtop.c \
+ common.c \
+ cursesdisplay.c \
+ cputop.c \
+ iostreamtop.c
+
+lttngtop_LDADD = -lbabeltrace -lctf
--- /dev/null
+/*
+ * Copyright (C) 2011 Julien Desfossez
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "common.h"
+
+struct processtop *find_process_tid(struct lttngtop *ctx, int tid, char *comm)
+{
+ gint i;
+ struct processtop *tmp;
+
+ for (i = 0; i < ctx->process_table->len; i++) {
+ tmp = g_ptr_array_index(ctx->process_table, i);
+ if (tmp && tmp->tid == tid)
+ return tmp;
+ }
+ return NULL;
+}
+
+struct processtop* add_proc(struct lttngtop *ctx, int tid, char *comm,
+ unsigned long timestamp)
+{
+ struct processtop *newproc;
+
+ /* if the PID already exists, we just rename the process */
+ /* FIXME : need to integrate with clone/fork/exit to be accurate */
+ newproc = find_process_tid(ctx, tid, comm);
+ if (!newproc) {
+ newproc = malloc(sizeof(struct processtop));
+ memset(newproc, 0, sizeof(struct processtop));
+ newproc->tid = tid;
+ newproc->birth = timestamp;
+ newproc->process_files_table = g_ptr_array_new();
+ newproc->threads = g_ptr_array_new();
+ newproc->perf = g_hash_table_new(g_direct_hash, g_direct_equal);
+ newproc->iostream = malloc(sizeof(struct iostream));
+ newproc->iostream->ret_read = 0;
+ newproc->iostream->ret_write = 0;
+ newproc->iostream->ret_total = 0;
+ newproc->iostream->syscall_info = NULL;
+ g_ptr_array_add(ctx->process_table, newproc);
+ }
+ newproc->comm = strdup(comm);
+
+ return newproc;
+}
+
+struct processtop* update_proc(struct processtop* proc, int pid, int tid,
+ int ppid, char *comm)
+{
+ if (proc) {
+ proc->pid = pid;
+ proc->tid = tid;
+ proc->ppid = ppid;
+ if (strcmp(proc->comm, comm) != 0) {
+ free(proc->comm);
+ proc->comm = strdup(comm);
+ }
+ }
+ return proc;
+}
+
+/*
+ * This function just sets the time of death of a process.
+ * When we rotate the cputime we remove it from the process list.
+ */
+void death_proc(struct lttngtop *ctx, int tid, char *comm,
+ unsigned long timestamp)
+{
+ struct processtop *tmp;
+ tmp = find_process_tid(ctx, tid, comm);
+ if (tmp && strcmp(tmp->comm, comm) == 0)
+ tmp->death = timestamp;
+}
+
+struct processtop* get_proc(struct lttngtop *ctx, int tid, char *comm,
+ unsigned long timestamp)
+{
+ struct processtop *tmp;
+ tmp = find_process_tid(ctx, tid, comm);
+ if (tmp && strcmp(tmp->comm, comm) == 0)
+ return tmp;
+ return add_proc(ctx, tid, comm, timestamp);
+}
+
+void add_thread(struct processtop *parent, struct processtop *thread)
+{
+ gint i;
+ struct processtop *tmp;
+
+ for (i = 0; i < parent->threads->len; i++) {
+ tmp = g_ptr_array_index(parent->threads, i);
+ if (tmp == thread)
+ return;
+ }
+ g_ptr_array_add(parent->threads, thread);
+}
+
+struct cputime* add_cpu(int cpu)
+{
+ struct cputime *newcpu;
+
+ newcpu = malloc(sizeof(struct cputime));
+ newcpu->id = cpu;
+ newcpu->current_task = NULL;
+ newcpu->perf = g_hash_table_new(g_direct_hash, g_direct_equal);
+
+ g_ptr_array_add(lttngtop.cpu_table, newcpu);
+
+ return newcpu;
+}
+struct cputime* get_cpu(int cpu)
+{
+ gint i;
+ struct cputime *tmp;
+
+ for (i = 0; i < lttngtop.cpu_table->len; i++) {
+ tmp = g_ptr_array_index(lttngtop.cpu_table, i);
+ if (tmp->id == cpu)
+ return tmp;
+ }
+
+ return add_cpu(cpu);
+}
+
+/*
+ * At the end of a sampling period, we need to display the cpu time for each
+ * process and to reset it to zero for the next period
+ */
+void rotate_cputime(unsigned long end)
+{
+ gint i;
+ struct cputime *tmp;
+ unsigned long elapsed;
+
+ for (i = 0; i < lttngtop.cpu_table->len; i++) {
+ tmp = g_ptr_array_index(lttngtop.cpu_table, i);
+ elapsed = end - tmp->task_start;
+ if (tmp->current_task) {
+ tmp->current_task->totalcpunsec += elapsed;
+ tmp->current_task->threadstotalcpunsec += elapsed;
+ if (tmp->current_task->pid != tmp->current_task->tid &&
+ tmp->current_task->threadparent) {
+ tmp->current_task->threadparent->threadstotalcpunsec += elapsed;
+ }
+ }
+ tmp->task_start = end;
+ }
+}
+
+void reset_perf_counter(gpointer key, gpointer value, gpointer user_data)
+{
+ ((struct perfcounter*) value)->count = 0;
+}
+
+void copy_perf_counter(gpointer key, gpointer value, gpointer new_table)
+{
+ struct perfcounter *newperf;
+
+ newperf = malloc(sizeof(struct perfcounter));
+ newperf->count = ((struct perfcounter *) value)->count;
+ newperf->visible = ((struct perfcounter *) value)->visible;
+ newperf->sort = ((struct perfcounter *) value)->sort;
+ g_hash_table_insert((GHashTable *) new_table, key, newperf);
+}
+
+void rotate_perfcounter() {
+ int i;
+ struct processtop *tmp;
+ for (i = 0; i < lttngtop.process_table->len; i++) {
+ tmp = g_ptr_array_index(lttngtop.process_table, i);
+ g_hash_table_foreach(tmp->perf, reset_perf_counter, NULL);
+ }
+}
+
+void cleanup_processtop()
+{
+ gint i;
+ struct processtop *tmp;
+
+ for (i = 0; i < lttngtop.process_table->len; i++) {
+ tmp = g_ptr_array_index(lttngtop.process_table, i);
+ tmp->totalcpunsec = 0;
+ tmp->threadstotalcpunsec = 0;
+ tmp->iostream->ret_read = 0;
+ tmp->iostream->ret_write = 0;
+ }
+}
+
+struct lttngtop* get_copy_lttngtop(unsigned long start, unsigned long end)
+{
+ gint i, j;
+ unsigned long time;
+ struct lttngtop *dst;
+ struct processtop *tmp, *tmp2, *new;
+ struct cputime *tmpcpu, *newcpu;
+ struct files *tmpfile, *newfile;
+
+ dst = malloc(sizeof(struct lttngtop));
+ dst = memset(dst, 0, sizeof(struct lttngtop));
+ dst->start = start;
+ dst->end = end;
+ dst->process_table = g_ptr_array_new();
+ dst->files_table = g_ptr_array_new();
+ dst->cpu_table = g_ptr_array_new();
+ dst->perf_list = g_hash_table_new(g_direct_hash, g_direct_equal);
+
+ rotate_cputime(end);
+
+ g_hash_table_foreach(lttngtop.perf_list, copy_perf_counter, dst->perf_list);
+ for (i = 0; i < lttngtop.process_table->len; i++) {
+ tmp = g_ptr_array_index(lttngtop.process_table, i);
+ new = malloc(sizeof(struct processtop));
+
+ memcpy(new, tmp, sizeof(struct processtop));
+ new->threads = g_ptr_array_new();
+ new->comm = strdup(tmp->comm);
+ new->process_files_table = g_ptr_array_new();
+ new->perf = g_hash_table_new(g_direct_hash, g_direct_equal);
+ g_hash_table_foreach(tmp->perf, copy_perf_counter, new->perf);
+
+ new->iostream = malloc(sizeof(struct iostream));
+ memcpy(new->iostream, tmp->iostream, sizeof(struct iostream));
+ /* compute the stream speed */
+ if (end - start != 0)
+ {
+ time = (end - start)/NSEC_PER_SEC;
+ new->iostream->ret_read = new->iostream->ret_read/(time);
+ new->iostream->ret_write = new->iostream->ret_write/(time);
+ }
+
+ for (j = 0; j < tmp->process_files_table->len; j++) {
+ tmpfile = g_ptr_array_index(tmp->process_files_table, j);
+ newfile = malloc(sizeof(struct files));
+
+ memcpy(newfile, tmpfile, sizeof(struct files));
+
+ newfile->name = strdup(tmpfile->name);
+ newfile->ref = new;
+
+ g_ptr_array_add(new->process_files_table, newfile);
+ g_ptr_array_add(dst->files_table, newfile);
+
+ /*
+ * if the process died during the last period, we remove all
+ * files associated with if after the copy
+ */
+ if (tmp->death > 0 && tmp->death < end) {
+ g_ptr_array_remove(tmp->process_files_table, tmpfile);
+ free(tmpfile);
+ }
+ }
+ g_ptr_array_add(dst->process_table, new);
+
+ /*
+ * if the process died during the last period, we remove it from
+ * the current process list after the copy
+ */
+ if (tmp->death > 0 && tmp->death < end) {
+ g_ptr_array_remove(lttngtop.process_table, tmp);
+ g_ptr_array_free(tmp->threads, TRUE);
+ free(tmp->comm);
+ g_ptr_array_free(tmp->process_files_table, TRUE);
+ g_hash_table_destroy(tmp->perf);
+ free(tmp);
+ }
+ }
+ rotate_perfcounter();
+
+ for (i = 0; i < lttngtop.cpu_table->len; i++) {
+ tmpcpu = g_ptr_array_index(lttngtop.cpu_table, i);
+ newcpu = malloc(sizeof(struct cputime));
+ memcpy(newcpu, tmpcpu, sizeof(struct cputime));
+ newcpu->perf = g_hash_table_new(g_direct_hash, g_direct_equal);
+ g_hash_table_foreach(tmpcpu->perf, copy_perf_counter, newcpu->perf);
+ /*
+ * note : we don't care about the current process pointer in the copy
+ * so the reference is invalid after the memcpy
+ */
+ g_ptr_array_add(dst->cpu_table, newcpu);
+ }
+ /* create the threads index if required */
+ for (i = 0; i < dst->process_table->len; i++) {
+ tmp = g_ptr_array_index(dst->process_table, i);
+ if (tmp->pid == tmp->tid) {
+ for (j = 0; j < dst->process_table->len; j++) {
+ tmp2 = g_ptr_array_index(dst->process_table, j);
+ if (tmp2->pid == tmp->pid) {
+ tmp2->threadparent = tmp;
+ g_ptr_array_add(tmp->threads, tmp2);
+ }
+ }
+ }
+ }
+
+ // update_global_stats(dst);
+ cleanup_processtop();
+
+ return dst;
+}
+
--- /dev/null
+/*
+ * Copyright (C) 2011 Julien Desfossez
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+#ifndef _COMMON_H
+#define _COMMON_H
+
+#include <semaphore.h>
+#include <babeltrace/ctf/events.h>
+#include "lttngtoptypes.h"
+#include "cputop.h"
+
+#define NSEC_PER_USEC 1000
+#define NSEC_PER_SEC 1000000000L
+
+sem_t goodtodisplay, goodtoupdate, timer, pause_sem, end_trace_sem, bootstrap;
+
+GPtrArray *copies; /* struct lttngtop */
+pthread_mutex_t perf_list_mutex;
+
+struct lttngtop *data;
+
+struct processtop *find_process_tid(struct lttngtop *ctx, int pid, char *comm);
+struct processtop* add_proc(struct lttngtop *ctx, int pid, char *comm,
+ unsigned long timestamp);
+struct processtop* update_proc(struct processtop* proc, int pid, int tid,
+ int ppid, char *comm);
+void add_thread(struct processtop *parent, struct processtop *thread);
+struct processtop* get_proc(struct lttngtop *ctx, int tid, char *comm,
+ unsigned long timestamp);
+void death_proc(struct lttngtop *ctx, int tid, char *comm,
+ unsigned long timestamp);
+struct cputime* add_cpu(int cpu);
+struct cputime* get_cpu(int cpu);
+struct lttngtop* get_copy_lttngtop(unsigned long start, unsigned long end);
+struct perfcounter *add_perf_counter(GPtrArray *perf, GQuark quark,
+ unsigned long count);
+struct perfcounter *get_perf_counter(const char *name, struct processtop *proc,
+ struct cputime *cpu);
+
+#endif /* _COMMON_H */
--- /dev/null
+/*
+ * Copyright (C) 2011 Julien Desfossez
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+#include <babeltrace/babeltrace.h>
+
+#include "lttngtoptypes.h"
+#include "common.h"
+#include "cputop.h"
+
+void update_cputop_data(unsigned long timestamp, int64_t cpu, int prev_pid,
+ int next_pid, char *prev_comm, char *next_comm)
+{
+ struct cputime *tmpcpu;
+ unsigned long elapsed;
+
+ tmpcpu = get_cpu(cpu);
+
+ if (tmpcpu->current_task && tmpcpu->current_task->pid == prev_pid) {
+ elapsed = timestamp - tmpcpu->task_start;
+ tmpcpu->current_task->totalcpunsec += elapsed;
+ tmpcpu->current_task->threadstotalcpunsec += elapsed;
+ if (tmpcpu->current_task->pid != tmpcpu->current_task->tid)
+ tmpcpu->current_task->threadparent->threadstotalcpunsec += elapsed;
+ }
+
+ if (next_pid != 0)
+ tmpcpu->current_task = get_proc(<tngtop, next_pid, next_comm, timestamp);
+ else
+ tmpcpu->current_task = NULL;
+
+ tmpcpu->task_start = timestamp;
+}
+
+enum bt_cb_ret handle_sched_switch(struct bt_ctf_event *call_data,
+ void *private_data)
+{
+ struct definition *scope;
+ unsigned long timestamp;
+ uint64_t cpu_id;
+ char *prev_comm, *next_comm;
+ int prev_tid, next_tid;
+
+ timestamp = bt_ctf_get_timestamp(call_data);
+ if (timestamp == -1ULL)
+ goto error;
+
+ scope = bt_ctf_get_top_level_scope(call_data,
+ BT_EVENT_FIELDS);
+ prev_comm = bt_ctf_get_char_array(bt_ctf_get_field(call_data,
+ scope, "_prev_comm"));
+ if (bt_ctf_field_get_error()) {
+ fprintf(stderr, "Missing prev_comm context info\n");
+ goto error;
+ }
+
+ next_comm = bt_ctf_get_char_array(bt_ctf_get_field(call_data,
+ scope, "_next_comm"));
+ if (bt_ctf_field_get_error()) {
+ fprintf(stderr, "Missing next_comm context info\n");
+ goto error;
+ }
+
+ prev_tid = bt_ctf_get_int64(bt_ctf_get_field(call_data,
+ scope, "_prev_tid"));
+ if (bt_ctf_field_get_error()) {
+ fprintf(stderr, "Missing prev_tid context info\n");
+ goto error;
+ }
+
+ next_tid = bt_ctf_get_int64(bt_ctf_get_field(call_data,
+ scope, "_next_tid"));
+ if (bt_ctf_field_get_error()) {
+ fprintf(stderr, "Missing next_tid context info\n");
+ goto error;
+ }
+
+ scope = bt_ctf_get_top_level_scope(call_data,
+ BT_STREAM_PACKET_CONTEXT);
+ cpu_id = bt_ctf_get_uint64(bt_ctf_get_field(call_data,
+ scope, "cpu_id"));
+ if (bt_ctf_field_get_error()) {
+ fprintf(stderr, "Missing cpu_id context info\n");
+ goto error;
+ }
+
+ update_cputop_data(timestamp, cpu_id, prev_tid, next_tid,
+ prev_comm, next_comm);
+
+ return BT_CB_OK;
+
+error:
+ return BT_CB_ERROR_STOP;
+}
+
+enum bt_cb_ret handle_sched_process_free(struct bt_ctf_event *call_data,
+ void *private_data)
+{
+ struct definition *scope;
+ unsigned long timestamp;
+ char *comm;
+ int tid;
+
+ timestamp = bt_ctf_get_timestamp(call_data);
+ if (timestamp == -1ULL)
+ goto error;
+
+ scope = bt_ctf_get_top_level_scope(call_data,
+ BT_EVENT_FIELDS);
+ comm = bt_ctf_get_char_array(bt_ctf_get_field(call_data,
+ scope, "_comm"));
+ if (bt_ctf_field_get_error()) {
+ fprintf(stderr, "Missing procname context info\n");
+ goto error;
+ }
+
+ tid = bt_ctf_get_int64(bt_ctf_get_field(call_data,
+ scope, "_tid"));
+ if (bt_ctf_field_get_error()) {
+ fprintf(stderr, "Missing tid context info\n");
+ goto error;
+ }
+
+ death_proc(<tngtop, tid, comm, timestamp);
+
+ return BT_CB_OK;
+
+error:
+ return BT_CB_ERROR_STOP;
+
+}
+
--- /dev/null
+/*
+ * Copyright (C) 2011 Julien Desfossez
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+#ifndef _LTTNGTOP_H
+#define _LTTNGTOP_H
+
+#include <babeltrace/babeltrace.h>
+#include <babeltrace/ctf/callbacks.h>
+#include <inttypes.h>
+#include <glib.h>
+
+enum bt_cb_ret handle_sched_switch(struct bt_ctf_event *hook_data,
+ void *call_data);
+
+enum bt_cb_ret handle_sched_process_free(struct bt_ctf_event *call_data,
+ void *private_data);
+
+#endif /* _LTTNGTOP_H */
--- /dev/null
+/*
+ * Copyright (C) 2011 Julien Desfossez
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <ncurses.h>
+#include <panel.h>
+#include <pthread.h>
+#include <semaphore.h>
+
+#include "cursesdisplay.h"
+#include "lttngtoptypes.h"
+#include "common.h"
+
+#define DEFAULT_DELAY 15
+#define MAX_LINE_LENGTH 50
+#define MAX_LOG_LINES 4
+
+/* to prevent concurrent updates of the different windows */
+sem_t update_display_sem;
+
+char *termtype;
+WINDOW *footer, *header, *center, *status;
+WINDOW *perf_panel_window = NULL;
+PANEL *perf_panel, *main_panel;
+
+int perf_panel_visible = 0;
+int perf_line_selected = 0;
+
+int last_display_index, currently_displayed_index;
+
+struct processtop *selected_process = NULL;
+int selected_tid;
+char *selected_comm;
+int selected_ret;
+
+int selected_line = 0; /* select bar position */
+int selected_in_list = 0; /* selection relative to the whole list */
+int list_offset = 0; /* first index in the list to display (scroll) */
+int nb_log_lines = 0;
+char log_lines[MAX_LINE_LENGTH * MAX_LOG_LINES + MAX_LOG_LINES];
+
+int max_elements = 80;
+
+int toggle_threads = -1;
+int toggle_pause = -1;
+int toggle_tree = -1;
+
+int max_center_lines;
+
+pthread_t keyboard_thread;
+
+void reset_ncurses()
+{
+ curs_set(1);
+ endwin();
+ exit(0);
+}
+
+static void handle_sigterm(int signal)
+{
+ reset_ncurses();
+}
+
+void init_screen()
+{
+ initscr();
+ noecho();
+ halfdelay(DEFAULT_DELAY);
+ nonl();
+ intrflush(stdscr, false);
+ keypad(stdscr, true);
+ curs_set(0);
+
+ if (has_colors()) {
+ start_color();
+ init_pair(1, COLOR_RED, COLOR_BLACK); /* - */
+ init_pair(2, COLOR_GREEN, COLOR_BLACK); /* + */
+ init_pair(3, COLOR_BLACK, COLOR_WHITE); /* keys */
+ init_pair(4, COLOR_WHITE, COLOR_GREEN); /* keys activated */
+ init_pair(5, COLOR_WHITE, COLOR_BLUE); /* select line */
+ }
+ termtype = getenv("TERM");
+ if (!strcmp(termtype, "xterm") || !strcmp(termtype, "xterm-color") ||
+ !strcmp(termtype, "vt220")) {
+ define_key("\033[H", KEY_HOME);
+ define_key("\033[F", KEY_END);
+ define_key("\033OP", KEY_F(1));
+ define_key("\033OQ", KEY_F(2));
+ define_key("\033OR", KEY_F(3));
+ define_key("\033OS", KEY_F(4));
+ define_key("\0330U", KEY_F(6));
+ define_key("\033[11~", KEY_F(1));
+ define_key("\033[12~", KEY_F(2));
+ define_key("\033[13~", KEY_F(3));
+ define_key("\033[14~", KEY_F(4));
+ define_key("\033[16~", KEY_F(6));
+ define_key("\033[17;2~", KEY_F(18));
+ }
+ signal(SIGTERM, handle_sigterm);
+ mousemask(BUTTON1_CLICKED, NULL);
+ refresh();
+}
+
+WINDOW *create_window(int height, int width, int startx, int starty)
+{
+ WINDOW *win;
+ win = newwin(height, width, startx, starty);
+ box(win, 0 , 0);
+ wrefresh(win);
+ return win;
+}
+
+WINDOW *create_window_no_border(int height, int width, int startx, int starty)
+{
+ WINDOW *win;
+ win = newwin(height, width, startx, starty);
+ wrefresh(win);
+ return win;
+}
+
+void print_digit(WINDOW *win, int digit)
+{
+ if (digit < 0) {
+ wattron(win, COLOR_PAIR(1));
+ wprintw(win, "%d", digit);
+ wattroff(win, COLOR_PAIR(1));
+ } else if (digit > 0) {
+ wattron(win, COLOR_PAIR(2));
+ wprintw(win, "+%d", digit);
+ wattroff(win, COLOR_PAIR(2));
+ } else {
+ wprintw(win, "0");
+ }
+}
+
+void print_digits(WINDOW *win, int first, int second)
+{
+ wprintw(win, "(");
+ print_digit(win, first);
+ wprintw(win, ", ");
+ print_digit(win, second);
+ wprintw(win, ")");
+}
+
+void print_headers(int line, char *desc, int value, int first, int second)
+{
+ wattron(header, A_BOLD);
+ mvwprintw(header, line, 4, "%s", desc);
+ wattroff(header, A_BOLD);
+ mvwprintw(header, line, 16, "N/A", value);
+ wmove(header, line, 24);
+ print_digits(header, first, second);
+ wmove(header, line, 40);
+}
+
+void set_window_title(WINDOW *win, char *title)
+{
+ wattron(win, A_BOLD);
+ mvwprintw(win, 0, 1, title);
+ wattroff(win, A_BOLD);
+}
+
+void print_log(char *str)
+{
+ int i;
+ int current_line = 1;
+ int current_char = 1;
+ char *tmp, *tmp2;
+ /* rotate the line buffer */
+ if (nb_log_lines >= MAX_LOG_LINES) {
+ tmp = strndup(log_lines, MAX_LINE_LENGTH * MAX_LOG_LINES + MAX_LOG_LINES);
+ tmp2 = strchr(tmp, '\n');
+ memset(log_lines, '\0', strlen(log_lines));
+ strncat(log_lines, tmp2 + 1, strlen(tmp2) - 1);
+ log_lines[strlen(log_lines)] = '\n';
+ log_lines[strlen(log_lines)] = '\0';
+ free(tmp);
+ }
+ nb_log_lines++;
+
+ strncat(log_lines, str, MAX_LINE_LENGTH - 1);
+
+ if (nb_log_lines < MAX_LOG_LINES)
+ log_lines[strlen(log_lines)] = '\n';
+ log_lines[strlen(log_lines)] = '\0';
+
+ werase(status);
+ box(status, 0 , 0);
+ set_window_title(status, "Status");
+ for (i = 0; i < strlen(log_lines); i++) {
+ if (log_lines[i] == '\n') {
+ wmove(status, ++current_line, 1);
+ current_char = 1;
+ } else {
+ mvwprintw(status, current_line, current_char++, "%c", log_lines[i]);
+ }
+ }
+ wrefresh(status);
+}
+
+void print_key(WINDOW *win, char *key, char *desc, int toggle)
+{
+ int pair;
+ if (toggle > 0)
+ pair = 4;
+ else
+ pair = 3;
+ wattron(win, COLOR_PAIR(pair));
+ wprintw(footer, "%s", key);
+ wattroff(win, COLOR_PAIR(pair));
+ wprintw(footer, ":%s", desc);
+}
+
+void update_footer()
+{
+ sem_wait(&update_display_sem);
+ werase(footer);
+ wmove(footer, 1, 1);
+ print_key(footer, "F2", "CPUtop ", current_view == cpu);
+ print_key(footer, "F3", "PerfTop ", current_view == perf);
+ print_key(footer, "F6", "IOTop ", current_view == iostream);
+ print_key(footer, "Enter", "Details ", current_view == process_details);
+ print_key(footer, "q", "Quit | ", 0);
+ print_key(footer, "P", "Perf Pref ", 0);
+ print_key(footer, "p", "Pause ", toggle_pause);
+
+ wrefresh(footer);
+ sem_post(&update_display_sem);
+}
+
+void basic_header()
+{
+ werase(header);
+ box(header, 0 , 0);
+ set_window_title(header, "Statistics for interval [gathering data...[");
+ wattron(header, A_BOLD);
+ mvwprintw(header, 1, 4, "CPUs");
+ mvwprintw(header, 2, 4, "Processes");
+ mvwprintw(header, 3, 4, "Threads");
+ mvwprintw(header, 4, 4, "Files");
+ mvwprintw(header, 5, 4, "Network");
+ mvwprintw(header, 6, 4, "IO");
+ wattroff(header, A_BOLD);
+ wrefresh(header);
+}
+
+void update_header()
+{
+ werase(header);
+ box(header, 0 , 0);
+ set_window_title(header, "Statistics for interval ");
+ wattron(header, A_BOLD);
+ /*
+ wprintw(header, "[%lu.%lu, %lu.%lu[",
+ data->start.tv_sec, data->start.tv_nsec,
+ data->end.tv_sec, data->end.tv_nsec);
+ */
+ wprintw(header, "[%lu, %lu[",
+ data->start,
+ data->end);
+ mvwprintw(header, 1, 4, "CPUs");
+ wattroff(header, A_BOLD);
+ wprintw(header, "\t%d\t(max/cpu : %0.2f%)", data->cpu_table->len,
+ 100.0/data->cpu_table->len);
+ print_headers(2, "Processes", data->nbproc, data->nbnewproc,
+ -1*(data->nbdeadproc));
+ print_headers(3, "Threads", data->nbthreads, data->nbnewthreads,
+ -1*(data->nbdeadthreads));
+ print_headers(4, "Files", data->nbfiles, data->nbnewfiles,
+ -1*(data->nbclosedfiles));
+ mvwprintw(header, 4, 43, "N/A kbytes/sec");
+ print_headers(5, "Network", 114, 0, 0);
+ mvwprintw(header, 5, 43, "N/A Mbytes/sec");
+ wrefresh(header);
+}
+
+gint sort_by_cpu_desc(gconstpointer p1, gconstpointer p2)
+{
+ struct processtop *n1 = *(struct processtop **)p1;
+ struct processtop *n2 = *(struct processtop **)p2;
+ unsigned long totaln1 = n1->totalcpunsec;
+ unsigned long totaln2 = n2->totalcpunsec;
+
+ if (totaln1 < totaln2)
+ return 1;
+ if (totaln1 == totaln2)
+ return 0;
+ return -1;
+}
+
+gint sort_by_cpu_group_by_threads_desc(gconstpointer p1, gconstpointer p2)
+{
+ struct processtop *n1 = *(struct processtop **)p1;
+ struct processtop *n2 = *(struct processtop **)p2;
+ unsigned long totaln1 = n1->threadstotalcpunsec;
+ unsigned long totaln2 = n2->threadstotalcpunsec;
+
+ if (totaln1 < totaln2)
+ return 1;
+ if (totaln1 == totaln2)
+ return 0;
+ return -1;
+}
+
+void update_cputop_display()
+{
+ int i;
+ int header_offset = 2;
+ struct processtop *tmp;
+ unsigned long elapsed;
+ double maxcputime;
+ int nblinedisplayed = 0;
+ int current_line = 0;
+
+ elapsed = data->end - data->start;
+ maxcputime = elapsed * data->cpu_table->len / 100.0;
+
+ g_ptr_array_sort(data->process_table, sort_by_cpu_desc);
+
+ set_window_title(center, "CPU Top");
+ wattron(center, A_BOLD);
+ mvwprintw(center, 1, 1, "CPU(%)");
+ mvwprintw(center, 1, 12, "TGID");
+ mvwprintw(center, 1, 22, "PID");
+ mvwprintw(center, 1, 32, "NAME");
+ wattroff(center, A_BOLD);
+
+ max_center_lines = LINES - 7 - 7 - 1 - header_offset;
+
+ /* iterate the process (thread) list */
+ for (i = list_offset; i < data->process_table->len &&
+ nblinedisplayed < max_center_lines; i++) {
+ tmp = g_ptr_array_index(data->process_table, i);
+
+ if (current_line == selected_line) {
+ selected_process = tmp;
+ selected_tid = tmp->tid;
+ selected_comm = tmp->comm;
+ wattron(center, COLOR_PAIR(5));
+ mvwhline(center, current_line + header_offset, 1, ' ', COLS-3);
+ }
+ /* CPU(%) */
+ mvwprintw(center, current_line + header_offset, 1, "%1.2f",
+ tmp->totalcpunsec / maxcputime);
+ /* TGID */
+ mvwprintw(center, current_line + header_offset, 12, "%d", tmp->pid);
+ /* PID */
+ mvwprintw(center, current_line + header_offset, 22, "%d", tmp->tid);
+ /* NAME */
+ mvwprintw(center, current_line + header_offset, 32, "%s", tmp->comm);
+ wattroff(center, COLOR_PAIR(5));
+ nblinedisplayed++;
+ current_line++;
+ }
+}
+
+gint sort_perf(gconstpointer p1, gconstpointer p2, gpointer key)
+{
+ struct processtop *n1 = *(struct processtop **) p1;
+ struct processtop *n2 = *(struct processtop **) p2;
+
+ struct perfcounter *tmp1, *tmp2;
+ unsigned long totaln2 = 0;
+ unsigned long totaln1 = 0;
+
+ if (!key)
+ return 0;
+
+ tmp1 = g_hash_table_lookup(n1->perf, key);
+ if (!tmp1)
+ totaln1 = 0;
+ else
+ totaln1 = tmp1->count;
+
+ tmp2 = g_hash_table_lookup(n2->perf, key);
+ if (!tmp2)
+ totaln2 = 0;
+ else
+ totaln2 = tmp2->count;
+
+ if (totaln1 < totaln2)
+ return 1;
+ if (totaln1 == totaln2) {
+ totaln1 = n1->tid;
+ totaln2 = n2->tid;
+ if (totaln1 < totaln2)
+ return 1;
+ return -1;
+ }
+ return -1;
+}
+
+void print_key_title(char *key, int line)
+{
+ wattron(center, A_BOLD);
+ mvwprintw(center, line, 1, "%s\t", key);
+ wattroff(center, A_BOLD);
+}
+
+void update_process_details()
+{
+ unsigned long elapsed;
+ double maxcputime;
+ struct processtop *tmp = find_process_tid(data, selected_tid, selected_comm);
+
+ set_window_title(center, "Process details");
+
+
+ elapsed = data->end - data->start;
+ maxcputime = elapsed * data->cpu_table->len / 100.0;
+
+ print_key_title("Name", 1);
+ wprintw(center, "%s", selected_comm);
+ print_key_title("TID", 2);
+ wprintw(center, "%d", selected_tid);
+ if (!tmp) {
+ print_key_title("Does not exit at this time", 3);
+ return;
+ }
+
+ print_key_title("PID", 3);
+ wprintw(center, "%d", tmp->pid);
+ print_key_title("PPID", 4);
+ wprintw(center, "%d", tmp->ppid);
+ print_key_title("CPU", 5);
+ wprintw(center, "%1.2f %%", tmp->totalcpunsec/maxcputime);
+}
+
+void update_perf()
+{
+ int i, j;
+ int nblinedisplayed = 0;
+ int current_line = 0;
+ struct processtop *tmp;
+ int header_offset = 2;
+ int perf_row = 40;
+ struct perfcounter *perfn1, *perfn2;
+ GList *perflist;
+ char *perf_key = NULL;
+ int value;
+
+ set_window_title(center, "Perf Top");
+ wattron(center, A_BOLD);
+ mvwprintw(center, 1, 1, "PID");
+ mvwprintw(center, 1, 11, "TID");
+ mvwprintw(center, 1, 22, "NAME");
+
+ perf_row = 40;
+ perflist = g_list_first(g_hash_table_get_keys(data->perf_list));
+ while (perflist) {
+ perfn1 = g_hash_table_lookup(data->perf_list, perflist->data);
+ /* + 6 to strip the "_perf_" prefix */
+ if (perfn1->visible) {
+ mvwprintw(center, 1, perf_row, "%s",
+ (char *) perflist->data + 6);
+ perf_row += 20;
+ }
+ if (perfn1->sort) {
+ perf_key = (char *) perflist->data;
+ }
+ perflist = g_list_next(perflist);
+ }
+ wattroff(center, A_BOLD);
+
+ g_ptr_array_sort_with_data(data->process_table, sort_perf, perf_key);
+ for (i = 0; i < data->process_table->len &&
+ nblinedisplayed < max_center_lines; i++) {
+ GList *perf_keys;
+ tmp = g_ptr_array_index(data->process_table, i);
+
+ if (current_line == selected_line) {
+ selected_process = tmp;
+ wattron(center, COLOR_PAIR(5));
+ mvwhline(center, current_line + header_offset, 1, ' ', COLS-3);
+ }
+
+ mvwprintw(center, current_line + header_offset, 1, "%d", tmp->pid);
+ mvwprintw(center, current_line + header_offset, 11, "%d", tmp->tid);
+ mvwprintw(center, current_line + header_offset, 22, "%s", tmp->comm);
+
+ /* FIXME : sometimes there is a segfault here, I have no idea why :-( */
+ perf_keys = g_hash_table_get_keys(data->perf_list);
+ if (perf_keys)
+ perflist = g_list_first(perf_keys);
+ else
+ perflist = NULL;
+
+ perf_row = 40;
+ j = 0;
+ while (perflist) {
+ j++;
+ perfn1 = g_hash_table_lookup(data->perf_list, perflist->data);
+ if (!perfn1) {
+ perflist = g_list_next(perflist);
+ continue;
+ }
+ if (perfn1->visible) {
+ perfn2 = g_hash_table_lookup(tmp->perf, perflist->data);
+ if (perfn2)
+ value = perfn2->count;
+ else
+ value = 0;
+ mvwprintw(center, current_line + header_offset, perf_row, "%d", value);
+ perf_row += 20;
+ }
+ perflist = g_list_next(perflist);
+ }
+
+ wattroff(center, COLOR_PAIR(5));
+ nblinedisplayed++;
+ current_line++;
+ }
+}
+
+void update_fileio()
+{
+ int i;
+ int offset;
+
+ set_window_title(center, "IO Top");
+ wattron(center, A_BOLD);
+ mvwprintw(center, 1, 10, "READ");
+ mvwprintw(center, 2, 1, "bytes");
+ mvwprintw(center, 2, 15, "bytes/sec");
+
+ mvwprintw(center, 1, 39, "WRITE");
+ mvwprintw(center, 2, 33, "bytes");
+ mvwprintw(center, 2, 45, "bytes/sec");
+
+ if (toggle_threads > 0) {
+ mvwprintw(center, 1, 60, "TGID");
+ mvwprintw(center, 1, 70, "PID");
+ offset = 8;
+ } else {
+ mvwprintw(center, 1, 60, "PID(TGID)");
+ offset = 0;
+ }
+ mvwprintw(center, 1, 72 + offset, "NAME");
+ wattroff(center, A_BOLD);
+
+ for (i = 3; i < LINES - 3 - 8 - 1; i++) {
+ mvwprintw(center, i, 1, "%d", i*1000);
+ mvwprintw(center, i, 15, "%dk", i);
+ mvwprintw(center, i, 28, "| %d", i*2000);
+ mvwprintw(center, i, 45, "%dk", i*2);
+ if (toggle_threads > 0) {
+ mvwprintw(center, i, 57, "| %d", i);
+ mvwprintw(center, i, 70, "%d", i);
+ } else {
+ mvwprintw(center, i, 57, "| %d", i);
+ }
+ mvwprintw(center, i, 72 + offset, "process_%d", i);
+ }
+}
+
+gint sort_by_ret_desc(gconstpointer p1, gconstpointer p2)
+{
+ struct processtop *n1 = *(struct processtop **)p1;
+ struct processtop *n2 = *(struct processtop **)p2;
+ unsigned long totaln1 = n1->iostream->ret_total;
+ unsigned long totaln2 = n2->iostream->ret_total;
+
+ if (totaln1 < totaln2)
+ return 1;
+ if (totaln1 == totaln2)
+ return 0;
+ return -1;
+}
+
+void update_iostream()
+{
+ int i;
+ int header_offset = 2;
+ struct processtop *tmp;
+ int nblinedisplayed = 0;
+ int current_line = 0;
+
+ set_window_title(center, "IO Top");
+ wattron(center, A_BOLD);
+ mvwprintw(center, 1, 1, "READ (B/s)");
+ mvwprintw(center, 1, 20, "WRITE (B/s)");
+
+ mvwprintw(center, 1, 40, "TOTAL STREAM");
+
+ mvwprintw(center, 1, 60, "TGID");
+ mvwprintw(center, 1, 80, "PID");
+
+ mvwprintw(center, 1, 92, "NAME");
+ wattroff(center, A_BOLD);
+
+ g_ptr_array_sort(data->process_table, sort_by_ret_desc);
+
+ for (i = list_offset; i < data->process_table->len &&
+ nblinedisplayed < max_center_lines; i++) {
+ tmp = g_ptr_array_index(data->process_table, i);
+
+ if (current_line == selected_line) {
+ selected_process = tmp;
+ selected_tid = tmp->tid;
+ selected_comm = tmp->comm;
+ wattron(center, COLOR_PAIR(5));
+ mvwhline(center, current_line + header_offset, 1, ' ', COLS-3);
+ }
+ /* READ (bytes/sec) */
+ mvwprintw(center, current_line + header_offset, 1, "%lu",
+ tmp->iostream->ret_read);
+
+ /* WRITE (bytes/sec) */
+ mvwprintw(center, current_line + header_offset, 20, "%lu",
+ tmp->iostream->ret_write);
+
+ /* TOTAL STREAM */
+ if(tmp->iostream->ret_total >= 1000000)
+ mvwprintw(center, current_line + header_offset, 40, "%lu MB",
+ tmp->iostream->ret_total/1000000);
+ else if(tmp->iostream->ret_total >=1000)
+ mvwprintw(center, current_line + header_offset, 40, "%lu KB",
+ tmp->iostream->ret_total/1000);
+ else
+ mvwprintw(center, current_line + header_offset, 40, "%lu B",
+ tmp->iostream->ret_total);
+ /* TGID */
+ mvwprintw(center, current_line + header_offset, 60, "%d", tmp->pid);
+ /* PID */
+ mvwprintw(center, current_line + header_offset, 80, "%d", tmp->tid);
+ /* NAME */
+ mvwprintw(center, current_line + header_offset, 92, "%s", tmp->comm);
+ wattroff(center, COLOR_PAIR(5));
+ nblinedisplayed++;
+ current_line++;
+ }
+}
+
+void update_current_view()
+{
+ sem_wait(&update_display_sem);
+ if (!data)
+ return;
+ update_header();
+
+ werase(center);
+ box(center, 0, 0);
+ switch (current_view) {
+ case cpu:
+ update_cputop_display();
+ break;
+ case perf:
+ update_perf();
+ break;
+ case process_details:
+ update_process_details();
+ break;
+ case fileio:
+ update_fileio();
+ break;
+ case iostream:
+ update_iostream();
+ break;
+ case tree:
+ update_cputop_display();
+ break;
+ default:
+ break;
+ }
+ update_panels();
+ doupdate();
+ sem_post(&update_display_sem);
+}
+
+void setup_perf_panel()
+{
+ int size;
+ if (!data)
+ return;
+ if (perf_panel_window) {
+ del_panel(perf_panel);
+ delwin(perf_panel_window);
+ }
+ size = g_hash_table_size(data->perf_list);
+ perf_panel_window = create_window(size + 2, 30, 10, 10);
+ perf_panel = new_panel(perf_panel_window);
+ perf_panel_visible = 0;
+ hide_panel(perf_panel);
+}
+
+void update_perf_panel(int line_selected, int toggle_view, int toggle_sort)
+{
+ int i;
+ struct perfcounter *perf;
+ GList *perflist;
+
+ if (!data)
+ return;
+
+ werase(perf_panel_window);
+ box(perf_panel_window, 0 , 0);
+ set_window_title(perf_panel_window, "Perf Preferences ");
+ wattron(perf_panel_window, A_BOLD);
+ mvwprintw(perf_panel_window, g_hash_table_size(data->perf_list) + 1, 1, " 's' to sort");
+ wattroff(perf_panel_window, A_BOLD);
+
+ if (toggle_sort == 1) {
+ i = 0;
+ perflist = g_list_first(g_hash_table_get_keys(data->perf_list));
+ while (perflist) {
+ perf = g_hash_table_lookup(data->perf_list, perflist->data);
+ if (i != line_selected)
+ perf->sort = 0;
+ else
+ perf->sort = 1;
+ i++;
+ perflist = g_list_next(perflist);
+ }
+ update_current_view();
+ }
+
+ i = 0;
+ perflist = g_list_first(g_hash_table_get_keys(data->perf_list));
+ while (perflist) {
+ perf = g_hash_table_lookup(data->perf_list, perflist->data);
+ if (i == line_selected && toggle_view == 1) {
+ perf->visible = perf->visible == 1 ? 0:1;
+ update_current_view();
+ }
+ if (i == line_selected) {
+ wattron(perf_panel_window, COLOR_PAIR(5));
+ mvwhline(perf_panel_window, i + 1, 1, ' ', 30 - 2);
+ }
+ if (perf->sort == 1)
+ wattron(perf_panel_window, A_BOLD);
+ mvwprintw(perf_panel_window, i + 1, 1, "[%c] %s",
+ perf->visible == 1 ? 'x' : ' ',
+ (char *) perflist->data + 6);
+ wattroff(perf_panel_window, A_BOLD);
+ wattroff(perf_panel_window, COLOR_PAIR(5));
+ i++;
+ perflist = g_list_next(perflist);
+ }
+ update_panels();
+ doupdate();
+}
+
+
+void toggle_perf_panel(void)
+{
+ if (perf_panel_visible) {
+ hide_panel(perf_panel);
+ perf_panel_visible = 0;
+ } else {
+ setup_perf_panel();
+ update_perf_panel(perf_line_selected, 0, 0);
+ show_panel(perf_panel);
+ perf_panel_visible = 1;
+ }
+ update_panels();
+ doupdate();
+}
+
+void display(unsigned int index)
+{
+ last_display_index = index;
+ currently_displayed_index = index;
+ data = g_ptr_array_index(copies, index);
+ if (!data)
+ return;
+ max_elements = data->process_table->len;
+ update_current_view();
+ update_footer();
+ update_panels();
+ doupdate();
+}
+
+void pause_display()
+{
+ toggle_pause = 1;
+ print_log("Pause");
+ sem_wait(&pause_sem);
+}
+
+void resume_display()
+{
+ toggle_pause = -1;
+ print_log("Resume");
+ sem_post(&pause_sem);
+}
+
+void *handle_keyboard(void *p)
+{
+ int ch;
+ while((ch = getch())) {
+ switch(ch) {
+ /* Move the cursor and scroll */
+ case KEY_DOWN:
+ if (perf_panel_visible) {
+ if (perf_line_selected < g_hash_table_size(data->perf_list) - 1)
+ perf_line_selected++;
+ update_perf_panel(perf_line_selected, 0, 0);
+ } else {
+ if (selected_line < (max_center_lines - 1) &&
+ selected_line < max_elements - 1) {
+ selected_line++;
+ selected_in_list++;
+ } else if (selected_in_list < (max_elements - 1)
+ && (list_offset < (max_elements - max_center_lines))) {
+ selected_in_list++;
+ list_offset++;
+ }
+ update_current_view();
+ }
+ break;
+ case KEY_NPAGE:
+ if ((selected_line + 10 < max_center_lines - 1) &&
+ ((selected_line + 10) < max_elements - 1)) {
+ selected_line += 10;
+ selected_in_list += 10;
+ } else if (max_elements > max_center_lines) {
+ selected_line = max_center_lines - 1;
+ if (selected_in_list + 10 < max_elements - 1) {
+ selected_in_list += 10;
+ list_offset += (selected_in_list - max_center_lines + 1);
+ }
+ } else if (selected_line + 10 > max_elements) {
+ selected_line = max_elements - 1;
+ }
+ update_current_view();
+ break;
+ case KEY_UP:
+ if (perf_panel_visible) {
+ if (perf_line_selected > 0)
+ perf_line_selected--;
+ update_perf_panel(perf_line_selected, 0, 0);
+ } else {
+ if (selected_line > 0) {
+ selected_line--;
+ selected_in_list--;
+ } else if (selected_in_list > 0 && list_offset > 0) {
+ selected_in_list--;
+ list_offset--;
+ }
+ update_current_view();
+ }
+ break;
+ case KEY_PPAGE:
+ if (selected_line - 10 > 0)
+ selected_line -= 10;
+ else
+ selected_line = 0;
+ update_current_view();
+ break;
+
+ /* Navigate the history with arrows */
+ case KEY_LEFT:
+ if (currently_displayed_index > 0) {
+ currently_displayed_index--;
+ print_log("Going back in time");
+ } else {
+ print_log("Cannot rewind, last data is already displayed");
+ }
+ data = g_ptr_array_index(copies, currently_displayed_index);
+ max_elements = data->process_table->len;
+
+ /* we force to pause the display when moving in time */
+ if (toggle_pause < 0)
+ pause_display();
+
+ update_current_view();
+ update_footer();
+ break;
+ case KEY_RIGHT:
+ if (currently_displayed_index < last_display_index) {
+ currently_displayed_index++;
+ print_log("Going forward in time");
+ data = g_ptr_array_index(copies, currently_displayed_index);
+ max_elements = data->process_table->len;
+ update_current_view();
+ update_footer();
+ } else {
+ print_log("Manually moving forward");
+ sem_post(&timer);
+ /* we force to resume the refresh when moving forward */
+ if (toggle_pause > 0)
+ resume_display();
+ }
+
+ break;
+ case ' ':
+ if (perf_panel_visible)
+ update_perf_panel(perf_line_selected, 1, 0);
+ break;
+ case 's':
+ if (perf_panel_visible)
+ update_perf_panel(perf_line_selected, 0, 1);
+ break;
+
+ case 13: /* FIXME : KEY_ENTER ?? */
+ if (current_view == cpu) {
+ current_view = process_details;
+ }
+ update_current_view();
+ break;
+
+ case KEY_F(1):
+ toggle_tree *= -1;
+ current_view = cpu;
+ update_current_view();
+ break;
+ case KEY_F(2):
+ current_view = cpu;
+ update_current_view();
+ break;
+ case KEY_F(3):
+ current_view = perf;
+ toggle_tree = -1;
+ update_current_view();
+ break;
+ case KEY_F(4):
+ current_view = fileio;
+ toggle_tree = -1;
+ update_current_view();
+ break;
+ case KEY_F(5):
+ current_view = netio;
+ toggle_tree = -1;
+ update_current_view();
+ break;
+ case KEY_F(6):
+ current_view = iostream;
+ toggle_tree = -1;
+ update_current_view();
+ break;
+ case KEY_F(10):
+ case 'q':
+ reset_ncurses();
+ break;
+ case 't':
+ toggle_threads *= -1;
+ update_current_view();
+ break;
+ case 'p':
+ if (toggle_pause < 0) {
+ pause_display();
+ } else {
+ resume_display();
+ }
+ break;
+ case 'P':
+ toggle_perf_panel();
+ break;
+ default:
+ /*
+ * commented because it makes the list refresh in different order
+ * if we sort and there are equal values
+ if (data)
+ update_current_view();
+ */
+ break;
+ }
+ update_footer();
+ }
+ return NULL;
+}
+
+void init_ncurses()
+{
+ sem_init(&update_display_sem, 0, 1);
+ init_screen();
+
+ header = create_window(7, COLS - 1, 0, 0);
+ center = create_window(LINES - 7 - 7, COLS - 1, 7, 0);
+ status = create_window(MAX_LOG_LINES + 2, COLS - 1, LINES - 7, 0);
+ footer = create_window(1, COLS - 1, LINES - 1, 0);
+
+ print_log("Starting display");
+
+ main_panel = new_panel(center);
+ setup_perf_panel();
+
+ current_view = cpu;
+
+ basic_header();
+ update_footer();
+
+ pthread_create(&keyboard_thread, NULL, handle_keyboard, (void *)NULL);
+}
+
--- /dev/null
+/*
+ * Copyright (C) 2011 Julien Desfossez
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+#ifndef CURSESDISPLAY_H
+#define CURSESDISPLAY_H
+
+#include <glib.h>
+#include <ncurses.h>
+#include "common.h"
+
+enum current_view_list
+{
+ cpu = 1,
+ perf,
+ process_details,
+ fileio,
+ netio,
+ iostream,
+ tree,
+} current_view;
+
+void display(unsigned int);
+void init_ncurses();
+void reset_ncurses();
+
+#endif // CURSESDISPLAY_H
--- /dev/null
+/*
+ * Copyright (C) 2011 Mathieu Bain <mathieu.bain@polymtl.ca>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+#include <babeltrace/babeltrace.h>
+
+#include "lttngtoptypes.h"
+#include "common.h"
+#include "iostreamtop.h"
+
+#include <stdlib.h>
+
+int update_iostream_ret(struct lttngtop *ctx, int tid, char *comm,
+ unsigned long timestamp, int cpu_id, int ret)
+{
+ struct processtop *tmp;
+ int err = 0;
+
+ tmp = get_proc(ctx, tid, comm, timestamp);
+ if ((tmp->iostream->syscall_info != NULL) && (tmp->iostream->syscall_info->cpu_id == cpu_id)) {
+ if (tmp->iostream->syscall_info->type == __NR_read && ret > 0) {
+ tmp->iostream->ret_read += ret;
+ tmp->iostream->ret_total += ret;
+ } else if(tmp->iostream->syscall_info->type == __NR_write && ret > 0) {
+ tmp->iostream->ret_write += ret;
+ tmp->iostream->ret_total += ret;
+ } else{
+ err = -1;
+ }
+ free(tmp->iostream->syscall_info);
+ tmp->iostream->syscall_info = NULL;
+ }
+
+ return err;
+}
+
+enum bt_cb_ret handle_exit_syscall(struct bt_ctf_event *call_data,
+ void *private_data)
+{
+ struct definition *scope;
+ unsigned long timestamp;
+ char *comm;
+ uint64_t ret, tid;
+ int64_t cpu_id;
+
+ timestamp = bt_ctf_get_timestamp(call_data);
+ if (timestamp == -1ULL)
+ goto error;
+
+ scope = bt_ctf_get_top_level_scope(call_data,
+ BT_STREAM_EVENT_CONTEXT);
+ comm = bt_ctf_get_char_array(bt_ctf_get_field(call_data,
+ scope, "_procname"));
+ if (bt_ctf_field_get_error()) {
+ fprintf(stderr, "Missing procname context info\n");
+ goto error;
+ }
+
+ tid = bt_ctf_get_int64(bt_ctf_get_field(call_data,
+ scope, "_tid"));
+ if (bt_ctf_field_get_error()) {
+ fprintf(stderr, "Missing tid context info\n");
+ goto error;
+ }
+
+ scope = bt_ctf_get_top_level_scope(call_data,
+ BT_EVENT_FIELDS);
+ ret = bt_ctf_get_int64(bt_ctf_get_field(call_data,
+ scope, "_ret"));
+ if (bt_ctf_field_get_error()) {
+ fprintf(stderr, "Missing ret context info\n");
+ goto error;
+ }
+
+ scope = bt_ctf_get_top_level_scope(call_data,
+ BT_STREAM_PACKET_CONTEXT);
+ cpu_id = bt_ctf_get_uint64(bt_ctf_get_field(call_data,
+ scope, "cpu_id"));
+ if (bt_ctf_field_get_error()) {
+ fprintf(stderr, "Missing cpu_id context info\n");
+ goto error;
+ }
+
+ /*
+ * if we encounter an exit_syscall and it is not for a syscall read or write
+ * we just abort the execution of this callback
+ */
+ if ((update_iostream_ret(<tngtop, tid, comm, timestamp, cpu_id, ret)) < 0)
+ return BT_CB_ERROR_CONTINUE;
+
+ return BT_CB_OK;
+
+error:
+ return BT_CB_ERROR_STOP;
+}
+
+
+enum bt_cb_ret handle_sys_write(struct bt_ctf_event *call_data,
+ void *private_data)
+{
+ struct definition *scope;
+ struct processtop *tmp;
+ struct syscalls *syscall_info;
+ unsigned long timestamp;
+ uint64_t cpu_id;
+ char *comm;
+ int64_t tid;
+
+ timestamp = bt_ctf_get_timestamp(call_data);
+ if (timestamp == -1ULL)
+ goto error;
+
+ scope = bt_ctf_get_top_level_scope(call_data,
+ BT_STREAM_EVENT_CONTEXT);
+ comm = bt_ctf_get_char_array(bt_ctf_get_field(call_data,
+ scope, "_procname"));
+ if (bt_ctf_field_get_error()) {
+ fprintf(stderr, "Missing procname context info\n");
+ goto error;
+ }
+
+ tid = bt_ctf_get_int64(bt_ctf_get_field(call_data,
+ scope, "_tid"));
+ if (bt_ctf_field_get_error()) {
+ fprintf(stderr, "Missing tid context info\n");
+ goto error;
+ }
+
+ scope = bt_ctf_get_top_level_scope(call_data,
+ BT_STREAM_PACKET_CONTEXT);
+ cpu_id = bt_ctf_get_uint64(bt_ctf_get_field(call_data,
+ scope, "cpu_id"));
+ if (bt_ctf_field_get_error()) {
+ fprintf(stderr, "Missing cpu_id context info\n");
+ goto error;
+ }
+
+ syscall_info = malloc(sizeof(struct syscalls));
+ syscall_info->cpu_id = cpu_id;
+ syscall_info->type = __NR_write;
+ syscall_info->tid = tid;
+ tmp = get_proc(<tngtop, tid, comm, timestamp);
+ tmp->iostream->syscall_info = syscall_info;
+
+ return BT_CB_OK;
+
+error:
+ return BT_CB_ERROR_STOP;
+}
+
+enum bt_cb_ret handle_sys_read(struct bt_ctf_event *call_data,
+ void *private_data)
+{
+ struct processtop *tmp;
+ struct definition *scope;
+ struct syscalls * syscall_info;
+ unsigned long timestamp;
+ uint64_t cpu_id;
+ char *comm;
+ int64_t tid;
+
+ timestamp = bt_ctf_get_timestamp(call_data);
+ if (timestamp == -1ULL)
+ goto error;
+
+ scope = bt_ctf_get_top_level_scope(call_data,
+ BT_STREAM_EVENT_CONTEXT);
+ comm = bt_ctf_get_char_array(bt_ctf_get_field(call_data,
+ scope, "_procname"));
+ if (bt_ctf_field_get_error()) {
+ fprintf(stderr, "Missing procname context info\n");
+ goto error;
+ }
+
+ tid = bt_ctf_get_int64(bt_ctf_get_field(call_data,
+ scope, "_tid"));
+ if (bt_ctf_field_get_error()) {
+ fprintf(stderr, "Missing tid context info\n");
+ goto error;
+ }
+
+ scope = bt_ctf_get_top_level_scope(call_data,
+ BT_STREAM_PACKET_CONTEXT);
+ cpu_id = bt_ctf_get_uint64(bt_ctf_get_field(call_data,
+ scope, "cpu_id"));
+ if (bt_ctf_field_get_error()) {
+ fprintf(stderr, "Missing cpu_id context info\n");
+ goto error;
+ }
+
+ syscall_info = malloc(sizeof(struct syscalls));
+ syscall_info->cpu_id = cpu_id;
+ syscall_info->type = __NR_read;
+ syscall_info->tid = tid;
+ tmp = get_proc(<tngtop, tid, comm, timestamp);
+ tmp->iostream->syscall_info = syscall_info;
+
+ return BT_CB_OK;
+
+error:
+ return BT_CB_ERROR_STOP;
+}
+
--- /dev/null
+/*
+ * Copyright (C) 2011 Mathieu Bain <mathieu.bain@polymtl.ca>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+#ifndef _IOSTREANTOP_H
+#define _IOSTREAMTOP_H
+
+#include <babeltrace/babeltrace.h>
+#include <inttypes.h>
+#include <glib.h>
+#include <asm/unistd.h>
+
+/*
+#define SYS_READ 1
+#define SYS_WRITE 2
+*/
+
+enum bt_cb_ret handle_exit_syscall(struct bt_ctf_event *call_data,
+ void *private_data);
+
+enum bt_cb_ret handle_sys_write(struct bt_ctf_event *call_data,
+ void *private_data);
+
+enum bt_cb_ret handle_sys_read(struct bt_ctf_event *call_data,
+ void *private_data);
+
+#endif /* _IOSTREAMTOP_H */
--- /dev/null
+/*
+ * Copyright (C) 2011 Julien Desfossez
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+#define _GNU_SOURCE
+#include <config.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <babeltrace/babeltrace.h>
+#include <babeltrace/ctf/events.h>
+#include <babeltrace/ctf/callbacks.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <popt.h>
+#include <stdlib.h>
+#include <ftw.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <fts.h>
+
+#include "lttngtoptypes.h"
+#include "cputop.h"
+#include "iostreamtop.h"
+#include "cursesdisplay.h"
+#include "common.h"
+
+#define DEFAULT_FILE_ARRAY_SIZE 1
+
+const char *opt_input_path;
+
+struct lttngtop *copy;
+pthread_t display_thread;
+pthread_t timer_thread;
+
+unsigned long refresh_display = 1 * NSEC_PER_SEC;
+unsigned long last_display_update = 0;
+int quit = 0;
+
+enum {
+ OPT_NONE = 0,
+ OPT_HELP,
+ OPT_LIST,
+ OPT_VERBOSE,
+ OPT_DEBUG,
+ OPT_NAMES,
+};
+
+static struct poptOption long_options[] = {
+ /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
+ { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL },
+ { NULL, 0, 0, NULL, 0, NULL, NULL },
+};
+
+void *refresh_thread(void *p)
+{
+ while (1) {
+ sem_wait(&pause_sem);
+ sem_post(&pause_sem);
+ sem_post(&timer);
+ sleep(refresh_display/NSEC_PER_SEC);
+ }
+}
+
+void *ncurses_display(void *p)
+{
+ unsigned int current_display_index = 0;
+
+ sem_wait(&bootstrap);
+ init_ncurses();
+
+ while (1) {
+ sem_wait(&timer);
+ sem_wait(&goodtodisplay);
+ sem_wait(&pause_sem);
+
+ copy = g_ptr_array_index(copies, current_display_index);
+ if (copy)
+ display(current_display_index++);
+
+ sem_post(&goodtoupdate);
+ sem_post(&pause_sem);
+
+ if (quit) {
+ reset_ncurses();
+ pthread_exit(0);
+ }
+ }
+}
+
+/*
+ * hook on each event to check the timestamp and refresh the display if
+ * necessary
+ */
+enum bt_cb_ret check_timestamp(struct bt_ctf_event *call_data, void *private_data)
+{
+ unsigned long timestamp;
+
+ timestamp = bt_ctf_get_timestamp(call_data);
+ if (timestamp == -1ULL)
+ goto error;
+
+ if (last_display_update == 0)
+ last_display_update = timestamp;
+
+ if (timestamp - last_display_update >= refresh_display) {
+ sem_wait(&goodtoupdate);
+ g_ptr_array_add(copies, get_copy_lttngtop(last_display_update,
+ timestamp));
+ sem_post(&goodtodisplay);
+ sem_post(&bootstrap);
+ last_display_update = timestamp;
+ }
+ return BT_CB_OK;
+
+error:
+ fprintf(stderr, "check_timestamp callback error\n");
+ return BT_CB_ERROR_STOP;
+}
+
+/*
+ * get_perf_counter : get or create and return a perf_counter struct for
+ * either a process or a cpu (only one of the 2 parameters mandatory)
+ */
+struct perfcounter *get_perf_counter(const char *name, struct processtop *proc,
+ struct cputime *cpu)
+{
+ struct perfcounter *ret, *global;
+ GHashTable *table;
+
+ if (proc)
+ table = proc->perf;
+ else if (cpu)
+ table = cpu->perf;
+ else
+ goto error;
+
+ ret = g_hash_table_lookup(table, (gpointer) name);
+ if (ret)
+ goto end;
+
+ ret = malloc(sizeof(struct perfcounter));
+ memset(ret, 0, sizeof(struct perfcounter));
+ /* by default, make it visible in the UI */
+ ret->visible = 1;
+ g_hash_table_insert(table, (gpointer) name, ret);
+
+ global = g_hash_table_lookup(lttngtop.perf_list, (gpointer) name);
+ if (!global) {
+ global = malloc(sizeof(struct perfcounter));
+ memset(global, 0, sizeof(struct perfcounter));
+ memcpy(global, ret, sizeof(struct perfcounter));
+ /* by default, sort on the first perf context */
+ if (g_hash_table_size(lttngtop.perf_list) == 0)
+ global->sort = 1;
+ g_hash_table_insert(lttngtop.perf_list, (gpointer) name, global);
+ }
+
+end:
+ return ret;
+
+error:
+ return NULL;
+}
+
+void update_perf_value(struct processtop *proc, struct cputime *cpu,
+ const char *name, int value)
+{
+ struct perfcounter *cpu_perf, *process_perf;
+
+ cpu_perf = get_perf_counter(name, NULL, cpu);
+ if (cpu_perf->count < value) {
+ process_perf = get_perf_counter(name, proc, NULL);
+ process_perf->count += value - cpu_perf->count;
+ cpu_perf->count = value;
+ }
+}
+
+void extract_perf_counter_scope(struct bt_ctf_event *event,
+ struct definition *scope,
+ struct processtop *proc,
+ struct cputime *cpu)
+{
+ struct definition const * const *list = NULL;
+ unsigned int count;
+ int i, ret;
+
+ if (!scope)
+ goto end;
+
+ ret = bt_ctf_get_field_list(event, scope, &list, &count);
+ if (ret < 0)
+ goto end;
+
+ for (i = 0; i < count; i++) {
+ const char *name = bt_ctf_field_name(list[i]);
+ if (strncmp(name, "_perf_", 6) == 0) {
+ int value = bt_ctf_get_uint64(list[i]);
+ if (bt_ctf_field_get_error())
+ continue;
+ update_perf_value(proc, cpu, name, value);
+ }
+ }
+
+end:
+ return;
+}
+
+void update_perf_counter(struct processtop *proc, struct bt_ctf_event *event)
+{
+ struct definition *scope;
+ uint64_t cpu_id;
+ struct cputime *cpu;
+
+ scope = bt_ctf_get_top_level_scope(event, BT_STREAM_PACKET_CONTEXT);
+ cpu_id = bt_ctf_get_uint64(bt_ctf_get_field(event, scope, "cpu_id"));
+ if (bt_ctf_field_get_error()) {
+ fprintf(stderr, "[error] get cpu_id\n");
+ goto end;
+ }
+ cpu = get_cpu(cpu_id);
+
+ scope = bt_ctf_get_top_level_scope(event, BT_STREAM_EVENT_CONTEXT);
+ extract_perf_counter_scope(event, scope, proc, cpu);
+
+ scope = bt_ctf_get_top_level_scope(event, BT_STREAM_PACKET_CONTEXT);
+ extract_perf_counter_scope(event, scope, proc, cpu);
+
+ scope = bt_ctf_get_top_level_scope(event, BT_EVENT_CONTEXT);
+ extract_perf_counter_scope(event, scope, proc, cpu);
+
+end:
+ return;
+}
+
+enum bt_cb_ret fix_process_table(struct bt_ctf_event *call_data,
+ void *private_data)
+{
+ int pid, tid, ppid;
+ char *comm;
+ struct processtop *parent, *child;
+ struct definition *scope;
+ unsigned long timestamp;
+
+ /* FIXME : check context pid, tid, ppid and comm */
+
+ timestamp = bt_ctf_get_timestamp(call_data);
+ if (timestamp == -1ULL)
+ goto error;
+
+ scope = bt_ctf_get_top_level_scope(call_data, BT_STREAM_EVENT_CONTEXT);
+
+ pid = bt_ctf_get_int64(bt_ctf_get_field(call_data, scope, "_pid"));
+ if (bt_ctf_field_get_error()) {
+// fprintf(stderr, "Missing pid context info\n");
+ goto error;
+ }
+ tid = bt_ctf_get_int64(bt_ctf_get_field(call_data, scope, "_tid"));
+ if (bt_ctf_field_get_error()) {
+// fprintf(stderr, "Missing tid context info\n");
+ goto error;
+ }
+ ppid = bt_ctf_get_int64(bt_ctf_get_field(call_data, scope, "_ppid"));
+ if (bt_ctf_field_get_error()) {
+// fprintf(stderr, "Missing ppid context info\n");
+ goto error;
+ }
+ comm = bt_ctf_get_char_array(bt_ctf_get_field(call_data, scope, "_procname"));
+ if (bt_ctf_field_get_error()) {
+// fprintf(stderr, "Missing procname context info\n");
+ goto error;
+ }
+
+ /* find or create the current process */
+ child = find_process_tid(<tngtop, tid, comm);
+ if (!child)
+ child = add_proc(<tngtop, tid, comm, timestamp);
+ update_proc(child, pid, tid, ppid, comm);
+
+ if (pid != tid) {
+ /* find or create the parent */
+ parent = find_process_tid(<tngtop, pid, comm);
+ if (!parent) {
+ parent = add_proc(<tngtop, pid, comm, timestamp);
+ parent->pid = pid;
+ }
+
+ /* attach the parent to the current process */
+ child->threadparent = parent;
+ add_thread(parent, child);
+ }
+
+ update_perf_counter(child, call_data);
+
+ return BT_CB_OK;
+
+error:
+ return BT_CB_ERROR_STOP;
+}
+
+void init_lttngtop()
+{
+ copies = g_ptr_array_new();
+ lttngtop.perf_list = g_hash_table_new(g_direct_hash, g_direct_equal);
+
+ sem_init(&goodtodisplay, 0, 0);
+ sem_init(&goodtoupdate, 0, 1);
+ sem_init(&timer, 0, 1);
+ sem_init(&bootstrap, 0, 0);
+ sem_init(&pause_sem, 0, 1);
+ sem_init(&end_trace_sem, 0, 0);
+
+ lttngtop.process_table = g_ptr_array_new();
+ lttngtop.files_table = g_ptr_array_new();
+ lttngtop.cpu_table = g_ptr_array_new();
+}
+
+void usage(FILE *fd)
+{
+
+}
+
+/*
+ * Return 0 if caller should continue, < 0 if caller should return
+ * error, > 0 if caller should exit without reporting error.
+ */
+static int parse_options(int argc, char **argv)
+{
+ poptContext pc;
+ int opt, ret = 0;
+
+ if (argc == 1) {
+ usage(stdout);
+ return 1; /* exit cleanly */
+ }
+
+ pc = poptGetContext(NULL, argc, (const char **) argv, long_options, 0);
+ poptReadDefaultConfig(pc, 0);
+
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case OPT_HELP:
+ usage(stdout);
+ ret = 1; /* exit cleanly */
+ goto end;
+ case OPT_LIST:
+ // list_formats(stdout);
+ ret = 1;
+ goto end;
+ case OPT_VERBOSE:
+// babeltrace_verbose = 1;
+ break;
+ case OPT_DEBUG:
+// babeltrace_debug = 1;
+ break;
+ case OPT_NAMES:
+// opt_field_names = 1;
+ break;
+ default:
+ ret = -EINVAL;
+ goto end;
+ }
+ }
+
+ opt_input_path = poptGetArg(pc);
+ if (!opt_input_path) {
+ ret = -EINVAL;
+ goto end;
+ }
+end:
+ if (pc) {
+ poptFreeContext(pc);
+ }
+ return ret;
+}
+
+void iter_trace(struct bt_context *bt_ctx)
+{
+ struct bt_ctf_iter *iter;
+ struct bt_iter_pos begin_pos;
+ struct bt_ctf_event *event;
+ int ret = 0;
+
+ begin_pos.type = BT_SEEK_BEGIN;
+ iter = bt_ctf_iter_create(bt_ctx, &begin_pos, NULL);
+
+ /* at each event check if we need to refresh */
+ bt_ctf_iter_add_callback(iter, 0, NULL, 0,
+ check_timestamp,
+ NULL, NULL, NULL);
+ /* at each event, verify the status of the process table */
+ bt_ctf_iter_add_callback(iter, 0, NULL, 0,
+ fix_process_table,
+ NULL, NULL, NULL);
+ /* to handle the scheduling events */
+ bt_ctf_iter_add_callback(iter,
+ g_quark_from_static_string("sched_switch"),
+ NULL, 0, handle_sched_switch, NULL, NULL, NULL);
+ /* to clean up the process table */
+ bt_ctf_iter_add_callback(iter,
+ g_quark_from_static_string("sched_process_free"),
+ NULL, 0, handle_sched_process_free, NULL, NULL, NULL);
+
+ /* for IO top */
+ bt_ctf_iter_add_callback(iter,
+ g_quark_from_static_string("exit_syscall"),
+ NULL, 0, handle_exit_syscall, NULL, NULL, NULL);
+ bt_ctf_iter_add_callback(iter,
+ g_quark_from_static_string("sys_write"),
+ NULL, 0, handle_sys_write, NULL, NULL, NULL);
+ bt_ctf_iter_add_callback(iter,
+ g_quark_from_static_string("sys_read"),
+ NULL, 0, handle_sys_read, NULL, NULL, NULL);
+ while ((event = bt_ctf_iter_read_event(iter)) != NULL) {
+ ret = bt_iter_next(bt_ctf_get_iter(iter));
+ if (ret < 0)
+ goto end_iter;
+ }
+
+ /* block until quit, we reached the end of the trace */
+ sem_wait(&end_trace_sem);
+
+end_iter:
+ bt_iter_destroy(bt_ctf_get_iter(iter));
+}
+
+/*
+ * bt_context_add_traces_recursive: Open a trace recursively
+ * (copied from BSD code in converter/babeltrace.c)
+ *
+ * Find each trace present in the subdirectory starting from the given
+ * path, and add them to the context. The packet_seek parameter can be
+ * NULL: this specify to use the default format packet_seek.
+ *
+ * Return: 0 on success, nonzero on failure.
+ * Unable to open toplevel: failure.
+ * Unable to open some subdirectory or file: warn and continue;
+ */
+int bt_context_add_traces_recursive(struct bt_context *ctx, const char *path,
+ const char *format_str,
+ void (*packet_seek)(struct stream_pos *pos,
+ size_t offset, int whence))
+{
+ FTS *tree;
+ FTSENT *node;
+ GArray *trace_ids;
+ char lpath[PATH_MAX];
+ char * const paths[2] = { lpath, NULL };
+ int ret;
+
+ /*
+ * Need to copy path, because fts_open can change it.
+ * It is the pointer array, not the strings, that are constant.
+ */
+ strncpy(lpath, path, PATH_MAX);
+ lpath[PATH_MAX - 1] = '\0';
+
+ tree = fts_open(paths, FTS_NOCHDIR | FTS_LOGICAL, 0);
+ if (tree == NULL) {
+ fprintf(stderr, "[error] [Context] Cannot traverse \"%s\" for reading.\n",
+ path);
+ return -EINVAL;
+ }
+
+ trace_ids = g_array_new(FALSE, TRUE, sizeof(int));
+
+ while ((node = fts_read(tree))) {
+ int dirfd, metafd;
+
+ if (!(node->fts_info & FTS_D))
+ continue;
+
+ dirfd = open(node->fts_accpath, 0);
+ if (dirfd < 0) {
+ fprintf(stderr, "[error] [Context] Unable to open trace "
+ "directory file descriptor.\n");
+ ret = dirfd;
+ goto error;
+ }
+ metafd = openat(dirfd, "metadata", O_RDONLY);
+ if (metafd < 0) {
+ ret = close(dirfd);
+ if (ret < 0) {
+ perror("close");
+ goto error;
+ }
+ } else {
+ int trace_id;
+
+ ret = close(metafd);
+ if (ret < 0) {
+ perror("close");
+ goto error;
+ }
+ ret = close(dirfd);
+ if (ret < 0) {
+ perror("close");
+ goto error;
+ }
+
+ trace_id = bt_context_add_trace(ctx,
+ node->fts_accpath, format_str,
+ packet_seek, NULL, NULL);
+ if (trace_id < 0) {
+ fprintf(stderr, "[error] [Context] opening trace \"%s\" from %s "
+ "for reading.\n", node->fts_accpath, path);
+ ret = trace_id;
+ goto error;
+ }
+ g_array_append_val(trace_ids, trace_id);
+ }
+ }
+
+ g_array_free(trace_ids, TRUE);
+ return 0;
+
+error:
+ return ret;
+}
+
+int main(int argc, char **argv)
+{
+ int ret;
+ struct bt_context *bt_ctx = NULL;
+
+ ret = parse_options(argc, argv);
+ if (ret < 0) {
+ fprintf(stdout, "Error parsing options.\n\n");
+ usage(stdout);
+ exit(EXIT_FAILURE);
+ } else if (ret > 0) {
+ exit(EXIT_SUCCESS);
+ }
+
+ init_lttngtop();
+
+ bt_ctx = bt_context_create();
+ ret = bt_context_add_traces_recursive(bt_ctx, opt_input_path, "ctf", NULL);
+ if (ret < 0) {
+ printf("[error] Opening the trace\n");
+ goto end;
+ }
+
+ pthread_create(&display_thread, NULL, ncurses_display, (void *) NULL);
+ pthread_create(&timer_thread, NULL, refresh_thread, (void *) NULL);
+
+ iter_trace(bt_ctx);
+
+ quit = 1;
+ pthread_join(display_thread, NULL);
+
+end:
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 Julien Desfossez
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+#ifndef LTTNGTOPTYPES_H
+#define LTTNGTOPTYPES_H
+
+#include <glib.h>
+
+struct lttngtop {
+ GPtrArray *process_table; /* struct processtop */
+ GPtrArray *files_table; /* struct files */
+ GPtrArray *cpu_table; /* struct cputime */
+ GHashTable *perf_list; /* struct perfcounter */
+ unsigned long start;
+ unsigned long end;
+ unsigned int nbproc;
+ unsigned int nbnewproc;
+ unsigned int nbdeadproc;
+ unsigned int nbthreads;
+ unsigned int nbnewthreads;
+ unsigned int nbdeadthreads;
+ unsigned int nbfiles;
+ unsigned int nbnewfiles;
+ unsigned int nbclosedfiles;
+} lttngtop;
+
+struct processtop {
+ unsigned int puuid;
+ int pid;
+ char *comm;
+ int tid;
+ int ppid;
+ int oldpid;
+ int oldtid;
+ int oldppid;
+ unsigned long birth;
+ unsigned long death;
+ unsigned long lastactivity;
+ GPtrArray *process_files_table;
+ GPtrArray *threads;
+ GHashTable *perf;
+ struct processtop *threadparent;
+ unsigned long totalfileread;
+ unsigned long totalfilewrite;
+ unsigned long totalcpunsec;
+ unsigned long threadstotalcpunsec;
+ /* IO speed for this process */
+ struct iostream *iostream;
+};
+
+struct perfcounter
+{
+ unsigned long count;
+ int visible;
+ int sort;
+};
+
+struct cputime {
+ guint id;
+ struct processtop *current_task;
+ unsigned long task_start;
+ GHashTable *perf;
+};
+
+/*
+ * used for "relative seeks" (with fd, for example fs.lseek)
+ * and for "absolute seeks" (events occuring on a device without
+ * any link to a particular process)
+ */
+struct seeks {
+ unsigned long offset;
+ unsigned long count;
+};
+
+struct ioctls {
+ unsigned int command;
+ unsigned long count;
+};
+
+struct files {
+ struct processtop *ref;
+ unsigned int fuuid;
+ int fd;
+ char *name;
+ int oldfd;
+ int device;
+ int openmode;
+ unsigned long openedat;
+ unsigned long closedat;
+ unsigned long lastaccess;
+ unsigned long read;
+ unsigned long write;
+ unsigned long nbpoll;
+ unsigned long nbselect;
+ unsigned long nbopen;
+ unsigned long nbclose;
+ //struct *seeks; /* relative seeks inside the file */
+ //struct *ioctls;
+ /* XXX : average wait time */
+};
+
+struct sockets {
+ int fd;
+ int parent_fd; /* on accept a new fd is created from the bound socket */
+ int family;
+ int type;
+ int protocol;
+ int sock_address;
+ unsigned long openedat;
+ unsigned long closedat;
+ unsigned long bind_address;
+ unsigned long remote_address;
+ //struct *sock_options;
+};
+
+struct sock_options {
+ int name;
+ int value;
+};
+
+struct vmas {
+ unsigned long start;
+ unsigned long end;
+ unsigned long flags;
+ unsigned long prot;
+ char *description; /* filename or description if possible (stack, heap) */
+ unsigned long page_faults;
+};
+
+struct syscalls {
+ unsigned int id;
+ unsigned long count;
+ unsigned int cpu_id;
+ unsigned int type;
+ unsigned int tid;
+};
+
+struct signals {
+ int dest_pid;
+ int id;
+ unsigned long count;
+};
+
+struct iostream {
+ struct syscalls *syscall_info; /* NULL if there is no waiting for an exit_syscall */
+ unsigned long ret_read; /* value returned by an I/O syscall_exit for a sys_read*/
+ unsigned long ret_write; /* value returned by an I/O syscall_exit for a sys_write*/
+ unsigned long ret_total;
+};
+
+#endif /* LTTNGTOPTYPES_H */