From: Pierre-Bernard Thiffault Date: Fri, 21 Dec 2012 18:39:17 +0000 (-0500) Subject: Convert LTTngTop to C++ and state system X-Git-Url: https://git.lttng.org./?a=commitdiff_plain;h=refs%2Fheads%2Fstate-system;p=lttngtop.git Convert LTTngTop to C++ and state system This commit integrates LTTngTop with a generic C++ state system. This creates a cleaner code base as the analysis won't require any ad-hoc state system anymore. Since the library is in C++, this commit also converts LTTngTop to C++, most of the code written in C stays the same. New dependencies (that should be integrated in the tree) : git://git.dorsal.polymtl.ca/~smarchi/libstate.git git://git.dorsal.polymtl.ca/~frajotte/librbrntrvll.git The code is not yet working, but it is close. Signed-off-by: Pierre-Bernard Thiffault Signed-off-by: Julien Desfossez --- diff --git a/configure.ac b/configure.ac index 358ead8..6eb6661 100644 --- a/configure.ac +++ b/configure.ac @@ -14,6 +14,7 @@ AC_CONFIG_HEADERS([config.h]) # Checks for programs. AC_PROG_CC +AC_PROG_CXX AC_PROG_MAKE_SET LT_INIT AC_PROG_YACC diff --git a/src/Makefile.am b/src/Makefile.am index 382cfc2..e060d65 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,4 +1,5 @@ AM_CFLAGS = $(PACKAGE_CFLAGS) +AM_CXXFLAGS = $(PACKAGE_CFLAGS) bin_PROGRAMS = lttngtop @@ -6,14 +7,18 @@ noinst_HEADERS = \ lttngtoptypes.h \ common.h \ cputop.h \ - cursesdisplay.h \ - iostreamtop.h + iostreamtop.h \ + cursesdisplay.h lttngtop_SOURCES = \ - lttngtop.c \ - common.c \ - cursesdisplay.c \ - cputop.c \ - iostreamtop.c + lttngtop.cpp \ + common.cpp \ + cputop.cpp \ + iostreamtop.cpp \ + cursesdisplay.cpp -lttngtop_LDADD = -lbabeltrace -lbabeltrace-ctf +lttngtop_LDADD = \ + -lbabeltrace \ + -lbabeltrace-ctf \ + -lboost_system \ + -lstate diff --git a/src/attributes.txt b/src/attributes.txt new file mode 100644 index 0000000..120ad12 --- /dev/null +++ b/src/attributes.txt @@ -0,0 +1,61 @@ +List of available attributes + +cpu: pointer to first cpu +cpus/[cpuid]/perf: pointer to first perf counter for cpu cpuid +cpus/[cpuid]/perf/[perfname]/count: count for perf counter perfname on cpu cpuid +cpus/[cpuid]/perf/[perfname]/visible: visibility for perf counter perfname on cpu cpuid +cpus/[cpuid]/perf/[perfname]/next: pointer to next perf counter in linked list +cpus/[cpuid]/task_start: timestamp for the start of current_task +cpus/[cpuid]/current_task: pointer to current_task +cpus/[cpuid]/next: pointer to next cpu in linked list +proc: pointer to first process +processes/[tid]/perf: pointer to first perf counter for task tid +processes/[tid]/perf/[perfname]/count: count for perf counter perfname on task tid +processes/[tid]/perf/[perfname]/visible: visibility for perf counter perfname on task tid +processes/[tid]/perf/[perfname]/next: pointer to next perf counter in linked list +processes/[tid]/files_history/current: pointer to first file for task tid +processes/[tid]/file_history[x]: file history entry for file x (sequential) for task tid +processes/[tid]/file_history[x]/file: file entry for file x (sequential) for task tid +processes/[tid]/file_history[x]/file/name: file name for file x (sequential) for task tid +processes/[tid]/file_history[x]/file/read: bytes read from file x (sequential) for task tid +processes/[tid]/file_history[x]/file/write: bytes written to file x (sequential) for task tid +processes/[tid]/file_history[x]/file/flag: status flag for file x (sequential) for task tid +processes/[tid]/file_history[x]/file/fd: file descriptor for file x (sequential) for task tid +processes/[tid]/file_history[x]/file/birth: timestamp for the opening of file x (sequential) for task tid +processes/[tid]/file_history[x]/next: pointer to next file history entry in linked list +processes/[tid]/files/[fd]: pointer to file entry for file fd for task tid +processes/[tid]/threads: pointer to first descendant thread for task tid +processes/[tid]/threads/[dtid]: pointer to descendant thread dtid for task tid +processes/[tid]/threads/next: pointer to next descendant thread in linked list +processes/[tid]/threadparent: pointer to parent thread for task tid +processes/[tid]/syscall_info/type: type for last syscall in task tid +processes/[tid]/syscall_info/cpu_id: cpuid for last syscall in task tid +processes/[tid]/syscall_info/tid: tid for last syscall in task tid +processes/[tid]/syscall_info/fd: fd for last syscall in task tid +processes/[tid]/tid: tid for task tid +processes/[tid]/comm: procname for task tid +processes/[tid]/pid: pid for task tid +processes/[tid]/ppid: ppid for task tid +processes/[tid]/birth: timestamp for beginning of task tid +processes/[tid]/totalfileread: bytes read from all files for task tid +processes/[tid]/totalfilewrite: bytes written to all files for task tid +processes/[tid]/read: read stream (B/s) for task tid +processes/[tid]/write: write stream (B/s) for task tid +processes/[tid]/totalcpunsec: execution time (ns) for task tid +processes/[tid]/threadstotalcpunsec: execution time (ns) for task tid and its descendants +processes/[tid]/selected: whether the task tid is selected by the UI +perf: pointer to first perf counter in global list +perf/[perfname]/count: global count for perf counter perfname +perf/[perfname]/visible: visibility of gloabl perf counter perfname +perf/[perfname]/sort: whether the global perf counter list is sorted by perf counter perfname's count +perf/[perfname]/next: next perf counter in linked list +nbproc: number of active processes +nbnewproc: number of processes created +nbdeadproc: number of processes destroyed +nbthreads: number of active threads +nbnewthreads: number of threads created +nbdeadthreads: number of threads destroyed +nbfiles: number of active files +nbnewfiles: number of files opened +nbdeadfiles: number of files destroyed + diff --git a/src/common.c b/src/common.c deleted file mode 100644 index c939847..0000000 --- a/src/common.c +++ /dev/null @@ -1,531 +0,0 @@ -/* - * Copyright (C) 2011-2012 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., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include -#include -#include -#include -#include "common.h" - -uint64_t get_cpu_id(const struct bt_ctf_event *event) -{ - const struct definition *scope; - uint64_t cpu_id; - - 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"); - return -1ULL; - } - - return cpu_id; -} - -uint64_t get_context_tid(const struct bt_ctf_event *event) -{ - const struct definition *scope; - uint64_t tid; - - scope = bt_ctf_get_top_level_scope(event, BT_STREAM_EVENT_CONTEXT); - tid = bt_ctf_get_int64(bt_ctf_get_field(event, - scope, "_tid")); - if (bt_ctf_field_get_error()) { - fprintf(stderr, "Missing tid context info\n"); - return -1ULL; - } - - return tid; -} - -uint64_t get_context_pid(const struct bt_ctf_event *event) -{ - const struct definition *scope; - uint64_t pid; - - scope = bt_ctf_get_top_level_scope(event, BT_STREAM_EVENT_CONTEXT); - pid = bt_ctf_get_int64(bt_ctf_get_field(event, - scope, "_pid")); - if (bt_ctf_field_get_error()) { - fprintf(stderr, "Missing pid context info\n"); - return -1ULL; - } - - return pid; -} - -uint64_t get_context_ppid(const struct bt_ctf_event *event) -{ - const struct definition *scope; - uint64_t ppid; - - scope = bt_ctf_get_top_level_scope(event, BT_STREAM_EVENT_CONTEXT); - ppid = bt_ctf_get_int64(bt_ctf_get_field(event, - scope, "_ppid")); - if (bt_ctf_field_get_error()) { - fprintf(stderr, "Missing ppid context info\n"); - return -1ULL; - } - - return ppid; -} - -char *get_context_comm(const struct bt_ctf_event *event) -{ - const struct definition *scope; - char *comm; - - scope = bt_ctf_get_top_level_scope(event, BT_STREAM_EVENT_CONTEXT); - comm = bt_ctf_get_char_array(bt_ctf_get_field(event, - scope, "_procname")); - if (bt_ctf_field_get_error()) { - fprintf(stderr, "Missing comm context info\n"); - return NULL; - } - - return comm; -} - -/* - * To get the parent process, put the pid in the tid field - * because the parent process gets pid = tid - * - * FIXME : char *comm useful ??? - */ -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 = g_new0(struct processtop, 1); - newproc->tid = tid; - newproc->birth = timestamp; - newproc->process_files_table = g_ptr_array_new(); - newproc->files_history = NULL; - newproc->totalfileread = 0; - newproc->totalfilewrite = 0; - newproc->fileread = 0; - newproc->filewrite = 0; - newproc->syscall_info = NULL; - newproc->threadparent = NULL; - newproc->threads = g_ptr_array_new(); - newproc->perf = g_hash_table_new(g_str_hash, g_str_equal); - g_ptr_array_add(ctx->process_table, newproc); - - ctx->nbnewthreads++; - ctx->nbthreads++; - } - 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; - ctx->nbdeadthreads++; - ctx->nbthreads--; - } -} - -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); -} - -struct processtop *get_proc_pid(struct lttngtop *ctx, int tid, int pid, - unsigned long timestamp) -{ - struct processtop *tmp; - tmp = find_process_tid(ctx, tid, NULL); - if (tmp && tmp->pid == pid) - return tmp; - return add_proc(ctx, tid, "Unknown", 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 = g_new0(struct cputime, 1); - newcpu->id = cpu; - newcpu->current_task = NULL; - newcpu->perf = g_hash_table_new(g_str_hash, g_str_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 = g_new0(struct perfcounter, 1); - 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, strdup(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, j; - struct processtop *tmp; - struct files *tmpf; /* a temporary file */ - - 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->fileread = 0; - tmp->filewrite = 0; - - for (j = 0; j < tmp->process_files_table->len; j++) { - tmpf = g_ptr_array_index(tmp->process_files_table, j); - if (tmpf != NULL) { - tmpf->read = 0; - tmpf->write = 0; - - if (tmpf->flag == __NR_close) - g_ptr_array_index( - tmp->process_files_table, j - ) = NULL; - } - } - } -} - -void reset_global_counters() -{ - lttngtop.nbnewproc = 0; - lttngtop.nbdeadproc = 0; - lttngtop.nbnewthreads = 0; - lttngtop.nbdeadthreads = 0; - lttngtop.nbnewfiles = 0; - lttngtop.nbclosedfiles = 0; -} - -void copy_global_counters(struct lttngtop *dst) -{ - dst->nbproc = lttngtop.nbproc; - dst->nbnewproc = lttngtop.nbnewproc; - dst->nbdeadproc = lttngtop.nbdeadproc; - dst->nbthreads = lttngtop.nbthreads; - dst->nbnewthreads = lttngtop.nbnewthreads; - dst->nbdeadthreads = lttngtop.nbdeadthreads; - dst->nbfiles = lttngtop.nbfiles; - dst->nbnewfiles = lttngtop.nbnewfiles; - dst->nbclosedfiles = lttngtop.nbclosedfiles; - reset_global_counters(); -} - -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 = g_new0(struct lttngtop, 1); - dst->start = start; - dst->end = end; - copy_global_counters(dst); - dst->process_table = g_ptr_array_new(); - dst->files_table = g_ptr_array_new(); - dst->cpu_table = g_ptr_array_new(); - - rotate_cputime(end); - - for (i = 0; i < lttngtop.process_table->len; i++) { - tmp = g_ptr_array_index(lttngtop.process_table, i); - new = g_new0(struct processtop, 1); - - 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->files_history = tmp->files_history; - new->perf = g_hash_table_new(g_str_hash, g_str_equal); - g_hash_table_foreach(tmp->perf, copy_perf_counter, new->perf); - - /* compute the stream speed */ - if (end - start != 0) { - time = (end - start) / NSEC_PER_SEC; - new->fileread = new->fileread/(time); - new->filewrite = new->filewrite/(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)); - - if (tmpfile != NULL) { - 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); - } else { - g_ptr_array_add(new->process_files_table, NULL); - g_ptr_array_add(dst->files_table, NULL); - } - /* - * 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) { - /* FIXME : close the files before */ - g_ptr_array_remove(tmp->process_files_table, tmpfile); - g_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); - /* FIXME : TRUE does not mean clears the object in it */ - g_ptr_array_free(tmp->threads, TRUE); - free(tmp->comm); - g_ptr_array_free(tmp->process_files_table, TRUE); - /* FIXME : clear elements */ - g_hash_table_destroy(tmp->perf); - g_free(tmp); - } - } - rotate_perfcounter(); - - for (i = 0; i < lttngtop.cpu_table->len; i++) { - tmpcpu = g_ptr_array_index(lttngtop.cpu_table, i); - newcpu = g_new0(struct cputime, 1); - memcpy(newcpu, tmpcpu, sizeof(struct cputime)); - newcpu->perf = g_hash_table_new(g_str_hash, g_str_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); - } - /* FIXME : better algo */ - /* 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; -} - - -enum bt_cb_ret handle_statedump_process_state(struct bt_ctf_event *call_data, - void *private_data) -{ - const struct definition *scope; - struct processtop *proc; - unsigned long timestamp; - int64_t pid, tid; - char *procname; - - timestamp = bt_ctf_get_timestamp(call_data); - if (timestamp == -1ULL) - goto error; - - scope = bt_ctf_get_top_level_scope(call_data, - BT_EVENT_FIELDS); - 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; - } - - scope = bt_ctf_get_top_level_scope(call_data, - BT_EVENT_FIELDS); - 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; - } - - /* - * FIXME - * I first tried with bt_ctf_get_string but doesn`t work at all - * It couldn`t find the field _name because it is an integer in - * the metadata and not a string like _filename for the - * statedump_file_descriptor - */ - scope = bt_ctf_get_top_level_scope(call_data, - BT_EVENT_FIELDS); - procname = bt_ctf_get_char_array(bt_ctf_get_field(call_data, - scope, "_name")); - if (bt_ctf_field_get_error()) { - fprintf(stderr, "Missing process name context info\n"); - goto error; - } - - proc = find_process_tid(<tngtop, tid, procname); - if (proc == NULL) - proc = add_proc(<tngtop, tid, procname, timestamp); - - free(proc->comm); - proc->comm = strdup(procname); - proc->pid = pid; - - /* - * FIXME - * I would like to free procname because it is duplicated - * when the process is created but it segfaults... - * - * free(procname); - */ - - return BT_CB_OK; - -error: - return BT_CB_ERROR_STOP; -} diff --git a/src/common.cpp b/src/common.cpp new file mode 100644 index 0000000..7ca08b4 --- /dev/null +++ b/src/common.cpp @@ -0,0 +1,1089 @@ +/* + * Copyright (C) 2011-2012 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +extern "C" { +#include +} +#include +#include +#include +#include "common.h" + +StateSystem *state_system; +std::set modified_quarks; +unsigned long last_display_update = 0; +unsigned long first_display_update = 0; +sem_t goodtodisplay, goodtoupdate, timer, pause_sem, end_trace_sem, bootstrap; + +int get_cpu_id(const struct bt_ctf_event *event) +{ + const struct definition *scope; + int cpu_id; + + 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"); + return -1; + } + + return cpu_id; +} + +int get_context_tid(const struct bt_ctf_event *event) +{ + const struct definition *scope; + int tid; + + scope = bt_ctf_get_top_level_scope(event, BT_STREAM_EVENT_CONTEXT); + tid = bt_ctf_get_int64(bt_ctf_get_field(event, scope, "_tid")); + if (bt_ctf_field_get_error()) { + fprintf(stderr, "Missing tid context info\n"); + return -1; + } + + return tid; +} + +int get_context_pid(const struct bt_ctf_event *event) +{ + const struct definition *scope; + int pid; + + scope = bt_ctf_get_top_level_scope(event, BT_STREAM_EVENT_CONTEXT); + pid = bt_ctf_get_int64(bt_ctf_get_field(event, scope, "_pid")); + if (bt_ctf_field_get_error()) { + fprintf(stderr, "Missing pid context info\n"); + return -1; + } + + return pid; +} + +int get_context_ppid(const struct bt_ctf_event *event) +{ + const struct definition *scope; + int ppid; + + scope = bt_ctf_get_top_level_scope(event, BT_STREAM_EVENT_CONTEXT); + ppid = bt_ctf_get_int64(bt_ctf_get_field(event, scope, "_ppid")); + if (bt_ctf_field_get_error()) { + fprintf(stderr, "Missing ppid context info\n"); + return -1; + } + + return ppid; +} + +char *get_context_comm(const struct bt_ctf_event *event) +{ + const struct definition *scope; + char *comm; + + scope = bt_ctf_get_top_level_scope(event, BT_STREAM_EVENT_CONTEXT); + comm = bt_ctf_get_char_array(bt_ctf_get_field(event, + scope, "_procname")); + if (bt_ctf_field_get_error()) { + fprintf(stderr, "Missing comm context info\n"); + return NULL; + } + + return comm; +} + +/* + * To get the parent process, put the pid in the tid field + * because the parent process gets pid = tid + */ +bool find_process_tid(int tid, Quark &ret_proc_quark) +{ + std::string path_name = path_name_from_tid(tid); + + if (state_system->attributeExists(path_name)) { + ret_proc_quark = state_system->getQuark(path_name); + return true; + } else { + return false; + } +} + +Quark add_proc(int tid, std::string comm, unsigned long timestamp) +{ + Quark proc_quark = state_system->getQuark(path_name_from_tid(tid)); + + modify_attribute(timestamp, &proc_quark, "comm", comm); + modify_attribute(timestamp, &proc_quark, "tid", tid); + modify_attribute(timestamp, &proc_quark, "birth", timestamp); + modify_attribute(timestamp, &proc_quark, "totalfileread", 0); + modify_attribute(timestamp, &proc_quark, "totalfilewrite", 0); + modify_attribute(timestamp, &proc_quark, "fileread", 0); + modify_attribute(timestamp, &proc_quark, "filewrite", 0); + modify_attribute(timestamp, &proc_quark, "totalcpunsec", 0UL); + modify_attribute(timestamp, &proc_quark, "threadstotalcpunsec", 0UL); + modify_attribute(timestamp, &proc_quark, "selected", 0); + + increment_attribute(timestamp, NULL, "nbnewthreads"); + increment_attribute(timestamp, NULL, "nbthreads"); + + add_in_sequence(timestamp, proc_quark, state_system->getQuark("proc")); + + return proc_quark; +} + +void update_proc(unsigned long timestamp, Quark proc, int pid, int tid, + int ppid, char *comm) +{ + modify_attribute(timestamp, &proc, "pid", pid); + modify_attribute(timestamp, &proc, "tid", tid); + modify_attribute(timestamp, &proc, "ppid", ppid); + modify_attribute(timestamp, &proc, "comm", comm); +} + +/* + * 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(int tid, char *comm, unsigned long timestamp) +{ + Quark proc_quark; + std::string procname; + bool proc_found; + bool procname_found; + + proc_found = find_process_tid(tid, proc_quark); + if (proc_found) { + procname_found = get_current_attribute_value_string( + &proc_quark, "comm", procname); + if (procname_found && procname == comm) { + modify_attribute(timestamp, &proc_quark, "death", + timestamp); + increment_attribute(timestamp, NULL, "nbdeadthreads"); + decrement_attribute(timestamp, NULL, "nbthreads"); + } + } +} + +Quark get_proc(int tid, char *comm, unsigned long timestamp) +{ + Quark proc_quark; + std::string proc_comm; + bool exists = find_process_tid(tid, proc_quark); + std::string comm_str(comm); + + if (!exists) + proc_quark = add_proc(tid, comm_str, timestamp); + + /* If PID already exists under different name, + we just rename the process */ + /* FIXME : need to integrate with clone/fork/exit to be accurate */ + else { + get_current_attribute_value_string(&proc_quark, "comm", + proc_comm); + if (comm_str != proc_comm) + modify_attribute(timestamp, &proc_quark, "comm", comm); + } + + return proc_quark; +} + +Quark get_proc_pid(int pid, int tid, unsigned long timestamp) +{ + Quark proc; + bool proc_found; + int proc_pid; + bool pid_found; + + proc_found = find_process_tid(tid, proc); + if (proc_found) { + pid_found = get_current_attribute_value_int(&proc, "pid", + proc_pid); + if (pid_found && proc_pid == pid) + return proc; + } + return add_proc(tid, "Unknown", timestamp); +} + +void add_thread(unsigned long timestamp, Quark parent, Quark thread) +{ + std::string path; + int tid; + Quark thread_in_parent; + + get_current_attribute_value_int(&thread, "tid", tid); + path = thread_path_name_from_tid(tid); + + if (!state_system->attributeExists(parent, path)) { + thread_in_parent = state_system->getQuark(parent, path); + modify_attribute(timestamp, &thread_in_parent, "", thread); + add_in_sequence(timestamp, thread_in_parent, + state_system->getQuark(parent, "threads")); + } +} + +Quark add_cpu(int cpu, unsigned long timestamp) +{ + Quark cpu_quark = state_system->getQuark(path_name_from_cpuid(cpu)); + Quark cpu_root = state_system->getQuark("cpu"); + modify_attribute(timestamp, &cpu_quark, "id", cpu); + + add_in_sequence(timestamp, cpu_quark, cpu_root); + + return cpu_quark; +} + +Quark get_cpu(int cpu, unsigned long timestamp) +{ + std::string path = path_name_from_cpuid(cpu); + if (state_system->attributeExists(path)) + return state_system->getQuark(path); + else + return add_cpu(cpu, timestamp); +} + +/* + * At the end of a sampling period, we need to display the cpu time for each + * process + */ +void rotate_cputime(unsigned long end) +{ + Quark cpu; + Quark current_task; + unsigned long elapsed; + unsigned long start; + int tid; + int pid; + + if (!get_current_attribute_value_quark(NULL, "cpu", cpu)) { + // No CPU to process + return; + } + + do { + get_current_attribute_value_ulong(&cpu, "task_start", start); + elapsed = end - start; + if (get_current_attribute_value_quark(&cpu, "current_task", + current_task)) { + increase_attribute( + end, ¤t_task, "totalcpunsec", elapsed); + increase_attribute( + end, ¤t_task, "threadstotalcpunsec", + elapsed); + get_current_attribute_value_int( + ¤t_task, "pid", pid); + get_current_attribute_value_int( + ¤t_task, "tid", tid); + if (pid != tid && + state_system->attributeExists( + current_task, "threadparent")) { + increase_attribute( + end, ¤t_task, + "threadparent/threadstotalcpunsec", + elapsed); + } + } + modify_attribute(end + 1, &cpu, "task_start", end); + } while (get_current_attribute_value_quark(&cpu, "next", cpu)); +} + +void update_state_on_refresh(unsigned long start, unsigned long end) +{ + Quark proc; + + rotate_cputime(end); + + if (get_current_attribute_value_quark(NULL, "proc", proc)) { + do { + update_proc_on_refresh(proc, start, end); + } while (get_current_attribute_value_quark( + &proc, "next", proc)); + } +} + +enum bt_cb_ret handle_statedump_process_state( + struct bt_ctf_event *call_data, void *private_data) +{ + const struct definition *scope; + unsigned long timestamp; + int pid, tid; + /* FIXME */ + /* char *procname; */ + Quark proc_quark; + + timestamp = bt_ctf_get_timestamp(call_data); + if (timestamp == -1ULL) + goto error; + + scope = bt_ctf_get_top_level_scope(call_data, + BT_EVENT_FIELDS); + 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; + } + + scope = bt_ctf_get_top_level_scope(call_data, + BT_EVENT_FIELDS); + 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; + } + + /* + * FIXME + * I first tried with bt_ctf_get_string but doesn`t work at all + * It couldn`t find the field _name because it is an integer in + * the metadata and not a string like _filename for the + * statedump_file_descriptor + */ + /* + * FIXME + * We use "Dumped" instead of procname because procname sometimes + * causes memory corruption. All FIXME's in this function are + * related to this. + */ + /* procname = bt_ctf_get_char_array(bt_ctf_get_field(call_data, + scope, "_name")); */ + if (bt_ctf_field_get_error()) { + fprintf(stderr, "Missing process name context info\n"); + goto error; + } + + proc_quark = get_proc(tid, (char *)"Dumped", timestamp); + modify_attribute(timestamp, &proc_quark, "pid", pid); + + /* FIXME */ + /* free(procname); */ + + return BT_CB_OK; + +error: + return BT_CB_ERROR_STOP; +} + +std::string path_name_from_cpuid(int cpuid) +{ + std::stringstream ss; + ss << "cpus/" << cpuid; + return ss.str(); +} + +std::string thread_path_name_from_tid(int tid) +{ + std::stringstream ss; + ss << "threads/" << tid; + return ss.str(); +} + +std::string path_name_from_tid(int tid) +{ + std::stringstream ss; + ss << "processes/" << tid; + return ss.str(); +} + +std::string path_name_from_fd(int fd) +{ + std::stringstream ss; + ss << "files/" << fd; + return ss.str(); +} + +void update_file_on_refresh(Quark proc, Quark file, unsigned long end) +{ + int flag; + int fd; + std::string path; + Quark file_pointer; + + /* File closed */ + get_current_attribute_value_int(&file, "file/flag", flag); + if (flag == __NR_close) { + get_current_attribute_value_int(&file, "file/fd", fd); + if (fd != -1) { + path = path_name_from_fd(fd); + file_pointer = state_system->getQuark(proc, path); + state_system->removeAttribute(end + 1, file_pointer); + state_system->modifyAttribute(end + 1, + state_system->getQuark( + file, "file/fd"), + -1); + } + } +} + +void update_proc_on_refresh(Quark proc, unsigned long start, unsigned long end) +{ + + int fileread = 0; + int filewrite = 0; + unsigned long time; + unsigned long death; + Quark parent; + Quark file; + int tid; + std::string path; + + /* Process died */ + if (get_current_attribute_value_ulong(&proc, "death", death) && + death > 0 && death <= end) { + /* Remove thread from threadparent's threads */ + if (get_current_attribute_value_quark( + &proc, "threadparent", parent)) { + get_current_attribute_value_int(&proc, "tid", tid); + path = thread_path_name_from_tid(tid); + remove_from_sequence( + end + 1, + state_system->getQuark(parent, path), + state_system->getQuark(parent, "threads")); + } + + remove_from_sequence(end + 1, proc, + state_system->getQuark("proc")); + return; + } + + /* Files */ + if (get_current_attribute_value_quark( + &proc, "files_history/current", file)) { + do { + update_file_on_refresh(proc, file, end); + } while (get_current_attribute_value_quark( + &file, "next", file)); + } + + /* compute the stream speed */ + if (end - start != 0) { + time = end - start; + get_current_attribute_value_int(&proc, "fileread", fileread); + get_current_attribute_value_int(&proc, "filewrite", filewrite); + modify_attribute(end, &proc, "fileread", (int)(fileread / time)); + modify_attribute(end, &proc, "filewrite", (int)(filewrite / time)); + } +} + +void modify_attribute(unsigned long timestamp, const Quark *starting_node, + std::string attribute, int value) +{ + Quark q; + if (starting_node) + q = state_system->getQuark(*starting_node, attribute); + else + q = state_system->getQuark(attribute); + state_system->updateCurrentState(q, value); + + modified_quarks.insert(q); +} + +void modify_attribute(unsigned long timestamp, const Quark *starting_node, + std::string attribute, Quark value) +{ + Quark q; + if (starting_node) + q = state_system->getQuark(*starting_node, attribute); + else + q = state_system->getQuark(attribute); + state_system->updateCurrentState(q, StateValue::SharedPtr( + new QuarkStateValue(value))); + + modified_quarks.insert(q); +} + +void modify_attribute(unsigned long timestamp, const Quark *starting_node, + std::string attribute, char *value) +{ + Quark q; + if (starting_node) + q = state_system->getQuark(*starting_node, attribute); + else + q = state_system->getQuark(attribute); + state_system->updateCurrentState(q, std::string(value)); + + modified_quarks.insert(q); +} + +void modify_attribute(unsigned long timestamp, const Quark *starting_node, + std::string attribute, std::string value) +{ + Quark q; + if (starting_node) + q = state_system->getQuark(*starting_node, attribute); + else + q = state_system->getQuark(attribute); + state_system->updateCurrentState(q, value); + + modified_quarks.insert(q); +} + +void modify_attribute(unsigned long timestamp, const Quark *starting_node, + std::string attribute, unsigned long value) +{ + // Libstate works with 32-bit ints, we split the 64-bit ulong + // into 2 32-bit ints + unsigned int h = value >> 32; + unsigned int l = value & 0xffffffff; + Quark ql, qh; + + if (starting_node) { + ql = state_system->getQuark(*starting_node, attribute + "/l"); + qh = state_system->getQuark(*starting_node, attribute + "/h"); + } + else { + ql = state_system->getQuark(attribute + "/l"); + qh = state_system->getQuark(attribute + "/h"); + } + state_system->updateCurrentState(ql, l); + state_system->updateCurrentState(qh, h); + + modified_quarks.insert(ql); + modified_quarks.insert(qh); +} + +void nullify_attribute(unsigned long timestamp, const Quark *starting_node, + std::string attribute) +{ + Quark q; + q = state_system->getQuark(*starting_node, attribute); + state_system->updateCurrentState(q, StateValue::getNullValue()); + + modified_quarks.insert(q); +} + +void increment_attribute(unsigned long timestamp, const Quark *starting_node, + std::string attribute) +{ + increase_attribute(timestamp, starting_node, attribute, 1); +} + +void increase_attribute(unsigned long timestamp, const Quark *starting_node, + std::string attribute, int amount) +{ + int starting_value = 0; + get_current_attribute_value_int(starting_node, attribute, + starting_value); + modify_attribute(timestamp, starting_node, attribute, + starting_value + amount); +} + +void increase_attribute(unsigned long timestamp, const Quark *starting_node, + std::string attribute, unsigned long amount) +{ + unsigned long starting_value = 0; + get_current_attribute_value_ulong(starting_node, attribute, + starting_value); + modify_attribute(timestamp, starting_node, attribute, + starting_value + amount); +} + +void decrement_attribute(unsigned long timestamp, const Quark *starting_node, + std::string attribute) +{ + decrease_attribute(timestamp, starting_node, attribute, 1); +} + +void decrease_attribute(unsigned long timestamp, const Quark *starting_node, + std::string attribute, int amount) +{ + int starting_value = 0; + get_current_attribute_value_int(starting_node, attribute, + starting_value); + modify_attribute(timestamp, starting_node, attribute, + starting_value - amount); +} + +void decrease_attribute(unsigned long timestamp, const Quark *starting_node, + std::string attribute, unsigned long amount) +{ + unsigned long starting_value = 0; + get_current_attribute_value_ulong(starting_node, attribute, + starting_value); + modify_attribute(timestamp, starting_node, attribute, + starting_value - amount); +} + +bool get_current_attribute_value_int(const Quark *starting_node, + std::string attribute, int &value) +{ + IntegerStateValue::SharedPtr value_ptr; + Quark q; + + if (starting_node == NULL) + q = state_system->getQuark(attribute); + else + q = state_system->getQuark(*starting_node, attribute); + value_ptr = std::tr1::dynamic_pointer_cast( + state_system->getCurrentStateValue(q)); + + if (value_ptr) { + value = value_ptr->getValue(); + return true; + } + return false; +} + +bool get_current_attribute_value_ulong(const Quark *starting_node, + std::string attribute, + unsigned long &value) +{ + // Libstate works with 32-bit ints, we split the 64-bit ulong + // into 2 32-bit ints + IntegerStateValue::SharedPtr value_ptr_l, value_ptr_h; + Quark ql, qh; + unsigned int l, h; + + if (starting_node == NULL) { + ql = state_system->getQuark(attribute + "/l"); + qh = state_system->getQuark(attribute + "/h"); + } + else { + ql = state_system->getQuark(*starting_node, attribute + "/l"); + qh = state_system->getQuark(*starting_node, attribute + "/h"); + } + value_ptr_l = std::tr1::dynamic_pointer_cast( + state_system->getCurrentStateValue(ql)); + value_ptr_h = std::tr1::dynamic_pointer_cast( + state_system->getCurrentStateValue(qh)); + + if (value_ptr_l && value_ptr_h) { + l = value_ptr_l->getValue(); + h = value_ptr_h->getValue(); + value = (unsigned long)h << 32 | l; + return true; + } + return false; +} + +bool get_current_attribute_value_quark(const Quark *starting_node, + std::string attribute, Quark &value) +{ + QuarkStateValue::SharedPtr value_ptr; + IntegerStateValue::SharedPtr value_ptr_int; + Quark q; + + if (starting_node == NULL) + q = state_system->getQuark(attribute); + else + q = state_system->getQuark(*starting_node, attribute); + value_ptr = std::tr1::dynamic_pointer_cast( + state_system->getCurrentStateValue(q)); + + if (value_ptr) { + value = value_ptr->getValue(); + return true; + } else { + /* Quark attribute support is not fully integrated in libstate + and librbrntrvll so quarks may get demoted to ints */ + value_ptr_int = std::tr1::dynamic_pointer_cast( + state_system->getCurrentStateValue(q)); + if (value_ptr_int) { + value = (Quark)value_ptr_int->getValue(); + return true; + } + } + return false; +} + +bool get_current_attribute_value_string(const Quark *starting_node, + std::string attribute, std::string &value) +{ + StringStateValue::SharedPtr value_ptr; + Quark q; + + if (starting_node == NULL) + q = state_system->getQuark(attribute); + else + q = state_system->getQuark(*starting_node, attribute); + value_ptr = std::tr1::dynamic_pointer_cast( + state_system->getCurrentStateValue(q)); + + if (value_ptr) { + value = value_ptr->getValue(); + return true; + } + return false; +} + +bool get_attribute_value_at_int(unsigned long timestamp, + const Quark *starting_node, + std::string attribute, + int &value) +{ + StateInterval interval; + Quark q; + IntegerStateValue::SharedPtr value_ptr; + + if (starting_node == NULL) + q = state_system->getQuark(attribute); + else + q = state_system->getQuark(*starting_node, attribute); + interval = state_system->getStateOfAt(timestamp, q); + value_ptr = std::tr1::dynamic_pointer_cast( + interval.value); + + if (value_ptr) { + value = value_ptr->getValue(); + return true; + } + return false; +} + +bool get_attribute_value_at_ulong(unsigned long timestamp, + const Quark *starting_node, + std::string attribute, + unsigned long &value) +{ + // Libstate works with 32-bit ints, we split the 64-bit ulong + // into 2 32-bit ints + StateInterval interval_l, interval_h; + Quark ql, qh; + IntegerStateValue::SharedPtr value_ptr_l, value_ptr_h; + unsigned int l, h; + + if (starting_node == NULL) { + ql = state_system->getQuark(attribute + "/l"); + qh = state_system->getQuark(attribute + "/h"); + } + else { + ql = state_system->getQuark(*starting_node, attribute + "/l"); + qh = state_system->getQuark(*starting_node, attribute + "/h"); + } + interval_l = state_system->getStateOfAt(timestamp, ql); + interval_h = state_system->getStateOfAt(timestamp, qh); + value_ptr_l = std::tr1::dynamic_pointer_cast( + interval_l.value); + value_ptr_h = std::tr1::dynamic_pointer_cast( + interval_h.value); + + if (value_ptr_l && value_ptr_h) { + l = value_ptr_l->getValue(); + h = value_ptr_h->getValue(); + value = (unsigned long)h << 32 | l; + return true; + } + return false; +} + +bool get_attribute_value_at_quark(unsigned long timestamp, + const Quark *starting_node, + std::string attribute, + Quark &value) +{ + StateInterval interval; + Quark q; + QuarkStateValue::SharedPtr value_ptr; + IntegerStateValue::SharedPtr value_ptr_int; + + if (starting_node == NULL) + q = state_system->getQuark(attribute); + else + q = state_system->getQuark(*starting_node, attribute); + interval = state_system->getStateOfAt(timestamp, q); + value_ptr = std::tr1::dynamic_pointer_cast( + interval.value); + + if (value_ptr) { + value = value_ptr->getValue(); + return true; + } else { + /* Quark attribute support is not fully integrated in libstate + and librbrntrvll so quarks may get demoted to ints */ + value_ptr_int = std::tr1::dynamic_pointer_cast( + interval.value); + if (value_ptr_int) { + value = (Quark)value_ptr_int->getValue(); + return true; + } + } + return false; +} + +bool get_attribute_value_at_string(unsigned long timestamp, + const Quark *starting_node, + std::string attribute, + std::string &value) +{ + StateInterval interval; + Quark q; + StringStateValue::SharedPtr value_ptr; + + if (starting_node == NULL) + q = state_system->getQuark(attribute); + else + q = state_system->getQuark(*starting_node, attribute); + interval = state_system->getStateOfAt(timestamp, q); + value_ptr = std::tr1::dynamic_pointer_cast( + interval.value); + + if (value_ptr) { + value = value_ptr->getValue(); + return true; + } + return false; +} + +void add_in_sequence(unsigned long timestamp, Quark item, Quark beg) +{ + Quark old_newest; + if (get_current_attribute_value_quark(&beg, "", old_newest)) { + modify_attribute(timestamp, &item, "next", old_newest); + modify_attribute(timestamp, &old_newest, "prev", item); + } + modify_attribute(timestamp, &beg, "", item); + +} + +void remove_from_sequence(unsigned long timestamp, Quark item, Quark beg) +{ + Quark prev; + Quark next; + bool hasPrev; + bool hasNext; + + hasPrev = get_current_attribute_value_quark(&item, + "prev", prev); + hasNext = get_current_attribute_value_quark(&item, + "next", next); + if (hasPrev && hasNext) { + state_system->modifyAttribute(timestamp, + state_system->getQuark(next, "prev"), + StateValue::SharedPtr( + new QuarkStateValue(prev))); + state_system->modifyAttribute(timestamp, + state_system->getQuark(prev, "next"), + StateValue::SharedPtr( + new QuarkStateValue(next))); + } else if (hasPrev) { + state_system->modifyAttribute(timestamp, + state_system->getQuark(prev, "next"), + StateValue::getNullValue()); + } else if (hasNext) { + state_system->modifyAttribute(timestamp, + state_system->getQuark(next, "prev"), + StateValue::getNullValue()); + state_system->modifyAttribute(timestamp, beg, + StateValue::SharedPtr( + new QuarkStateValue(next))); + } else { + state_system->modifyAttribute(timestamp, beg, + StateValue::getNullValue()); + } + + state_system->removeAttribute(timestamp, item); +} + +int get_sequence_length(unsigned long timestamp, Quark beg) +{ + int length = 0; + Quark it; + + if (get_attribute_value_at_quark(timestamp, &beg, "", it)) { + do { + length++; + } while (get_attribute_value_at_quark( + timestamp, &it, "next", it)); + } + + return length; +} + +bool get_interval_value_int(unsigned long start, + unsigned long end, + const Quark *starting_node, + std::string attribute, + int &value) +{ + int start_value, end_value; + + if (get_attribute_value_at_int( + start, starting_node, attribute, start_value) && + get_attribute_value_at_int( + end, starting_node, attribute, end_value)) { + value = end_value - start_value; + return true; + } + return false; +} + +bool get_interval_value_ulong(unsigned long start, + unsigned long end, + const Quark *starting_node, + std::string attribute, + unsigned long &value) +{ + unsigned long start_value, end_value; + + if (get_attribute_value_at_ulong( + start, starting_node, attribute, start_value) && + get_attribute_value_at_ulong( + end, starting_node, attribute, end_value)) { + value = end_value - start_value; + return true; + } + return false; +} + +int sequence_to_array(unsigned long timestamp, Quark beg, Quark *arr, + int len) +{ + int ret = 0; + Quark it; + + if (arr == NULL || len < 1) + return 0; + + if (get_attribute_value_at_quark(timestamp, &beg, "", it)) { + do { + arr[ret] = it; + ret++; + } while (get_attribute_value_at_quark( + timestamp, &it, "next", it) && ret < len); + } + + return ret; +} + +int get_global_perf_list(unsigned long timestamp, Quark *arr, int len) +{ + std::map perfs; + std::string key; + int val; + int ret; + Quark proc; + Quark perf; + std::map::const_iterator iter; + + if (arr == NULL || len < 1) + return 0; + + if (get_attribute_value_at_quark(timestamp, NULL, "proc", proc)) { + do { + if (get_attribute_value_at_quark(timestamp, &proc, + "perf", perf)) { + do { + get_attribute_value_at_string(timestamp, + &perf, + "key", + key); + get_attribute_value_at_int(timestamp, + &perf, + "count", + val); + if (perfs.find(key) != perfs.end()) + perfs[key] += val; + else + perfs[key] = val; + } while (get_attribute_value_at_quark(timestamp, + &perf, + "next", + perf)); + } + } while (get_attribute_value_at_quark(timestamp, &proc, "next", + proc)); + } + + for (iter = perfs.begin(), ret = 0; iter != perfs.end() && ret < len; + iter++, ret++) { + perf = state_system->getQuark("perf/" + iter->first); + modify_attribute(timestamp, &perf, "count", iter->second); + arr[ret] = perf; + } + + return ret; +} + +int get_global_perf_list_size(unsigned long timestamp) +{ + std::set perfs; + std::string key; + int len = 0; + Quark proc; + Quark perf; + + if (get_attribute_value_at_quark(timestamp, NULL, "proc", proc)) { + do { + if (get_attribute_value_at_quark(timestamp, &proc, + "perf", perf)) { + do { + get_attribute_value_at_string(timestamp, + &perf, + "key", + key); + if (perfs.find(key) == perfs.end()) { + perfs.insert(key); + len++; + } + } while (get_attribute_value_at_quark(timestamp, + &perf, + "next", + perf)); + } + } while (get_attribute_value_at_quark(timestamp, &proc, "next", + proc)); + } + + return len; +} + +int get_number_of_opened_files(unsigned long timestamp, Quark proc) +{ + int len = 0; + Quark fh; + int fd; + + if (get_attribute_value_at_quark(timestamp, &proc, + "files_history/current", fh)) { + do { + get_attribute_value_at_int(timestamp, &fh, "file/fd", + fd); + if (fd != -1) + len++; + } while (get_attribute_value_at_quark(timestamp, &fh, "next", + fh)); + } + + return len; +} + +int get_opened_files(unsigned long timestamp, Quark proc, Quark *arr, int len) +{ + int ret = 0; + Quark file, fh; + int fd; + + if (arr == NULL || len < 1) + return 0; + + if (get_attribute_value_at_quark(timestamp, &proc, + "files_history/current", fh)) { + do { + file = state_system->getQuark(fh, "file"); + get_attribute_value_at_int(timestamp, &file, "fd", + fd); + if (fd != -1) { + arr[ret] = file; + ret++; + } + if (ret == len) + break; + } while (get_attribute_value_at_quark(timestamp, &fh, "next", + fh)); + } + + return ret; +} diff --git a/src/common.h b/src/common.h index fd9a367..4aad992 100644 --- a/src/common.h +++ b/src/common.h @@ -19,51 +19,135 @@ #define _COMMON_H #include +extern "C" { #include +} +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + #include "lttngtoptypes.h" #include "cputop.h" +using namespace State; + #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 */ -GHashTable *global_perf_liszt; - -struct lttngtop *data; +extern StateSystem *state_system; +extern std::set modified_quarks; +const unsigned long refresh_display = 1 * NSEC_PER_SEC; +extern unsigned long last_display_update; +extern unsigned long first_display_update; -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); +extern sem_t goodtodisplay, goodtoupdate, timer, pause_sem, end_trace_sem, bootstrap; -struct processtop *get_proc_pid(struct lttngtop *ctx, int tid, int pid, - unsigned long timestamp); +bool find_process_tid(int tid, Quark &ret_proc_quark); +Quark add_proc(int tid, std::string comm, unsigned long timestamp); +void update_proc(unsigned long timestamp, Quark proc, int pid, int tid, + int ppid, char *comm); +void add_thread(unsigned long timestamp, Quark parent, Quark thread); +Quark get_proc(int tid, char *comm, unsigned long timestamp); +Quark get_proc_pid(int pid, int tid, unsigned long timestamp); +void death_proc(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); -void reset_global_counters(void); +Quark add_cpu(int cpu, unsigned long timestamp); +Quark get_cpu(int cpu, unsigned long timestamp); +void update_state_on_refresh(unsigned long start, unsigned long end); +Quark get_perf_counter(unsigned long timestamp, Quark root, std::string name); /* common field access functions */ -uint64_t get_cpu_id(const struct bt_ctf_event *event); -uint64_t get_context_tid(const struct bt_ctf_event *event); -uint64_t get_context_pid(const struct bt_ctf_event *event); -uint64_t get_context_ppid(const struct bt_ctf_event *event); +int get_cpu_id(const struct bt_ctf_event *event); +int get_context_tid(const struct bt_ctf_event *event); +int get_context_pid(const struct bt_ctf_event *event); +int get_context_ppid(const struct bt_ctf_event *event); char *get_context_comm(const struct bt_ctf_event *event); -enum bt_cb_ret handle_statedump_process_state(struct bt_ctf_event *call_data, - void *private_data); +enum bt_cb_ret handle_statedump_process_state( + struct bt_ctf_event *call_data, void *private_data); + +std::string path_name_from_cpuid(int cpuid); +std::string thread_path_name_from_tid(int tid); +std::string path_name_from_tid(int tid); +std::string path_name_from_fd(int fd); + +void update_file_on_refresh(Quark proc, Quark file, unsigned long end); +void update_proc_on_refresh(Quark proc, unsigned long start, unsigned long end); + +void modify_attribute(unsigned long timestamp, const Quark *starting_node, + std::string attribute, int value); +void modify_attribute(unsigned long timestamp, const Quark *starting_node, + std::string attribute, Quark value); +void modify_attribute(unsigned long timestamp, const Quark *starting_node, + std::string attribute, char *value); +void modify_attribute(unsigned long timestamp, const Quark *starting_node, + std::string attribute, unsigned long value); +void modify_attribute(unsigned long timestamp, const Quark *starting_node, + std::string attribute, std::string value); +void nullify_attribute(unsigned long timestamp, const Quark *starting_node, + std::string attribute); +void increment_attribute(unsigned long timestamp, const Quark *starting_node, + std::string attribute); +void increase_attribute(unsigned long timestamp, const Quark *starting_node, + std::string attribute, int amount); +void increase_attribute(unsigned long timestamp, const Quark *starting_node, + std::string attribute, unsigned long amount); +void decrement_attribute(unsigned long timestamp, const Quark *starting_node, + std::string attribute); +void decrease_attribute(unsigned long timestamp, const Quark *starting_node, + std::string attribute, int amount); +void decrease_attribute(unsigned long timestamp, const Quark *starting_node, + std::string attribute, unsigned long amount); +bool get_current_attribute_value_int(const Quark *starting_node, + std::string attribute, int &value); +bool get_current_attribute_value_ulong(const Quark *starting_node, + std::string attribute, unsigned long &value); +bool get_current_attribute_value_quark(const Quark *starting_node, + std::string attribute, Quark &value); +bool get_current_attribute_value_string(const Quark *starting_node, + std::string attribute, std::string &value); +bool get_attribute_value_at_int(unsigned long timestamp, + const Quark *starting_node, + std::string attribute, + int &value); +bool get_attribute_value_at_ulong(unsigned long timestamp, + const Quark *starting_node, + std::string attribute, + unsigned long &value); +bool get_attribute_value_at_quark(unsigned long timestamp, + const Quark *starting_node, + std::string attribute, + Quark &value); +bool get_attribute_value_at_string(unsigned long timestamp, + const Quark *starting_node, + std::string attribute, + std::string &value); +bool get_interval_value_int(unsigned long start, + unsigned long end, + const Quark *starting_node, + std::string attribute, + int &value); +bool get_interval_value_ulong(unsigned long start, + unsigned long end, + const Quark *starting_node, + std::string attribute, + unsigned long &value); +void add_in_sequence(unsigned long timestamp, Quark item, Quark beg); +void remove_from_sequence(unsigned long timestamp, Quark item, Quark beg); +int get_sequence_length(unsigned long timestamp, Quark beg); +int sequence_to_array(unsigned long timestamp, Quark beg, Quark *arr, + int len); +int get_global_perf_list(unsigned long timestamp, Quark *arr, int len); +int get_global_perf_list_size(unsigned long timestamp); +int get_number_of_opened_files(unsigned long timestamp, Quark proc); +int get_opened_files(unsigned long timestamp, Quark proc, Quark *arr, int len); #endif /* _COMMON_H */ diff --git a/src/cputop.c b/src/cputop.c deleted file mode 100644 index eb7afb3..0000000 --- a/src/cputop.c +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (C) 2011-2012 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., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include - -#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) -{ - const 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; - } - - cpu_id = get_cpu_id(call_data); - - 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) -{ - const 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 field\n"); - goto error; - } - - death_proc(<tngtop, tid, comm, timestamp); - - return BT_CB_OK; - -error: - return BT_CB_ERROR_STOP; - -} - diff --git a/src/cputop.cpp b/src/cputop.cpp new file mode 100644 index 0000000..4fde9a8 --- /dev/null +++ b/src/cputop.cpp @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2011-2012 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +extern "C" { +#include +} + +#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) +{ + Quark cpu_quark; + Quark current_task_quark; + bool current_task_found; + int current_task_pid; + int current_task_tid; + unsigned long elapsed; + unsigned long task_start; + bool current_task_threadparent_found; + Quark current_task_threadparent; + + cpu_quark = get_cpu(cpu, timestamp); + current_task_found = get_current_attribute_value_quark(&cpu_quark, + "current_task", current_task_quark); + + if (current_task_found) { + get_current_attribute_value_int(¤t_task_quark, "pid", + current_task_pid); + if (current_task_pid == prev_pid) { + get_current_attribute_value_ulong(&cpu_quark, + "task_start", task_start); + elapsed = timestamp - task_start; + increase_attribute(timestamp, ¤t_task_quark, + "totalcpunsec", elapsed); + increase_attribute(timestamp, ¤t_task_quark, + "threadstotalcpunsec", elapsed); + get_current_attribute_value_int(¤t_task_quark, + "tid", current_task_tid); + if (current_task_tid != current_task_pid) { + current_task_threadparent_found = + get_current_attribute_value_quark( + ¤t_task_quark, + "threadparent", + current_task_threadparent); + if (current_task_threadparent_found) + increase_attribute(timestamp, + ¤t_task_threadparent, + "threadstotalcpuns", elapsed); + } + } + } + + if (next_pid != 0) + modify_attribute(timestamp, &cpu_quark, "current_task", + get_proc(next_pid, next_comm, timestamp)); + else + nullify_attribute(timestamp, &cpu_quark, "current_task"); + + modify_attribute(timestamp, &cpu_quark, "task_start", timestamp); +} + +enum bt_cb_ret handle_sched_switch(struct bt_ctf_event *call_data, + void *private_data) +{ + const 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; + } + + cpu_id = get_cpu_id(call_data); + + 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) +{ + const 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 field\n"); + goto error; + } + + death_proc(tid, comm, timestamp); + + return BT_CB_OK; + +error: + return BT_CB_ERROR_STOP; + +} + diff --git a/src/cputop.h b/src/cputop.h index c146644..e782c7f 100644 --- a/src/cputop.h +++ b/src/cputop.h @@ -18,8 +18,10 @@ #ifndef _LTTNGTOP_H #define _LTTNGTOP_H +extern "C" { #include #include +} #include #include diff --git a/src/cursesdisplay.c b/src/cursesdisplay.c deleted file mode 100644 index beb7bdf..0000000 --- a/src/cursesdisplay.c +++ /dev/null @@ -1,1586 +0,0 @@ -/* - * Copyright (C) 2011-2012 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., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "cursesdisplay.h" -#include "lttngtoptypes.h" -#include "iostreamtop.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 *pref_panel_window = NULL; -PANEL *pref_panel, *main_panel; - -int pref_panel_visible = 0; -int pref_line_selected = 0; -int pref_current_sort = 0; - -int last_display_index, currently_displayed_index; - -struct processtop *selected_process = NULL; -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 max_center_lines; -GPtrArray *selected_processes; - -pthread_t keyboard_thread; - -struct header_view cputopview[4]; -struct header_view iostreamtopview[3]; -struct header_view fileview[3]; - -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 */ - init_pair(6, COLOR_WHITE, COLOR_GREEN); /* selected process */ - } - 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, "%d", 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); -} - -int process_selected(struct processtop *process) -{ - int i; - struct processtop *stored_process; - - for (i = 0; i < selected_processes->len; i++) { - stored_process = g_ptr_array_index(selected_processes, i); - if (!stored_process) - return 0; - if (stored_process->tid == process->tid) - return 1; - } - return 0; -} - -void update_selected_processes() -{ - int i; - struct processtop *stored_process; - - if (process_selected(selected_process)) { - for (i = 0; i < selected_processes->len; i++) { - stored_process = g_ptr_array_index(selected_processes, i); - if (!stored_process) - return; - if (stored_process->tid == selected_process->tid) - g_ptr_array_remove(selected_processes, - stored_process); - print_log("Process removed"); - } - } else { - g_ptr_array_add(selected_processes, selected_process); - print_log("Process added"); - } -} - -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, "F4", "IOTop ", current_view == iostream); - print_key(footer, "Enter", "Details ", current_view == process_details); - print_key(footer, "Space", "Highlight ", 0); - print_key(footer, "q", "Quit ", 0); - print_key(footer, "r", "Pref ", 0); - print_key(footer, "t", "Threads ", toggle_threads); - 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, "Threads"); - mvwprintw(header, 3, 4, "FDs"); - wattroff(header, A_BOLD); - wrefresh(header); -} - -struct tm format_timestamp(uint64_t timestamp) -{ - struct tm tm; - uint64_t ts_sec = 0, ts_nsec; - time_t time_s; - - ts_nsec = timestamp; - ts_sec += ts_nsec / NSEC_PER_SEC; - ts_nsec = ts_nsec % NSEC_PER_SEC; - - time_s = (time_t) ts_sec; - - localtime_r(&time_s, &tm); - - return tm; -} - -static void scale_unit(uint64_t bytes, char *ret) -{ - if (bytes >= 1000000000) - sprintf(ret, "%" PRIu64 "G", bytes/1000000000); - if (bytes >= 1000000) - sprintf(ret, "%" PRIu64 "M", bytes/1000000); - else if (bytes >= 1000) - sprintf(ret, "%" PRIu64 "K", bytes/1000); - else - sprintf(ret, "%" PRIu64, bytes); -} - -uint64_t total_io() -{ - int i; - struct processtop *tmp; - uint64_t total = 0; - - for (i = 0; i < data->process_table->len; i++) { - tmp = g_ptr_array_index(data->process_table, i); - total += tmp->fileread; - total += tmp->filewrite; - } - - return total; -} - -void update_header() -{ - struct tm start, end; - uint64_t ts_nsec_start, ts_nsec_end; - char io[4]; - - ts_nsec_start = data->start % NSEC_PER_SEC; - start = format_timestamp(data->start); - - ts_nsec_end = data->end % NSEC_PER_SEC; - end = format_timestamp(data->end); - - werase(header); - box(header, 0 , 0); - set_window_title(header, "Statistics for interval "); - wattron(header, A_BOLD); - - wprintw(header, "[%02d:%02d:%02d.%09" PRIu64 ", %02d:%02d:%02d.%09" PRIu64 "[", - start.tm_hour, start.tm_min, start.tm_sec, ts_nsec_start, - end.tm_hour, end.tm_min, end.tm_sec, ts_nsec_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, "Threads", data->nbthreads, data->nbnewthreads, - -1*(data->nbdeadthreads)); - print_headers(3, "FDs", data->nbfiles, data->nbnewfiles, - -1*(data->nbclosedfiles)); - scale_unit(total_io(), io); - mvwprintw(header, 3, 43, "%sB/sec", io); - 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_tid_desc(gconstpointer p1, gconstpointer p2) -{ - struct processtop *n1 = *(struct processtop **)p1; - struct processtop *n2 = *(struct processtop **)p2; - unsigned long totaln1 = n1->tid; - unsigned long totaln2 = n2->tid; - - if (totaln1 < totaln2) - return 1; - if (totaln1 == totaln2) - return 0; - return -1; -} - -gint sort_by_pid_desc(gconstpointer p1, gconstpointer p2) -{ - struct processtop *n1 = *(struct processtop **)p1; - struct processtop *n2 = *(struct processtop **)p2; - unsigned long totaln1 = n1->pid; - unsigned long totaln2 = n2->pid; - - if (totaln1 < totaln2) - return 1; - if (totaln1 == totaln2) - return 0; - return -1; -} - -gint sort_by_process_read_desc(gconstpointer p1, gconstpointer p2) -{ - struct processtop *n1 = *(struct processtop **)p1; - struct processtop *n2 = *(struct processtop **)p2; - unsigned long totaln1 = n1->fileread; - unsigned long totaln2 = n2->fileread; - - if (totaln1 < totaln2) - return 1; - if (totaln1 == totaln2) - return 0; - return -1; -} - -gint sort_by_process_write_desc(gconstpointer p1, gconstpointer p2) -{ - struct processtop *n1 = *(struct processtop **)p1; - struct processtop *n2 = *(struct processtop **)p2; - unsigned long totaln1 = n1->filewrite; - unsigned long totaln2 = n2->filewrite; - - if (totaln1 < totaln2) - return 1; - if (totaln1 == totaln2) - return 0; - return -1; -} - -gint sort_by_process_total_desc(gconstpointer p1, gconstpointer p2) -{ - struct processtop *n1 = *(struct processtop **)p1; - struct processtop *n2 = *(struct processtop **)p2; - unsigned long totaln1 = n1->totalfilewrite + n1->totalfileread; - unsigned long totaln2 = n2->totalfilewrite + n2->totalfileread; - - if (totaln1 < totaln2) - return 1; - if (totaln1 == totaln2) - return 0; - return -1; -} - -gint sort_by_file_read_desc(gconstpointer p1, gconstpointer p2) -{ - struct files *n1 = *(struct files **)p1; - struct files *n2 = *(struct files **)p2; - unsigned long totaln1; - unsigned long totaln2; - - totaln1 = n1->read; - totaln2 = n2->read; - - if (totaln1 < totaln2) - return 1; - if (totaln1 == totaln2) - return 0; - return -1; -} - -gint sort_by_file_write_desc(gconstpointer p1, gconstpointer p2) -{ - struct files *n1 = *(struct files **)p1; - struct files *n2 = *(struct files **)p2; - unsigned long totaln1; - unsigned long totaln2; - - totaln1 = n1->write; - totaln2 = n2->write; - - if (totaln1 < totaln2) - return 1; - if (totaln1 == totaln2) - return 0; - return -1; -} - -gint sort_by_file_fd_desc(gconstpointer p1, gconstpointer p2) -{ - struct files *n1 = *(struct files **)p1; - struct files *n2 = *(struct files **)p2; - unsigned long totaln1; - unsigned long totaln2; - - totaln1 = n1->fd; - totaln2 = n2->fd; - - 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; - int column; - - elapsed = data->end - data->start; - maxcputime = elapsed * data->cpu_table->len / 100.0; - - if (cputopview[0].sort == 1) - g_ptr_array_sort(data->process_table, sort_by_cpu_desc); - else if (cputopview[1].sort == 1) - g_ptr_array_sort(data->process_table, sort_by_pid_desc); - else if (cputopview[2].sort == 1) - g_ptr_array_sort(data->process_table, sort_by_tid_desc); - else if (cputopview[3].sort == 1) - g_ptr_array_sort(data->process_table, sort_by_cpu_desc); - else - g_ptr_array_sort(data->process_table, sort_by_cpu_desc); - - set_window_title(center, "CPU Top"); - wattron(center, A_BOLD); - column = 1; - for (i = 0; i < 4; i++) { - if (cputopview[i].sort) { - wattron(center, A_UNDERLINE); - pref_current_sort = i; - } - mvwprintw(center, 1, column, cputopview[i].title); - wattroff(center, A_UNDERLINE); - column += 10; - } - wattroff(center, A_BOLD); - - max_center_lines = LINES - 5 - 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 (tmp->pid != tmp->tid) - if (toggle_threads == -1) - continue; - - if (process_selected(tmp)) { - wattron(center, COLOR_PAIR(6)); - mvwhline(center, current_line + header_offset, 1, ' ', COLS-3); - } - if (current_line == selected_line) { - selected_process = tmp; - 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); - /* PID */ - mvwprintw(center, current_line + header_offset, 11, "%d", tmp->pid); - /* TID */ - mvwprintw(center, current_line + header_offset, 21, "%d", tmp->tid); - /* NAME */ - mvwprintw(center, current_line + header_offset, 31, "%s", tmp->comm); - wattroff(center, COLOR_PAIR(6)); - 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", key); - mvwprintw(center, line, 30, " "); - wattroff(center, A_BOLD); -} - -void update_process_details() -{ - unsigned long elapsed; - double maxcputime; - struct processtop *tmp; - struct files *file_tmp; - int i, j = 0; - char unit[4]; - char filename_buf[COLS]; - int line = 1; - int column; - GPtrArray *newfilearray = g_ptr_array_new(); - GHashTableIter iter; - struct perfcounter *perfn1, *perfn2; - gpointer key; - - set_window_title(center, "Process details"); - - - tmp = find_process_tid(data, - selected_process->tid, - selected_process->comm); - elapsed = data->end - data->start; - maxcputime = elapsed * data->cpu_table->len / 100.0; - - print_key_title("Name", line++); - wprintw(center, "%s", selected_process->comm); - print_key_title("TID", line++); - wprintw(center, "%d", selected_process->tid); - if (!tmp) { - print_key_title("Does not exit at this time", 3); - return; - } - - print_key_title("PID", line++); - wprintw(center, "%d", tmp->pid); - print_key_title("PPID", line++); - wprintw(center, "%d", tmp->ppid); - print_key_title("CPU", line++); - wprintw(center, "%1.2f %%", tmp->totalcpunsec/maxcputime); - - print_key_title("READ B/s", line++); - scale_unit(tmp->fileread, unit); - wprintw(center, "%s", unit); - - print_key_title("WRITE B/s", line++); - scale_unit(tmp->filewrite, unit); - wprintw(center, "%s", unit); - - g_hash_table_iter_init(&iter, global_perf_liszt); - while (g_hash_table_iter_next (&iter, &key, (gpointer) &perfn1)) { - print_key_title((char *) key, line++); - perfn2 = g_hash_table_lookup(tmp->perf, (char *) key); - wprintw(center, "%d", perfn2 ? perfn2->count : 0); - } - line++; - - wattron(center, A_BOLD); - column = 1; - for (i = 0; i < 3; i++) { - if (fileview[i].sort) { - pref_current_sort = i; - wattron(center, A_UNDERLINE); - } - mvwprintw(center, line, column, fileview[i].title); - wattroff(center, A_UNDERLINE); - column += 10; - } - mvwprintw(center, line++, column, "FILENAME"); - wattroff(center, A_BOLD); - - /* - * since the process_files_table array could contain NULL file structures, - * and that the positions inside the array is important (it is the FD), we - * need to create a temporary array that we can sort. - */ - for (i = 0; i < tmp->process_files_table->len; i++) { - file_tmp = g_ptr_array_index(tmp->process_files_table, i); - if (file_tmp) - g_ptr_array_add(newfilearray, file_tmp); - } - - if (fileview[0].sort == 1) - g_ptr_array_sort(newfilearray, sort_by_file_fd_desc); - else if (fileview[1].sort == 1) - g_ptr_array_sort(newfilearray, sort_by_file_read_desc); - else if (fileview[2].sort == 1) - g_ptr_array_sort(newfilearray, sort_by_file_write_desc); - else - g_ptr_array_sort(newfilearray, sort_by_file_read_desc); - - for (i = selected_line; i < newfilearray->len && - i < (selected_line + max_center_lines - line + 2); i++) { - file_tmp = g_ptr_array_index(newfilearray, i); - if (!file_tmp) - continue; - mvwprintw(center, line + j, 1, "%d", file_tmp->fd); - scale_unit(file_tmp->read, unit); - mvwprintw(center, line + j, 11, "%s", unit); - scale_unit(file_tmp->write, unit); - mvwprintw(center, line + j, 21, "%s", unit); - snprintf(filename_buf, COLS - 25, "%s", file_tmp->name); - mvwprintw(center, line + j, 31, "%s", filename_buf); - j++; - } - g_ptr_array_free(newfilearray, TRUE); -} - -void update_perf() -{ - int i; - int nblinedisplayed = 0; - int current_line = 0; - struct processtop *tmp; - int header_offset = 2; - int perf_row = 40; - struct perfcounter *perfn1, *perfn2; - char *perf_key = NULL; - int value; - GHashTableIter iter; - gpointer key; - - 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; - g_hash_table_iter_init(&iter, global_perf_liszt); - while (g_hash_table_iter_next (&iter, &key, (gpointer) &perfn1)) { - if (perfn1->visible) { - if (perfn1->sort) { - /* pref_current_sort = i; */ - wattron(center, A_UNDERLINE); - } - /* + 5 to strip the "perf_" prefix */ - mvwprintw(center, 1, perf_row, "%s", - (char *) key + 5); - wattroff(center, A_UNDERLINE); - perf_row += 20; - } - if (perfn1->sort) { - perf_key = (char *) key; - } - } - 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++) { - tmp = g_ptr_array_index(data->process_table, i); - if (tmp->pid != tmp->tid) - if (toggle_threads == -1) - continue; - - if (process_selected(tmp)) { - wattron(center, COLOR_PAIR(6)); - mvwhline(center, current_line + header_offset, 1, ' ', COLS-3); - } - 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); - - g_hash_table_iter_init(&iter, global_perf_liszt); - - perf_row = 40; - while (g_hash_table_iter_next (&iter, &key, (gpointer) &perfn1)) { - if (perfn1->visible) { - perfn2 = g_hash_table_lookup(tmp->perf, (char *) key); - if (perfn2) - value = perfn2->count; - else - value = 0; - mvwprintw(center, current_line + header_offset, - perf_row, "%d", value); - perf_row += 20; - } - } - - wattroff(center, COLOR_PAIR(6)); - wattroff(center, COLOR_PAIR(5)); - nblinedisplayed++; - current_line++; - } -} - -void update_iostream() -{ - int i; - int header_offset = 2; - struct processtop *tmp; - int nblinedisplayed = 0; - int current_line = 0; - int total = 0; - char unit[4]; - int column; - - set_window_title(center, "IO Top"); - wattron(center, A_BOLD); - mvwprintw(center, 1, 1, "PID"); - mvwprintw(center, 1, 11, "TID"); - mvwprintw(center, 1, 22, "NAME"); - column = 40; - for (i = 0; i < 3; i++) { - if (iostreamtopview[i].sort) { - pref_current_sort = i; - wattron(center, A_UNDERLINE); - } - mvwprintw(center, 1, column, iostreamtopview[i].title); - wattroff(center, A_UNDERLINE); - column += 12; - } - wattroff(center, A_BOLD); - wattroff(center, A_UNDERLINE); - - if (iostreamtopview[0].sort == 1) - g_ptr_array_sort(data->process_table, sort_by_process_read_desc); - else if (iostreamtopview[1].sort == 1) - g_ptr_array_sort(data->process_table, sort_by_process_write_desc); - else if (iostreamtopview[2].sort == 1) - g_ptr_array_sort(data->process_table, sort_by_process_total_desc); - else - g_ptr_array_sort(data->process_table, sort_by_process_total_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 (tmp->pid != tmp->tid) - if (toggle_threads == -1) - continue; - - if (process_selected(tmp)) { - wattron(center, COLOR_PAIR(6)); - mvwhline(center, current_line + header_offset, 1, ' ', COLS-3); - } - if (current_line == selected_line) { - selected_process = tmp; - wattron(center, COLOR_PAIR(5)); - mvwhline(center, current_line + header_offset, 1, ' ', COLS-3); - } - /* TGID */ - mvwprintw(center, current_line + header_offset, 1, "%d", tmp->pid); - /* PID */ - mvwprintw(center, current_line + header_offset, 11, "%d", tmp->tid); - /* NAME */ - mvwprintw(center, current_line + header_offset, 22, "%s", tmp->comm); - - /* READ (bytes/sec) */ - scale_unit(tmp->fileread, unit); - mvwprintw(center, current_line + header_offset, 40, "%s", unit); - - /* WRITE (bytes/sec) */ - scale_unit(tmp->filewrite, unit); - mvwprintw(center, current_line + header_offset, 52, "%s", unit); - - /* TOTAL STREAM */ - total = tmp->totalfileread + tmp->totalfilewrite; - - scale_unit(total, unit); - mvwprintw(center, current_line + header_offset, 64, "%s", unit); - - wattroff(center, COLOR_PAIR(6)); - 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 iostream: - update_iostream(); - break; - case tree: - update_cputop_display(); - break; - default: - break; - } - update_panels(); - doupdate(); - sem_post(&update_display_sem); -} - -void update_process_detail_sort(int *line_selected) -{ - int i; - int size; - - size = 3; - - if (*line_selected > (size - 1)) - *line_selected = size - 1; - else if (*line_selected < 0) - *line_selected = 0; - - if (fileview[*line_selected].sort == 1) - fileview[*line_selected].reverse = 1; - for (i = 0; i < size; i++) - fileview[i].sort = 0; - fileview[*line_selected].sort = 1; -} - -void update_process_detail_pref(int *line_selected, int toggle_view, int toggle_sort) -{ - int i; - int size; - - if (!data) - return; - if (pref_panel_window) { - del_panel(pref_panel); - delwin(pref_panel_window); - } - size = 3; - - pref_panel_window = create_window(size + 2, 30, 10, 10); - pref_panel = new_panel(pref_panel_window); - - werase(pref_panel_window); - box(pref_panel_window, 0 , 0); - set_window_title(pref_panel_window, "Process Detail Preferences "); - wattron(pref_panel_window, A_BOLD); - mvwprintw(pref_panel_window, size + 1, 1, - " 's' : sort, space : toggle"); - wattroff(pref_panel_window, A_BOLD); - - if (*line_selected > (size - 1)) - *line_selected = size - 1; - else if (*line_selected < 0) - *line_selected = 0; - if (toggle_sort == 1) { - update_process_detail_sort(line_selected); - update_current_view(); - } - - for (i = 0; i < size; i++) { - if (i == *line_selected) { - wattron(pref_panel_window, COLOR_PAIR(5)); - mvwhline(pref_panel_window, i + 1, 1, ' ', 30 - 2); - } - if (fileview[i].sort == 1) - wattron(pref_panel_window, A_BOLD); - mvwprintw(pref_panel_window, i + 1, 1, "[-] %s", - fileview[i].title); - wattroff(pref_panel_window, A_BOLD); - wattroff(pref_panel_window, COLOR_PAIR(5)); - - } - update_panels(); - doupdate(); -} - -void update_iostream_sort(int *line_selected) -{ - int i; - int size; - - size = 3; - if (*line_selected > (size - 1)) - *line_selected = size - 1; - else if (*line_selected < 0) - *line_selected = 0; - if (iostreamtopview[*line_selected].sort == 1) - iostreamtopview[*line_selected].reverse = 1; - for (i = 0; i < size; i++) - iostreamtopview[i].sort = 0; - iostreamtopview[*line_selected].sort = 1; - -} - -void update_iostream_pref(int *line_selected, int toggle_view, int toggle_sort) -{ - int i; - int size; - - if (!data) - return; - if (pref_panel_window) { - del_panel(pref_panel); - delwin(pref_panel_window); - } - size = 3; - - pref_panel_window = create_window(size + 2, 30, 10, 10); - pref_panel = new_panel(pref_panel_window); - - werase(pref_panel_window); - box(pref_panel_window, 0 , 0); - set_window_title(pref_panel_window, "IOTop Preferences "); - wattron(pref_panel_window, A_BOLD); - mvwprintw(pref_panel_window, size + 1, 1, - " 's' : sort, space : toggle"); - wattroff(pref_panel_window, A_BOLD); - - if (*line_selected > (size - 1)) - *line_selected = size - 1; - else if (*line_selected < 0) - *line_selected = 0; - if (toggle_sort == 1) { - update_iostream_sort(line_selected); - update_current_view(); - } - - for (i = 0; i < size; i++) { - if (i == *line_selected) { - wattron(pref_panel_window, COLOR_PAIR(5)); - mvwhline(pref_panel_window, i + 1, 1, ' ', 30 - 2); - } - if (iostreamtopview[i].sort == 1) - wattron(pref_panel_window, A_BOLD); - mvwprintw(pref_panel_window, i + 1, 1, "[-] %s", - iostreamtopview[i].title); - wattroff(pref_panel_window, A_BOLD); - wattroff(pref_panel_window, COLOR_PAIR(5)); - - } - update_panels(); - doupdate(); -} - -void update_cpu_sort(int *line_selected) -{ - int i; - int size = 3; - - if (*line_selected > (size - 1)) - *line_selected = size - 1; - else if (*line_selected < 0) - *line_selected = 0; - - /* special case, we don't support sorting by procname for now */ - if (*line_selected != 3) { - if (cputopview[*line_selected].sort == 1) - cputopview[*line_selected].reverse = 1; - for (i = 0; i < size; i++) - cputopview[i].sort = 0; - cputopview[*line_selected].sort = 1; - } -} - -void update_cpu_pref(int *line_selected, int toggle_view, int toggle_sort) -{ - int i; - int size; - - if (!data) - return; - if (pref_panel_window) { - del_panel(pref_panel); - delwin(pref_panel_window); - } - size = 4; - - pref_panel_window = create_window(size + 2, 30, 10, 10); - pref_panel = new_panel(pref_panel_window); - - werase(pref_panel_window); - box(pref_panel_window, 0 , 0); - set_window_title(pref_panel_window, "CPUTop Preferences "); - wattron(pref_panel_window, A_BOLD); - mvwprintw(pref_panel_window, size + 1, 1, - " 's' : sort, space : toggle"); - wattroff(pref_panel_window, A_BOLD); - - if (*line_selected > (size - 1)) - *line_selected = size - 1; - else if (*line_selected < 0) - *line_selected = 0; - if (toggle_sort == 1) { - update_cpu_sort(line_selected); - update_current_view(); - } - - for (i = 0; i < size; i++) { - if (i == *line_selected) { - wattron(pref_panel_window, COLOR_PAIR(5)); - mvwhline(pref_panel_window, i + 1, 1, ' ', 30 - 2); - } - if (cputopview[i].sort == 1) - wattron(pref_panel_window, A_BOLD); - mvwprintw(pref_panel_window, i + 1, 1, "[-] %s", - cputopview[i].title); - wattroff(pref_panel_window, A_BOLD); - wattroff(pref_panel_window, COLOR_PAIR(5)); - - } - update_panels(); - doupdate(); -} - -void update_perf_sort(int *line_selected) -{ - int i; - struct perfcounter *perf; - GList *perflist; - int size; - - size = g_hash_table_size(global_perf_liszt); - if (*line_selected > (size - 1)) - *line_selected = size - 1; - else if (*line_selected < 0) - *line_selected = 0; - - i = 0; - perflist = g_list_first(g_hash_table_get_keys(global_perf_liszt)); - while (perflist) { - perf = g_hash_table_lookup(global_perf_liszt, perflist->data); - if (i != *line_selected) - perf->sort = 0; - else - perf->sort = 1; - i++; - perflist = g_list_next(perflist); - } -} - -void update_perf_pref(int *line_selected, int toggle_view, int toggle_sort) -{ - int i; - struct perfcounter *perf; - GList *perflist; - int size; - - if (!data) - return; - if (pref_panel_window) { - del_panel(pref_panel); - delwin(pref_panel_window); - } - size = g_hash_table_size(global_perf_liszt); - - pref_panel_window = create_window(size + 2, 30, 10, 10); - pref_panel = new_panel(pref_panel_window); - - werase(pref_panel_window); - box(pref_panel_window, 0 , 0); - set_window_title(pref_panel_window, "Perf Preferences "); - wattron(pref_panel_window, A_BOLD); - mvwprintw(pref_panel_window, g_hash_table_size(global_perf_liszt) + 1, 1, - " 's' : sort, space : toggle"); - wattroff(pref_panel_window, A_BOLD); - - if (*line_selected > (size - 1)) - *line_selected = size - 1; - else if (*line_selected < 0) - *line_selected = 0; - - if (toggle_sort == 1) { - update_perf_sort(line_selected); - update_current_view(); - } - - i = 0; - perflist = g_list_first(g_hash_table_get_keys(global_perf_liszt)); - while (perflist) { - perf = g_hash_table_lookup(global_perf_liszt, perflist->data); - if (i == *line_selected && toggle_view == 1) { - perf->visible = perf->visible == 1 ? 0:1; - update_current_view(); - } - if (i == *line_selected) { - wattron(pref_panel_window, COLOR_PAIR(5)); - mvwhline(pref_panel_window, i + 1, 1, ' ', 30 - 2); - } - if (perf->sort == 1) - wattron(pref_panel_window, A_BOLD); - mvwprintw(pref_panel_window, i + 1, 1, "[%c] %s", - perf->visible == 1 ? 'x' : ' ', - (char *) perflist->data + 5); - wattroff(pref_panel_window, A_BOLD); - wattroff(pref_panel_window, COLOR_PAIR(5)); - i++; - perflist = g_list_next(perflist); - } - update_panels(); - doupdate(); -} - -int update_preference_panel(int *line_selected, int toggle_view, int toggle_sort) -{ - int ret = 0; - - switch(current_view) { - case perf: - update_perf_pref(line_selected, toggle_view, toggle_sort); - break; - case cpu: - update_cpu_pref(line_selected, toggle_view, toggle_sort); - break; - case iostream: - update_iostream_pref(line_selected, toggle_view, toggle_sort); - break; - case process_details: - update_process_detail_pref(line_selected, toggle_view, toggle_sort); - break; - default: - ret = -1; - break; - } - - return ret; -} - -int update_sort(int *line_selected) -{ - int ret = 0; - - switch(current_view) { - case perf: - update_perf_sort(line_selected); - break; - case cpu: - update_cpu_sort(line_selected); - break; - case iostream: - update_iostream_sort(line_selected); - break; - case process_details: - update_process_detail_sort(line_selected); - break; - default: - ret = -1; - break; - } - - return ret; -} - - -void toggle_pref_panel(void) -{ - int ret; - - if (pref_panel_visible) { - hide_panel(pref_panel); - pref_panel_visible = 0; - } else { - ret = update_preference_panel(&pref_line_selected, 0, 0); - if (ret < 0) - return; - show_panel(pref_panel); - pref_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 'j': - case KEY_DOWN: - if (pref_panel_visible) { - pref_line_selected++; - update_preference_panel(&pref_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: - break; - case 'k': - case KEY_UP: - if (pref_panel_visible) { - if (pref_line_selected > 0) - pref_line_selected--; - update_preference_panel(&pref_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: - 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); - if (toggle_pause > 0) { - sem_post(&pause_sem); - update_current_view(); - sem_wait(&pause_sem); - } - } - - break; - case ' ': - if (pref_panel_visible) { - update_preference_panel(&pref_line_selected, 1, 0); - } else { - update_selected_processes(); - update_current_view(); - } - break; - case 's': - if (pref_panel_visible) - update_preference_panel(&pref_line_selected, 0, 1); - break; - case '>': - /* perf uses a hashtable, it is ordered backward */ - if (current_view == perf) { - pref_current_sort--; - } else if (!pref_panel_visible) { - pref_current_sort++; - } - update_sort(&pref_current_sort); - update_current_view(); - break; - case '<': - /* perf uses a hashtable, it is ordered backward */ - if (current_view == perf) { - pref_current_sort++; - } else if (!pref_panel_visible) { - pref_current_sort--; - } - update_sort(&pref_current_sort); - update_current_view(); - break; - - case 13: /* FIXME : KEY_ENTER ?? */ - if (pref_panel_visible) - break; - if (current_view != process_details) { - previous_view = current_view; - current_view = process_details; - } else { - current_view = previous_view; - previous_view = process_details; - } - update_current_view(); - break; - - case KEY_F(1): - if (pref_panel_visible) - toggle_pref_panel(); - current_view = cpu; - selected_line = 0; - update_current_view(); - break; - case KEY_F(2): - if (pref_panel_visible) - toggle_pref_panel(); - current_view = cpu; - selected_line = 0; - update_current_view(); - break; - case KEY_F(3): - if (pref_panel_visible) - toggle_pref_panel(); - current_view = perf; - selected_line = 0; - update_current_view(); - break; - case KEY_F(4): - if (pref_panel_visible) - toggle_pref_panel(); - current_view = iostream; - selected_line = 0; - 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 'r': - toggle_pref_panel(); - break; - /* ESCAPE, but slow to process, don't know why */ - case 27: - if (pref_panel_visible) - toggle_pref_panel(); - else if (current_view == process_details) { - current_view = previous_view; - previous_view = process_details; - } - update_current_view(); - break; - default: - if (data) - update_current_view(); - break; - } - update_footer(); - } - return NULL; -} - -void init_view_headers() -{ - cputopview[0].title = strdup("CPU(%)"); - cputopview[0].sort = 1; - cputopview[1].title = strdup("PID"); - cputopview[2].title = strdup("TID"); - cputopview[3].title = strdup("NAME"); - - iostreamtopview[0].title = strdup("R (B/sec)"); - iostreamtopview[1].title = strdup("W (B/sec)"); - iostreamtopview[2].title = strdup("Total (B)"); - iostreamtopview[2].sort = 1; - - fileview[0].title = strdup("FD"); - fileview[1].title = strdup("READ"); - fileview[1].sort = 1; - fileview[2].title = strdup("WRITE"); -} - -void init_ncurses() -{ - selected_processes = g_ptr_array_new(); - sem_init(&update_display_sem, 0, 1); - init_view_headers(); - init_screen(); - - header = create_window(5, COLS - 1, 0, 0); - center = create_window(LINES - 5 - 7, COLS - 1, 5, 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); - - current_view = cpu; - - basic_header(); - update_footer(); - - pthread_create(&keyboard_thread, NULL, handle_keyboard, (void *)NULL); -} diff --git a/src/cursesdisplay.cpp b/src/cursesdisplay.cpp new file mode 100644 index 0000000..3cd273f --- /dev/null +++ b/src/cursesdisplay.cpp @@ -0,0 +1,1797 @@ +/* + * Copyright (C) 2011-2012 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define __STDC_FORMAT_MACROS +#include + +#include "cursesdisplay.h" +#include "lttngtoptypes.h" +#include "iostreamtop.h" +#include "common.h" + +#define DEFAULT_DELAY 15 +#define MAX_LINE_LENGTH 50 +#define MAX_LOG_LINES 4 + +using namespace State; + +enum view_list current_view; +enum view_list previous_view; + +/* to prevent concurrent updates of the different windows */ +sem_t update_display_sem; + +char *termtype; +WINDOW *footer, *header, *center, *status; +WINDOW *pref_panel_window = NULL; +PANEL *pref_panel, *main_panel; + +int pref_panel_visible = 0; +int pref_line_selected = 0; +int pref_current_sort = 0; + +Quark selected_process; +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 max_center_lines; +GPtrArray *selected_processes; + +pthread_t keyboard_thread; + +struct header_view cputopview[4]; +struct header_view iostreamtopview[3]; +struct header_view fileview[3]; + +unsigned long i_start = 0; +unsigned long i_end = 0; + +std::string sort_perf_key; + + +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 */ + init_pair(6, COLOR_WHITE, COLOR_GREEN); /* selected process */ + } + 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, const 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, "%d", value); + wmove(header, line, 24); + print_digits(header, first, second); + wmove(header, line, 40); +} + +void set_window_title(WINDOW *win, const char title[]) +{ + wattron(win, A_BOLD); + mvwprintw(win, 0, 1, title); + wattroff(win, A_BOLD); +} + +void print_log(const char str[]) +{ + unsigned 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); +} + +int process_selected(Quark proc) +{ + int selected; + get_current_attribute_value_int(&proc, "selected", selected); + return selected; +} + +void update_selected_processes() +{ + Quark selected = state_system->getQuark(selected_process, "selected"); + if (process_selected(selected_process)) { + state_system->updateCurrentState(selected, 0); + print_log("Process removed"); + } else { + state_system->updateCurrentState(selected, 1); + print_log("Process added"); + } +} + +void print_key(WINDOW *win, const char key[], const 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, "F4", "IOTop ", current_view == iostream); + print_key(footer, "Enter", "Details ", current_view == process_details); + print_key(footer, "Space", "Highlight ", 0); + print_key(footer, "q", "Quit ", 0); + print_key(footer, "r", "Pref ", 0); + print_key(footer, "t", "Threads ", toggle_threads); + 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, "Threads"); + mvwprintw(header, 3, 4, "FDs"); + wattroff(header, A_BOLD); + wrefresh(header); +} + +struct tm format_timestamp(uint64_t timestamp) +{ + struct tm tm; + uint64_t ts_sec = 0, ts_nsec; + time_t time_s; + + ts_nsec = timestamp; + ts_sec += ts_nsec / NSEC_PER_SEC; + ts_nsec = ts_nsec % NSEC_PER_SEC; + + time_s = (time_t) ts_sec; + + localtime_r(&time_s, &tm); + + return tm; +} + +static void scale_unit(uint64_t bytes, char *ret) +{ + if (bytes >= 1000000000) + sprintf(ret, "%" PRIu64 "G", bytes/1000000000); + if (bytes >= 1000000) + sprintf(ret, "%" PRIu64 "M", bytes/1000000); + else if (bytes >= 1000) + sprintf(ret, "%" PRIu64 "K", bytes/1000); + else + sprintf(ret, "%" PRIu64, bytes); +} + +uint64_t total_io() +{ + uint64_t total = 0; + int read, write; + unsigned long birth; + unsigned long real_start; + Quark proc; + + if (get_attribute_value_at_quark(i_end, NULL, "proc", proc)) { + do { + get_attribute_value_at_ulong( + i_end, &proc, "birth", birth); + if (birth > i_start) + real_start = birth; + else + real_start = i_start; + get_interval_value_int(real_start, i_end, &proc, + "fileread", read); + get_interval_value_int(real_start, i_end, &proc, + "filewrite", write); + total += read + write; + + } while (get_attribute_value_at_quark( + i_end, &proc, "next", proc)); + } + + return total; +} + +void update_header() +{ + struct tm start, end; + uint64_t ts_nsec_start, ts_nsec_end; + char io[16]; + int cpu_nb; + int nbthreads, nbnewthreads, nbdeadthreads; + int nbfiles, nbnewfiles, nbdeadfiles; + + ts_nsec_start = i_start % NSEC_PER_SEC; + start = format_timestamp(i_start); + + ts_nsec_end = i_end % NSEC_PER_SEC; + end = format_timestamp(i_end); + + werase(header); + box(header, 0 , 0); + set_window_title(header, "Statistics for interval "); + wattron(header, A_BOLD); + + wprintw(header, + "[%02d:%02d:%02d.%09" PRIu64 ", %02d:%02d:%02d.%09" PRIu64 "[", + start.tm_hour, start.tm_min, start.tm_sec, ts_nsec_start, + end.tm_hour, end.tm_min, end.tm_sec, ts_nsec_end); + mvwprintw(header, 1, 4, "CPUs"); + wattroff(header, A_BOLD); + + cpu_nb = get_sequence_length(i_end, state_system->getQuark("cpu")); + get_attribute_value_at_int(i_end, NULL, "nbthreads", nbthreads); + get_interval_value_int(i_start, i_end, NULL, "nbnewthreads", + nbnewthreads); + get_interval_value_int(i_start, i_end, NULL, "nbdeadthreads", + nbdeadthreads); + get_attribute_value_at_int(i_end, NULL, "nbfiles", nbfiles); + get_interval_value_int(i_start, i_end, NULL, "nbnewfiles", nbnewfiles); + get_interval_value_int(i_start, i_end, NULL, "nbdeadfiles", + nbdeadfiles); + + wprintw(header, "\t%d\t(max/cpu : %0.2f%)", cpu_nb, + 100.0/cpu_nb); + print_headers(2, "Threads", nbthreads, nbnewthreads,-1*nbdeadthreads); + print_headers(3, "FDs", nbfiles, nbnewfiles,-1*nbdeadfiles); + scale_unit(total_io(), io); + mvwprintw(header, 3, 43, "%sB/sec", io); + wrefresh(header); +} + +int sort_by_cpu_desc(const void *pp1, const void *pp2) +{ + Quark p1 = *(Quark *)pp1; + Quark p2 = *(Quark *)pp2; + unsigned long total1, total2; + unsigned long birth1, birth2; + unsigned long start1 = i_start; + unsigned long start2 = i_start; + + get_attribute_value_at_ulong(i_end, &p1, "birth", birth1); + get_attribute_value_at_ulong(i_end, &p2, "birth", birth2); + if (birth1 > i_start) + start1 = birth1; + if (birth2 > i_start) + start2 = birth2; + + get_interval_value_ulong(start1, i_end, &p1, "totalcpunsec", total1); + get_interval_value_ulong(start2, i_end, &p2, "totalcpunsec", total2); + + if (total1 > total2) + return 1; + if (total1 == total2) + return 0; + return -1; +} + +int sort_by_tid_desc(const void *pp1, const void *pp2) +{ + Quark p1 = *(Quark *)pp1; + Quark p2 = *(Quark *)pp2; + int tid1, tid2; + + get_attribute_value_at_int(i_end, &p1, "tid", tid1); + get_attribute_value_at_int(i_end, &p2, "tid", tid2); + + if (tid1 > tid2) + return 1; + if (tid1 == tid2) + return 0; + return -1; +} + +int sort_by_pid_desc(const void *pp1, const void *pp2) +{ + Quark p1 = *(Quark *)pp1; + Quark p2 = *(Quark *)pp2; + int pid1, pid2; + + get_attribute_value_at_int(i_end, &p1, "pid", pid1); + get_attribute_value_at_int(i_end, &p2, "pid", pid2); + + if (pid1 > pid2) + return 1; + if (pid1 == pid2) + return 0; + return -1; +} + + +int sort_by_process_read_desc(const void *pp1, const void *pp2) +{ + Quark p1 = *(Quark *)pp1; + Quark p2 = *(Quark *)pp2; + int total1, total2; + unsigned long birth1, birth2; + unsigned long start1 = i_start; + unsigned long start2 = i_start; + + get_attribute_value_at_ulong(i_end, &p1, "birth", birth1); + get_attribute_value_at_ulong(i_end, &p2, "birth", birth2); + if (birth1 > i_start) + start1 = birth1; + if (birth2 > i_start) + start2 = birth2; + + get_interval_value_int(start1, i_end, &p1, "fileread", total1); + get_interval_value_int(start2, i_end, &p2, "fileread", total2); + + if (total1 > total2) + return 1; + if (total1 == total2) + return 0; + return -1; +} + +int sort_by_process_write_desc(const void *pp1, const void *pp2) +{ + Quark p1 = *(Quark *)pp1; + Quark p2 = *(Quark *)pp2; + int total1, total2; + unsigned long birth1, birth2; + unsigned long start1 = i_start; + unsigned long start2 = i_start; + + get_attribute_value_at_ulong(i_end, &p1, "birth", birth1); + get_attribute_value_at_ulong(i_end, &p2, "birth", birth2); + if (birth1 > i_start) + start1 = birth1; + if (birth2 > i_start) + start2 = birth2; + + get_interval_value_int(start1, i_end, &p1, "filewrite", total1); + get_interval_value_int(start2, i_end, &p2, "filewrite", total2); + + if (total1 > total2) + return 1; + if (total1 == total2) + return 0; + return -1; +} + +int sort_by_process_total_desc(const void *pp1, const void *pp2) +{ + Quark p1 = *(Quark *)pp1; + Quark p2 = *(Quark *)pp2; + int p1Read, p2Read; + int p1Write, p2Write; + int total1, total2; + unsigned long birth1, birth2; + unsigned long start1 = i_start; + unsigned long start2 = i_start; + + get_attribute_value_at_ulong(i_end, &p1, "birth", birth1); + get_attribute_value_at_ulong(i_end, &p2, "birth", birth2); + if (birth1 > i_start) + start1 = birth1; + if (birth2 > i_start) + start2 = birth2; + + get_interval_value_int(start1, i_end, &p1, "fileread", p1Read); + get_interval_value_int(start2, i_end, &p2, "fileread", p2Read); + get_interval_value_int(start1, i_end, &p1, "filewrite", p1Write); + get_interval_value_int(start2, i_end, &p2, "filewrite", p2Write); + + total1 = p1Read + p1Write; + total2 = p2Read + p2Write; + + if (total1 > total2) + return 1; + if (total1 == total2) + return 0; + return -1; +} + +int sort_by_file_read_desc(const void *pf1, const void *pf2) +{ + Quark f1 = *(Quark *)pf1; + Quark f2 = *(Quark *)pf2; + int total1, total2; + unsigned long birth1, birth2; + unsigned long start1 = i_start; + unsigned long start2 = i_start; + + get_attribute_value_at_ulong(i_end, &f1, "file/birth", birth1); + get_attribute_value_at_ulong(i_end, &f2, "file/birth", birth2); + if (birth1 > i_start) + start1 = birth1; + if (birth2 > i_start) + start2 = birth2; + + get_interval_value_int(start1, i_end, &f1, "file/read", total1); + get_interval_value_int(start2, i_end, &f2, "file/read", total2); + + if (total1 > total2) + return 1; + if (total1 == total2) + return 0; + return -1; +} + +int sort_by_file_write_desc(const void *pf1, const void *pf2) +{ + Quark f1 = *(Quark *)pf1; + Quark f2 = *(Quark *)pf2; + int total1, total2; + unsigned long birth1, birth2; + unsigned long start1 = i_start; + unsigned long start2 = i_start; + + get_attribute_value_at_ulong(i_end, &f1, "file/birth", birth1); + get_attribute_value_at_ulong(i_end, &f2, "file/birth", birth2); + if (birth1 > i_start) + start1 = birth1; + if (birth2 > i_start) + start2 = birth2; + + get_interval_value_int(start1, i_end, &f1, "file/write", total1); + get_interval_value_int(start2, i_end, &f2, "file/write", total2); + + if (total1 > total2) + return 1; + if (total1 == total2) + return 0; + return -1; +} + +int sort_by_file_fd_desc(const void *pf1, const void *pf2) +{ + Quark f1 = *(Quark *)pf1; + Quark f2 = *(Quark *)pf2; + int fd1, fd2; + + get_attribute_value_at_int(i_end, &f1, "file/fd", fd1); + get_attribute_value_at_int(i_end, &f2, "file/fd", fd2); + + if (fd1 > fd2) + return 1; + if (fd1 == fd2) + return 0; + return -1; +} + +int sort_by_cpu_group_by_threads_desc(const void *pp1, const void *pp2) +{ + Quark p1 = *(Quark *)pp1; + Quark p2 = *(Quark *)pp2; + unsigned long total1, total2; + unsigned long birth1, birth2; + unsigned long start1 = i_start; + unsigned long start2 = i_start; + + get_attribute_value_at_ulong(i_end, &p1, "birth", birth1); + get_attribute_value_at_ulong(i_end, &p2, "birth", birth2); + if (birth1 > i_start) + start1 = birth1; + if (birth2 > i_start) + start2 = birth2; + + get_interval_value_ulong(start1, i_end, &p1, "threadstotalcpunsec", + total1); + get_interval_value_ulong(start2, i_end, &p2, "threadstotalcpunsec", + total2); + + if (total1 > total2) + return 1; + if (total1 == total2) + return 0; + return -1; +} + +void update_cputop_display() +{ + unsigned int i; + int header_offset = 2; + unsigned long elapsed; + double maxcputime; + int nblinedisplayed = 0; + int current_line = 0; + int column; + Quark *processes; + int tid, pid; + unsigned long totalcpunsec; + std::string comm; + + Quark cpu_root = state_system->getQuark("cpu"); + Quark proc_root = state_system->getQuark("proc"); + unsigned int cpu_nb = get_sequence_length(i_end, cpu_root); + unsigned int proc_nb = get_sequence_length(i_end, proc_root); + + elapsed = i_end - i_start; + maxcputime = elapsed * cpu_nb / 100.0; + + processes = g_new(Quark, proc_nb); + sequence_to_array(i_end, proc_root, processes, proc_nb); + + if (cputopview[0].sort == 1) + qsort(processes, proc_nb, sizeof(Quark), sort_by_cpu_desc); + else if (cputopview[1].sort == 1) + qsort(processes, proc_nb, sizeof(Quark), sort_by_pid_desc); + else if (cputopview[2].sort == 1) + qsort(processes, proc_nb, sizeof(Quark), sort_by_tid_desc); + else if (cputopview[3].sort == 1) + qsort(processes, proc_nb, sizeof(Quark), sort_by_cpu_desc); + else + qsort(processes, proc_nb, sizeof(Quark), sort_by_cpu_desc); + + set_window_title(center, "CPU Top"); + wattron(center, A_BOLD); + column = 1; + for (i = 0; i < 4; i++) { + if (cputopview[i].sort) { + wattron(center, A_UNDERLINE); + pref_current_sort = i; + } + mvwprintw(center, 1, column, cputopview[i].title); + wattroff(center, A_UNDERLINE); + column += 10; + } + wattroff(center, A_BOLD); + + max_center_lines = LINES - 5 - 7 - 1 - header_offset; + + /* iterate the process (thread) list */ + for (i = list_offset; i < proc_nb && nblinedisplayed < max_center_lines; + i++) { + get_attribute_value_at_int(i_end, processes + i, "tid", tid); + get_attribute_value_at_int(i_end, processes + i, "pid", pid); + + if (pid != tid) + if (toggle_threads == -1) + continue; + + if (process_selected(processes[i])) { + wattron(center, COLOR_PAIR(6)); + mvwhline(center, current_line + header_offset, 1, ' ', + COLS-3); + } + if (current_line == selected_line) { + selected_process = processes[i]; + wattron(center, COLOR_PAIR(5)); + mvwhline(center, current_line + header_offset, 1, ' ', + COLS-3); + } + /* CPU(%) */ + get_interval_value_ulong(i_start, i_end, processes + i, + "totalcpunsec", totalcpunsec); + mvwprintw(center, current_line + header_offset, 1, "%1.2f", + totalcpunsec / maxcputime); + /* PID */ + mvwprintw(center, current_line + header_offset, 11, "%d", pid); + /* TID */ + mvwprintw(center, current_line + header_offset, 21, "%d", tid); + /* NAME */ + get_attribute_value_at_string(i_end, processes + i, "comm", + comm); + mvwprintw(center, current_line + header_offset, 31, "%s", + comm.c_str()); + wattroff(center, COLOR_PAIR(6)); + wattroff(center, COLOR_PAIR(5)); + nblinedisplayed++; + current_line++; + } + + g_free(processes); +} + +int sort_perf(const void *pp1, const void *pp2) +{ + Quark p1 = *(Quark *)pp1; + Quark p2 = *(Quark *)pp2; + std::string key = sort_perf_key; + int total1; + int total2; + int tid1; + int tid2; + + if (!get_attribute_value_at_int(i_end, &p1, "perf/" + key + "/count", + total1)) + total1 = 0; + + if (!get_attribute_value_at_int(i_end, &p2, "perf/" + key + "/count", + total2)) + total2 = 0; + + + if (total1 < total2) + return 1; + if (total1 == total2) { + get_attribute_value_at_int(i_end, &p1, "tid", tid1); + get_attribute_value_at_int(i_end, &p2, "tid", tid2); + if (tid1 < tid2) + return 1; + return -1; + } + return -1; +} + +void print_key_title(const char key[], int line) +{ + wattron(center, A_BOLD); + mvwprintw(center, line, 1, "%s", key); + mvwprintw(center, line, 30, " "); + wattroff(center, A_BOLD); +} + +void update_process_details() +{ + unsigned long elapsed; + double maxcputime; + unsigned int i, j = 0; + char unit[4]; + char filename_buf[COLS]; + int line = 1; + int column; + unsigned int cpu_nb; + std::string comm; + int tid = -1, pid, ppid, fileread, filewrite; + unsigned long totalcpunsec; + Quark *global_perf; + unsigned int global_perf_nb; + std::string key; + int count; + Quark *files; + unsigned int file_nb; + int fd, read, write; + std::string name; + + set_window_title(center, "Process details"); + + elapsed = i_end - i_start; + cpu_nb = get_sequence_length(i_end, state_system->getQuark("cpu")); + maxcputime = elapsed * cpu_nb / 100.0; + + get_attribute_value_at_int(i_end, &selected_process, "tid", tid); + if (tid == -1) { + print_key_title("Does not exit at this time", 3); + return; + } + + print_key_title("Name", line++); + get_attribute_value_at_string(i_end, &selected_process, "comm", comm); + wprintw(center, "%s", comm.c_str()); + print_key_title("TID", line++); + wprintw(center, "%d", tid); + + print_key_title("PID", line++); + get_attribute_value_at_int(i_end, &selected_process, "pid", pid); + wprintw(center, "%d", pid); + print_key_title("PPID", line++); + get_attribute_value_at_int(i_end, &selected_process, "ppid", ppid); + wprintw(center, "%d", ppid); + print_key_title("CPU", line++); + get_attribute_value_at_ulong(i_end, &selected_process, "totalcpunsec", + totalcpunsec); + wprintw(center, "%1.2f %%", totalcpunsec/maxcputime); + + print_key_title("READ B/s", line++); + get_attribute_value_at_int(i_end, &selected_process, "fileread", + fileread); + scale_unit(fileread, unit); + wprintw(center, "%s", unit); + + print_key_title("WRITE B/s", line++); + get_attribute_value_at_int(i_end, &selected_process, "filewrite", + filewrite); + scale_unit(filewrite, unit); + wprintw(center, "%s", unit); + + + global_perf_nb = get_global_perf_list_size(i_end); + global_perf = g_new(Quark, global_perf_nb); + get_global_perf_list(i_end, global_perf, global_perf_nb); + for (i = 0; i < global_perf_nb; i++) { + get_attribute_value_at_string(i_end, global_perf + i, "key", + key); + print_key_title(key.c_str(), line++); + if (!get_attribute_value_at_int(i_end, global_perf + i, "count", + count)) + count = 0; + wprintw(center, "%d", count); + } + line++; + + wattron(center, A_BOLD); + column = 1; + for (i = 0; i < 3; i++) { + if (fileview[i].sort) { + pref_current_sort = i; + wattron(center, A_UNDERLINE); + } + mvwprintw(center, line, column, fileview[i].title); + wattroff(center, A_UNDERLINE); + column += 10; + } + mvwprintw(center, line++, column, "FILENAME"); + wattroff(center, A_BOLD); + + file_nb = get_number_of_opened_files(i_end, selected_process); + files = g_new(Quark, file_nb); + get_opened_files(i_end, selected_process, files, file_nb); + + if (fileview[0].sort == 1) + qsort(files, file_nb, sizeof(Quark), sort_by_file_fd_desc); + else if (fileview[1].sort == 1) + qsort(files, file_nb, sizeof(Quark), sort_by_file_read_desc); + else if (fileview[2].sort == 1) + qsort(files, file_nb, sizeof(Quark), sort_by_file_write_desc); + else + qsort(files, file_nb, sizeof(Quark), sort_by_file_fd_desc); + + for (i = selected_line; + i < file_nb && i < (selected_line + max_center_lines - line + 2); + i++) { + get_attribute_value_at_int(i_end, files + i, "fd", fd); + mvwprintw(center, line + j, 1, "%d", fd); + get_attribute_value_at_int(i_end, files + i, "read", read); + scale_unit(read, unit); + mvwprintw(center, line + j, 11, "%s", unit); + get_attribute_value_at_int(i_end, files + i, "write", write); + scale_unit(write, unit); + mvwprintw(center, line + j, 21, "%s", unit); + get_attribute_value_at_string(i_end, files + i, "name", name); + snprintf(filename_buf, COLS - 25, "%s", name.c_str()); + mvwprintw(center, line + j, 31, "%s", filename_buf); + j++; + } + g_free(global_perf); + g_free(files); +} + +void update_perf() +{ + unsigned int i, j; + int nblinedisplayed = 0; + int current_line = 0; + int header_offset = 2; + int perf_row = 40; + int value; + unsigned int global_perf_nb; + Quark *global_perf; + int sort = 0; + int visible = 0; + std::string key; + bool sort_exists; + unsigned int proc_nb; + Quark *procs; + Quark proc_beg; + int tid, pid; + std::string comm; + + 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; + global_perf_nb = get_global_perf_list_size(i_end); + global_perf = g_new(Quark, global_perf_nb); + get_global_perf_list(i_end, global_perf, global_perf_nb); + for (i = 0; i < global_perf_nb; i++) { + get_attribute_value_at_int(i_end, global_perf + i, "visible", + visible); + get_attribute_value_at_string(i_end, global_perf + i, "key", + key); + sort_exists = get_attribute_value_at_int(i_end, global_perf + i, + "sort", sort); + if (visible) { + if (sort_exists && sort) { + wattron(center, A_UNDERLINE); + } + /* + 5 to strip the "perf_" prefix */ + mvwprintw(center, 1, perf_row, "%s", + key.c_str() + 5); + wattroff(center, A_UNDERLINE); + perf_row += 20; + } + if (sort_exists && sort) { + sort_perf_key = key; + } + } + wattroff(center, A_BOLD); + + proc_beg = state_system->getQuark("proc"); + proc_nb = get_sequence_length(i_end, proc_beg); + procs = g_new(Quark, proc_nb); + sequence_to_array(i_end, proc_beg, procs, proc_nb); + for (i = 0; i < proc_nb && nblinedisplayed < max_center_lines; i++) { + get_attribute_value_at_int(i_end, procs + i, "tid", tid); + get_attribute_value_at_int(i_end, procs + i, "pid", pid); + get_attribute_value_at_string(i_end, procs + 1, "comm", comm); + + if (pid != tid) + if (toggle_threads == -1) + continue; + + if (process_selected(procs[i])) { + wattron(center, COLOR_PAIR(6)); + mvwhline(center, current_line + header_offset, 1, ' ', + COLS-3); + } + if (current_line == selected_line) { + selected_process = procs[i]; + wattron(center, COLOR_PAIR(5)); + mvwhline(center, current_line + header_offset, 1, ' ', + COLS-3); + } + + mvwprintw(center, current_line + header_offset, 1, "%d", pid); + mvwprintw(center, current_line + header_offset, 11, "%d", tid); + mvwprintw(center, current_line + header_offset, 22, "%s", + comm.c_str()); + + perf_row = 40; + for (j = 0; j < global_perf_nb; j++) { + get_attribute_value_at_int(i_end, global_perf + j, + "visible", visible); + get_attribute_value_at_string(i_end, global_perf + j, + "key", key); + + if (visible) { + if (!get_interval_value_int(i_start, i_end, + procs + i, "perf/" + + key + "/count", + value)) { + value = 0; + } + mvwprintw(center, current_line + header_offset, + perf_row, "%d", value); + perf_row += 20; + } + } + + wattroff(center, COLOR_PAIR(6)); + wattroff(center, COLOR_PAIR(5)); + nblinedisplayed++; + current_line++; + } + g_free(global_perf); + g_free(procs); +} + +void update_iostream() +{ + unsigned int i; + int header_offset = 2; + int nblinedisplayed = 0; + int current_line = 0; + char unit[4]; + int column; + unsigned int proc_nb; + Quark *procs; + Quark proc_root; + int pid, tid, read, write, totalread, totalwrite; + std::string comm; + + set_window_title(center, "IO Top"); + wattron(center, A_BOLD); + mvwprintw(center, 1, 1, "PID"); + mvwprintw(center, 1, 11, "TID"); + mvwprintw(center, 1, 22, "NAME"); + column = 40; + for (i = 0; i < 3; i++) { + if (iostreamtopview[i].sort) { + pref_current_sort = i; + wattron(center, A_UNDERLINE); + } + mvwprintw(center, 1, column, iostreamtopview[i].title); + wattroff(center, A_UNDERLINE); + column += 12; + } + wattroff(center, A_BOLD); + wattroff(center, A_UNDERLINE); + + proc_root = state_system->getQuark("proc"); + proc_nb = get_sequence_length(i_end, proc_root); + procs = g_new(Quark, proc_nb); + sequence_to_array(i_end, proc_root, procs, proc_nb); + + if (iostreamtopview[0].sort == 1) + qsort(procs, proc_nb, sizeof(Quark), sort_by_process_read_desc); + else if (iostreamtopview[1].sort == 1) + qsort(procs, proc_nb, sizeof(Quark), + sort_by_process_write_desc); + else if (iostreamtopview[2].sort == 1) + qsort(procs, proc_nb, sizeof(Quark), + sort_by_process_total_desc); + else + qsort(procs, proc_nb, sizeof(Quark), + sort_by_process_total_desc); + + for (i = list_offset; i < proc_nb && nblinedisplayed < max_center_lines; + i++) { + get_attribute_value_at_int(i_end, procs + i, "tid", tid); + get_attribute_value_at_int(i_end, procs + i, "pid", pid); + get_attribute_value_at_string(i_end, procs + i, "comm", comm); + get_attribute_value_at_int(i_end, procs + i, "fileread", + read); + get_attribute_value_at_int(i_end, procs + i, "filewrite", + write); + get_attribute_value_at_int(i_end, procs + i, "totalfileread", + totalread); + get_attribute_value_at_int(i_end, procs + i, "totalfilewrite", + totalwrite); + + + if (pid != tid) + if (toggle_threads == -1) + continue; + + if (process_selected(procs[i])) { + wattron(center, COLOR_PAIR(6)); + mvwhline(center, current_line + header_offset, 1, ' ', + COLS-3); + } + if (current_line == selected_line) { + selected_process = procs[i]; + wattron(center, COLOR_PAIR(5)); + mvwhline(center, current_line + header_offset, 1, ' ', + COLS-3); + } + /* PID */ + mvwprintw(center, current_line + header_offset, 1, "%d", pid); + /* TID */ + mvwprintw(center, current_line + header_offset, 11, "%d", tid); + /* NAME */ + mvwprintw(center, current_line + header_offset, 22, "%s", + comm.c_str()); + + /* READ (bytes/sec) */ + scale_unit(read, unit); + mvwprintw(center, current_line + header_offset, 40, "%s", unit); + + /* WRITE (bytes/sec) */ + scale_unit(write, unit); + mvwprintw(center, current_line + header_offset, 52, "%s", unit); + + /* TOTAL STREAM */ + scale_unit(totalread + totalwrite, unit); + mvwprintw(center, current_line + header_offset, 64, "%s", unit); + + wattroff(center, COLOR_PAIR(6)); + wattroff(center, COLOR_PAIR(5)); + nblinedisplayed++; + current_line++; + } + g_free(procs); +} + +void update_current_view() +{ + sem_wait(&update_display_sem); + 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 iostream: + update_iostream(); + break; + case tree: + update_cputop_display(); + break; + default: + break; + } + update_panels(); + doupdate(); + sem_post(&update_display_sem); +} + +void update_process_detail_sort(int *line_selected) +{ + int i; + int size; + + size = 3; + + if (*line_selected > (size - 1)) + *line_selected = size - 1; + else if (*line_selected < 0) + *line_selected = 0; + + if (fileview[*line_selected].sort == 1) + fileview[*line_selected].reverse = 1; + for (i = 0; i < size; i++) + fileview[i].sort = 0; + fileview[*line_selected].sort = 1; +} + +void update_process_detail_pref(int *line_selected, int toggle_view, int toggle_sort) +{ + int i; + int size; + + if (pref_panel_window) { + del_panel(pref_panel); + delwin(pref_panel_window); + } + size = 3; + + pref_panel_window = create_window(size + 2, 30, 10, 10); + pref_panel = new_panel(pref_panel_window); + + werase(pref_panel_window); + box(pref_panel_window, 0 , 0); + set_window_title(pref_panel_window, "Process Detail Preferences "); + wattron(pref_panel_window, A_BOLD); + mvwprintw(pref_panel_window, size + 1, 1, + " 's' : sort, space : toggle"); + wattroff(pref_panel_window, A_BOLD); + + if (*line_selected > (size - 1)) + *line_selected = size - 1; + else if (*line_selected < 0) + *line_selected = 0; + if (toggle_sort == 1) { + update_process_detail_sort(line_selected); + update_current_view(); + } + + for (i = 0; i < size; i++) { + if (i == *line_selected) { + wattron(pref_panel_window, COLOR_PAIR(5)); + mvwhline(pref_panel_window, i + 1, 1, ' ', 30 - 2); + } + if (fileview[i].sort == 1) + wattron(pref_panel_window, A_BOLD); + mvwprintw(pref_panel_window, i + 1, 1, "[-] %s", + fileview[i].title); + wattroff(pref_panel_window, A_BOLD); + wattroff(pref_panel_window, COLOR_PAIR(5)); + + } + update_panels(); + doupdate(); +} + +void update_iostream_sort(int *line_selected) +{ + int i; + int size; + + size = 3; + if (*line_selected > (size - 1)) + *line_selected = size - 1; + else if (*line_selected < 0) + *line_selected = 0; + if (iostreamtopview[*line_selected].sort == 1) + iostreamtopview[*line_selected].reverse = 1; + for (i = 0; i < size; i++) + iostreamtopview[i].sort = 0; + iostreamtopview[*line_selected].sort = 1; + +} + +void update_iostream_pref(int *line_selected, int toggle_view, int toggle_sort) +{ + int i; + int size; + + if (pref_panel_window) { + del_panel(pref_panel); + delwin(pref_panel_window); + } + size = 3; + + pref_panel_window = create_window(size + 2, 30, 10, 10); + pref_panel = new_panel(pref_panel_window); + + werase(pref_panel_window); + box(pref_panel_window, 0 , 0); + set_window_title(pref_panel_window, "IOTop Preferences "); + wattron(pref_panel_window, A_BOLD); + mvwprintw(pref_panel_window, size + 1, 1, + " 's' : sort, space : toggle"); + wattroff(pref_panel_window, A_BOLD); + + if (*line_selected > (size - 1)) + *line_selected = size - 1; + else if (*line_selected < 0) + *line_selected = 0; + if (toggle_sort == 1) { + update_iostream_sort(line_selected); + update_current_view(); + } + + for (i = 0; i < size; i++) { + if (i == *line_selected) { + wattron(pref_panel_window, COLOR_PAIR(5)); + mvwhline(pref_panel_window, i + 1, 1, ' ', 30 - 2); + } + if (iostreamtopview[i].sort == 1) + wattron(pref_panel_window, A_BOLD); + mvwprintw(pref_panel_window, i + 1, 1, "[-] %s", + iostreamtopview[i].title); + wattroff(pref_panel_window, A_BOLD); + wattroff(pref_panel_window, COLOR_PAIR(5)); + + } + update_panels(); + doupdate(); +} + +void update_cpu_sort(int *line_selected) +{ + int i; + int size = 3; + + if (*line_selected > (size - 1)) + *line_selected = size - 1; + else if (*line_selected < 0) + *line_selected = 0; + + /* special case, we don't support sorting by procname for now */ + if (*line_selected != 3) { + if (cputopview[*line_selected].sort == 1) + cputopview[*line_selected].reverse = 1; + for (i = 0; i < size; i++) + cputopview[i].sort = 0; + cputopview[*line_selected].sort = 1; + } +} + +void update_cpu_pref(int *line_selected, int toggle_view, int toggle_sort) +{ + int i; + int size; + + if (pref_panel_window) { + del_panel(pref_panel); + delwin(pref_panel_window); + } + size = 4; + + pref_panel_window = create_window(size + 2, 30, 10, 10); + pref_panel = new_panel(pref_panel_window); + + werase(pref_panel_window); + box(pref_panel_window, 0 , 0); + set_window_title(pref_panel_window, "CPUTop Preferences "); + wattron(pref_panel_window, A_BOLD); + mvwprintw(pref_panel_window, size + 1, 1, + " 's' : sort, space : toggle"); + wattroff(pref_panel_window, A_BOLD); + + if (*line_selected > (size - 1)) + *line_selected = size - 1; + else if (*line_selected < 0) + *line_selected = 0; + if (toggle_sort == 1) { + update_cpu_sort(line_selected); + update_current_view(); + } + + for (i = 0; i < size; i++) { + if (i == *line_selected) { + wattron(pref_panel_window, COLOR_PAIR(5)); + mvwhline(pref_panel_window, i + 1, 1, ' ', 30 - 2); + } + if (cputopview[i].sort == 1) + wattron(pref_panel_window, A_BOLD); + mvwprintw(pref_panel_window, i + 1, 1, "[-] %s", + cputopview[i].title); + wattroff(pref_panel_window, A_BOLD); + wattroff(pref_panel_window, COLOR_PAIR(5)); + + } + update_panels(); + doupdate(); +} + +void update_perf_sort(int *line_selected) +{ + int i; + int size; + Quark *global_perf; + + size = get_global_perf_list_size(i_end); + global_perf = g_new(Quark, size); + get_global_perf_list(i_end, global_perf, size); + + if (*line_selected > (size - 1)) + *line_selected = size - 1; + else if (*line_selected < 0) + *line_selected = 0; + + for (i = 0; i < size; i++) { + if (i != *line_selected) + modify_attribute(i_end, global_perf + i, "sort", 0); + else + modify_attribute(i_end, global_perf + i, "sort", 1); + } + + g_free(global_perf); +} + +void update_perf_pref(int *line_selected, int toggle_view, int toggle_sort) +{ + int i; + int size; + Quark *global_perf; + int visible; + int sort; + std::string key; + + if (pref_panel_window) { + del_panel(pref_panel); + delwin(pref_panel_window); + } + + size = get_global_perf_list_size(i_end); + global_perf = g_new(Quark, size); + get_global_perf_list(i_end, global_perf, size); + + pref_panel_window = create_window(size + 2, 30, 10, 10); + pref_panel = new_panel(pref_panel_window); + + werase(pref_panel_window); + box(pref_panel_window, 0 , 0); + set_window_title(pref_panel_window, "Perf Preferences "); + wattron(pref_panel_window, A_BOLD); + mvwprintw(pref_panel_window, size + 1, 1, + " 's' : sort, space : toggle"); + wattroff(pref_panel_window, A_BOLD); + + if (*line_selected > (size - 1)) + *line_selected = size - 1; + else if (*line_selected < 0) + *line_selected = 0; + + if (toggle_sort == 1) { + update_perf_sort(line_selected); + update_current_view(); + } + + for (i = 0; i < size; i++) { + get_attribute_value_at_int(i_end, global_perf + i, + "visible", visible); + get_attribute_value_at_string(i_end, global_perf + i, "key", + key); + if (i == *line_selected && toggle_view == 1) { + visible = visible == 1 ? 0 : 1; + modify_attribute(i_end, global_perf + i, "visible", + visible); + update_current_view(); + } + if (i == *line_selected) { + wattron(pref_panel_window, COLOR_PAIR(5)); + mvwhline(pref_panel_window, i + 1, 1, ' ', 30 - 2); + } + + if (get_attribute_value_at_int(i_end, global_perf + 1, "sort", + sort) && sort) { + wattron(pref_panel_window, A_BOLD); + } + mvwprintw(pref_panel_window, i + 1, 1, "[%c] %s", + visible == 1 ? 'x' : ' ', key.c_str() + 5); + wattroff(pref_panel_window, A_BOLD); + wattroff(pref_panel_window, COLOR_PAIR(5)); + } + update_panels(); + doupdate(); + + g_free(global_perf); +} + +int update_preference_panel(int *line_selected, int toggle_view, int toggle_sort) +{ + int ret = 0; + + switch(current_view) { + case perf: + update_perf_pref(line_selected, toggle_view, toggle_sort); + break; + case cpu: + update_cpu_pref(line_selected, toggle_view, toggle_sort); + break; + case iostream: + update_iostream_pref(line_selected, toggle_view, toggle_sort); + break; + case process_details: + update_process_detail_pref(line_selected, toggle_view, toggle_sort); + break; + default: + ret = -1; + break; + } + + return ret; +} + +int update_sort(int *line_selected) +{ + int ret = 0; + + switch(current_view) { + case perf: + update_perf_sort(line_selected); + break; + case cpu: + update_cpu_sort(line_selected); + break; + case iostream: + update_iostream_sort(line_selected); + break; + case process_details: + update_process_detail_sort(line_selected); + break; + default: + ret = -1; + break; + } + + return ret; +} + + +void toggle_pref_panel(void) +{ + int ret; + + if (pref_panel_visible) { + hide_panel(pref_panel); + pref_panel_visible = 0; + } else { + ret = update_preference_panel(&pref_line_selected, 0, 0); + if (ret < 0) + return; + show_panel(pref_panel); + pref_panel_visible = 1; + } + update_panels(); + doupdate(); +} + +void display() +{ + if (i_start == 0) + i_start = first_display_update; + else + i_start += refresh_display; + i_end = i_start + refresh_display; + + max_elements = get_sequence_length( + i_end, state_system->getQuark("proc")); + 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 'j': + case KEY_DOWN: + if (pref_panel_visible) { + pref_line_selected++; + update_preference_panel(&pref_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: + break; + case 'k': + case KEY_UP: + if (pref_panel_visible) { + if (pref_line_selected > 0) + pref_line_selected--; + update_preference_panel(&pref_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: + break; + + /* Navigate the history with arrows */ + case KEY_LEFT: + if ((i_start - refresh_display) >= + first_display_update) { + i_start -= refresh_display; + i_end = i_start + refresh_display; + print_log("Going back in time"); + } else { + print_log("Cannot rewind, last data is already displayed"); + } + max_elements = get_sequence_length( + i_end, state_system->getQuark("proc")); + + /* 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 ((i_end + refresh_display) <= + last_display_update) { + i_end += refresh_display; + i_start = i_end - refresh_display; + print_log("Going forward in time"); + max_elements = get_sequence_length( + i_end, state_system->getQuark( + "proc")); + update_current_view(); + update_footer(); + } else { + print_log("Manually moving forward"); + sem_post(&timer); + if (toggle_pause > 0) { + sem_post(&pause_sem); + update_current_view(); + sem_wait(&pause_sem); + } + } + + break; + case ' ': + if (pref_panel_visible) { + update_preference_panel(&pref_line_selected, 1, 0); + } else { + update_selected_processes(); + update_current_view(); + } + break; + case 's': + if (pref_panel_visible) + update_preference_panel(&pref_line_selected, 0, 1); + break; + case '>': + /* perf uses a hashtable, it is ordered backward */ + if (current_view == perf) { + pref_current_sort--; + } else if (!pref_panel_visible) { + pref_current_sort++; + } + update_sort(&pref_current_sort); + update_current_view(); + break; + case '<': + /* perf uses a hashtable, it is ordered backward */ + if (current_view == perf) { + pref_current_sort++; + } else if (!pref_panel_visible) { + pref_current_sort--; + } + update_sort(&pref_current_sort); + update_current_view(); + break; + + case 13: /* FIXME : KEY_ENTER ?? */ + if (pref_panel_visible) + break; + if (current_view != process_details) { + previous_view = current_view; + current_view = process_details; + } else { + current_view = previous_view; + previous_view = process_details; + } + update_current_view(); + break; + + case KEY_F(1): + if (pref_panel_visible) + toggle_pref_panel(); + current_view = cpu; + selected_line = 0; + update_current_view(); + break; + case KEY_F(2): + if (pref_panel_visible) + toggle_pref_panel(); + current_view = cpu; + selected_line = 0; + update_current_view(); + break; + case KEY_F(3): + if (pref_panel_visible) + toggle_pref_panel(); + current_view = perf; + selected_line = 0; + update_current_view(); + break; + case KEY_F(4): + if (pref_panel_visible) + toggle_pref_panel(); + current_view = iostream; + selected_line = 0; + 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 'r': + toggle_pref_panel(); + break; + /* ESCAPE, but slow to process, don't know why */ + case 27: + if (pref_panel_visible) + toggle_pref_panel(); + else if (current_view == process_details) { + current_view = previous_view; + previous_view = process_details; + } + update_current_view(); + break; + default: + update_current_view(); + break; + } + update_footer(); + } + return NULL; +} + +void init_view_headers() +{ + cputopview[0].title = strdup("CPU(%)"); + cputopview[0].sort = 1; + cputopview[1].title = strdup("PID"); + cputopview[2].title = strdup("TID"); + cputopview[3].title = strdup("NAME"); + + iostreamtopview[0].title = strdup("R (B/sec)"); + iostreamtopview[1].title = strdup("W (B/sec)"); + iostreamtopview[2].title = strdup("Total (B)"); + iostreamtopview[2].sort = 1; + + fileview[0].title = strdup("FD"); + fileview[1].title = strdup("READ"); + fileview[1].sort = 1; + fileview[2].title = strdup("WRITE"); +} + +void init_ncurses() +{ + selected_processes = g_ptr_array_new(); + sem_init(&update_display_sem, 0, 1); + init_view_headers(); + init_screen(); + + header = create_window(5, COLS - 1, 0, 0); + center = create_window(LINES - 5 - 7, COLS - 1, 5, 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); + + current_view = cpu; + + basic_header(); + update_footer(); + + pthread_create(&keyboard_thread, NULL, handle_keyboard, (void *)NULL); +} diff --git a/src/cursesdisplay.h b/src/cursesdisplay.h index fac2629..6e21509 100644 --- a/src/cursesdisplay.h +++ b/src/cursesdisplay.h @@ -19,7 +19,9 @@ #define CURSESDISPLAY_H #include +extern "C" { #include +} #include "common.h" enum view_list @@ -31,10 +33,10 @@ enum view_list tree, }; -enum view_list current_view; -enum view_list previous_view; +extern enum view_list current_view; +extern enum view_list previous_view; -void display(unsigned int); +void display(); void init_ncurses(); void reset_ncurses(); diff --git a/src/iostreamtop.c b/src/iostreamtop.c deleted file mode 100644 index bda9c7c..0000000 --- a/src/iostreamtop.c +++ /dev/null @@ -1,513 +0,0 @@ -/* - * Copyright (C) 2011-2012 Mathieu Bain - * - * 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., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include -#include -#include -#include -#include -#include - -#include "lttngtoptypes.h" -#include "common.h" -#include "iostreamtop.h" - -void add_file(struct processtop *proc, struct files *file, int fd) -{ - struct files *tmp_file; - struct processtop *parent; - int size; - int i; - - size = proc->process_files_table->len; - parent = proc->threadparent; - if (parent) - insert_file(parent, fd); - if (size <= fd) { - /* Add NULL file structures for undefined FDs */ - for (i = size; i < fd; i++) { - g_ptr_array_add(proc->process_files_table, NULL); - } - g_ptr_array_add(proc->process_files_table, file); - } else { - tmp_file = g_ptr_array_index(proc->process_files_table, fd); - if (tmp_file == NULL) - g_ptr_array_index(proc->process_files_table, fd) = file; - else { - if (strcmp(tmp_file->name, file->name) != 0) { - size = proc->process_files_table->len; - g_ptr_array_set_size(proc->process_files_table, - size+1); - g_ptr_array_index(proc->process_files_table, - size) = tmp_file; - g_ptr_array_index(proc->process_files_table, - fd) = file; - } else - tmp_file->flag = __NR_open; - } - } - /* - * The file may have be created in the parent - */ - if (file->flag == -1) { - file->fd = fd; - file->flag = __NR_open; - lttngtop.nbfiles++; - lttngtop.nbnewfiles++; - } -} - -/* - * Edit the file - * Called by handled_statedump_filename - */ -void edit_file(struct processtop *proc, struct files *file, int fd) -{ - int size = proc->process_files_table->len; - struct files *tmpfile; - - if (fd >= size) { - add_file(proc, file, fd); - } else { - tmpfile = g_ptr_array_index(proc->process_files_table, fd); - if (tmpfile) { - tmpfile->name = strdup(file->name); - free(file); - } else - add_file(proc, file, fd); - } -} - -void insert_file(struct processtop *proc, int fd) -{ - struct files *tmp; - struct files *tmp_parent; - struct processtop *parent; - - if (fd < 0) - return; - if (fd >= proc->process_files_table->len) { - tmp = g_new0(struct files, 1); - tmp->name = "Unknown"; - tmp->read = 0; - tmp->write = 0; - tmp->fd = fd; - tmp->flag = -1; - add_file(proc, tmp, fd); - } else { - tmp = g_ptr_array_index(proc->process_files_table, fd); - if (tmp == NULL) { - tmp = g_new0(struct files, 1); - tmp->name = "Unknown"; - tmp->read = 0; - tmp->write = 0; - tmp->fd = fd; - tmp->flag = -1; - add_file(proc, tmp, fd); - } else { - parent = proc->threadparent; - if (parent) { - tmp_parent = g_ptr_array_index( - parent->process_files_table, fd); - if (tmp_parent && - (strcmp(tmp->name, tmp_parent->name)) != 0) - tmp->name = strdup(tmp_parent->name); - } - } - } -} - -void close_file(struct processtop *proc, int fd) -{ - struct files *file; - - file = get_file(proc, fd); - if (file != NULL) { - file->flag = __NR_close; - lttngtop.nbfiles--; - } - lttngtop.nbclosedfiles++; -} - -struct files *get_file(struct processtop *proc, int fd) -{ - int len; - struct files *tmp = NULL; - - len = proc->process_files_table->len; - - /* - * It is possible that a file was open before taking the trace - * and its fd could be greater than all of the others fd - * used by the process - */ - if (fd < len && fd >= 0) - tmp = g_ptr_array_index(proc->process_files_table, fd); - - return tmp; -} - -void show_table(GPtrArray *tab) -{ - int i; - struct files *file; - - for (i = 0 ; i < tab->len; i++) { - file = g_ptr_array_index(tab, i); - if (file == NULL) - fprintf(stderr, "NULL, "); - else - fprintf(stderr, "%s, ", file->name); - } - fprintf(stderr, "]\n\n"); -} - -void show_history(struct file_history *history) -{ - struct file_history *tmp = history; - - while (tmp != NULL) { - fprintf(stderr, "fd = %d, name = %s\n", tmp->file->fd, - tmp->file->name); - tmp = tmp->next; - } - -} - -int update_iostream_ret(struct lttngtop *ctx, int tid, char *comm, - unsigned long timestamp, uint64_t cpu_id, int ret) -{ - struct processtop *tmp; - struct files *tmpfile; - int err = 0; - - tmp = get_proc(ctx, tid, comm, timestamp); - - if (tmp->syscall_info != NULL) { - if (tmp->syscall_info->type == __NR_read - && ret > 0) { - tmp->totalfileread += ret; - tmp->fileread += ret; - tmpfile = get_file(tmp, tmp->syscall_info->fd); - if (tmpfile) - tmpfile->read += ret; - } else if (tmp->syscall_info->type == __NR_write - && ret > 0) { - tmp->totalfilewrite += ret; - tmp->filewrite += ret; - tmpfile = get_file(tmp, tmp->syscall_info->fd); - if (tmpfile) - tmpfile->write += ret; - } else if (tmp->syscall_info->type == __NR_open - && ret > 0) { - tmpfile = tmp->files_history->file; - add_file(tmp, tmpfile, ret); - tmpfile->fd = ret; - } else { - err = -1; - } - g_free(tmp->syscall_info); - tmp->syscall_info = NULL; - } - return err; -} - -struct syscalls *create_syscall_info(unsigned int type, uint64_t cpu_id, - unsigned int tid, int fd) -{ - struct syscalls *syscall_info; - - syscall_info = g_new0(struct syscalls, 1); - syscall_info->type = type; - syscall_info->cpu_id = cpu_id; - syscall_info->tid = tid; - syscall_info->fd = fd; - - return syscall_info; -} - -struct file_history *create_file(struct file_history *history, char *file_name) -{ - struct files *new_file; - struct file_history *new_history; - - new_file = g_new0(struct files, 1); - new_history = g_new0(struct file_history, 1); - new_file->name = strdup(file_name); - new_file->read = 0; - new_file->write = 0; - new_file->flag = -1; - new_history->file = new_file; - new_history->next = history; - - return new_history; -} - -enum bt_cb_ret handle_exit_syscall(struct bt_ctf_event *call_data, - void *private_data) -{ - const struct definition *scope; - unsigned long timestamp; - char *comm; - uint64_t ret, tid; - uint64_t cpu_id; - - timestamp = bt_ctf_get_timestamp(call_data); - if (timestamp == -1ULL) - goto error; - - comm = get_context_comm(call_data); - tid = get_context_tid(call_data); - - 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; - } - - cpu_id = get_cpu_id(call_data); - - /* - * 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) -{ - const struct definition *scope; - struct processtop *tmp; - unsigned long timestamp; - uint64_t cpu_id; - int64_t tid; - char *procname; - int fd; - - timestamp = bt_ctf_get_timestamp(call_data); - if (timestamp == -1ULL) - goto error; - - tid = get_context_tid(call_data); - cpu_id = get_cpu_id(call_data); - - procname = get_context_comm(call_data); - - scope = bt_ctf_get_top_level_scope(call_data, - BT_EVENT_FIELDS); - fd = bt_ctf_get_uint64(bt_ctf_get_field(call_data, - scope, "_fd")); - if (bt_ctf_field_get_error()) { - fprintf(stderr, "Missing fd context info\n"); - goto error; - } - - tmp = get_proc(<tngtop, tid, procname, timestamp); - tmp->syscall_info = create_syscall_info(__NR_write, cpu_id, tid, fd); - - insert_file(tmp, fd); - - 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; - const struct definition *scope; - unsigned long timestamp; - uint64_t cpu_id; - int64_t tid; - char *procname; - int fd; - - timestamp = bt_ctf_get_timestamp(call_data); - if (timestamp == -1ULL) - goto error; - - tid = get_context_tid(call_data); - cpu_id = get_cpu_id(call_data); - - procname = get_context_comm(call_data); - - scope = bt_ctf_get_top_level_scope(call_data, - BT_EVENT_FIELDS); - fd = bt_ctf_get_uint64(bt_ctf_get_field(call_data, - scope, "_fd")); - if (bt_ctf_field_get_error()) { - fprintf(stderr, "Missing fd context info\n"); - goto error; - } - - tmp = get_proc(<tngtop, tid, procname, timestamp); - tmp->syscall_info = create_syscall_info(__NR_read, cpu_id, tid, fd); - - insert_file(tmp, fd); - - return BT_CB_OK; - -error: - return BT_CB_ERROR_STOP; -} - - -enum bt_cb_ret handle_sys_open(struct bt_ctf_event *call_data, - void *private_data) -{ - - struct processtop *tmp; - const struct definition *scope; - unsigned long timestamp; - uint64_t cpu_id; - int64_t tid; - char *procname; - char *file; - - timestamp = bt_ctf_get_timestamp(call_data); - if (timestamp == -1ULL) - goto error; - - tid = get_context_tid(call_data); - cpu_id = get_cpu_id(call_data); - - procname = get_context_comm(call_data); - - scope = bt_ctf_get_top_level_scope(call_data, - BT_EVENT_FIELDS); - file = bt_ctf_get_string(bt_ctf_get_field(call_data, - scope, "_filename")); - if (bt_ctf_field_get_error()) { - fprintf(stderr, "Missing file name context info\n"); - goto error; - } - - tmp = get_proc(<tngtop, tid, procname, timestamp); - tmp->syscall_info = create_syscall_info(__NR_open, cpu_id, tid, -1); - - tmp->files_history = create_file(tmp->files_history, file); - - return BT_CB_OK; - -error: - return BT_CB_ERROR_STOP; -} - - -enum bt_cb_ret handle_sys_close(struct bt_ctf_event *call_data, - void *private_data) -{ - const struct definition *scope; - struct processtop *tmp; - unsigned long timestamp; - int64_t tid; - char *procname; - int fd; - - timestamp = bt_ctf_get_timestamp(call_data); - if (timestamp == -1ULL) - goto error; - - tid = get_context_tid(call_data); - - procname = get_context_comm(call_data); - - scope = bt_ctf_get_top_level_scope(call_data, - BT_EVENT_FIELDS); - fd = bt_ctf_get_uint64(bt_ctf_get_field(call_data, - scope, "_fd")); - if (bt_ctf_field_get_error()) { - fprintf(stderr, "Missing fd context info\n"); - goto error; - } - - tmp = get_proc(<tngtop, tid, procname, timestamp); - - close_file(tmp, fd); - - return BT_CB_OK; - -error: - return BT_CB_ERROR_STOP; -} - -enum bt_cb_ret handle_statedump_file_descriptor(struct bt_ctf_event *call_data, - void *private_data) -{ - const struct definition *scope; - struct processtop *parent; - struct files *file; - unsigned long timestamp; - int64_t pid; - char *file_name; - int fd; - - timestamp = bt_ctf_get_timestamp(call_data); - if (timestamp == -1ULL) - goto error; - - scope = bt_ctf_get_top_level_scope(call_data, - BT_EVENT_FIELDS); - pid = bt_ctf_get_int64(bt_ctf_get_field(call_data, - scope, "_pid")); - 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); - fd = bt_ctf_get_int64(bt_ctf_get_field(call_data, - scope, "_fd")); - if (bt_ctf_field_get_error()) { - fprintf(stderr, "Missing fd context info\n"); - goto error; - } - - scope = bt_ctf_get_top_level_scope(call_data, - BT_EVENT_FIELDS); - file_name = bt_ctf_get_string(bt_ctf_get_field(call_data, - scope, "_filename")); - if (bt_ctf_field_get_error()) { - fprintf(stderr, "Missing file name context info\n"); - goto error; - } - - parent = get_proc_pid(<tngtop, pid, pid, timestamp); - parent->files_history = create_file(parent->files_history, file_name); - file = parent->files_history->file; - edit_file(parent, file, fd); - - return BT_CB_OK; - -error: - return BT_CB_ERROR_STOP; -} diff --git a/src/iostreamtop.cpp b/src/iostreamtop.cpp new file mode 100644 index 0000000..3e96c60 --- /dev/null +++ b/src/iostreamtop.cpp @@ -0,0 +1,540 @@ +/* + * Copyright (C) 2011-2012 Mathieu Bain + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +extern "C" +{ +#include +} + +#include "lttngtoptypes.h" +#include "common.h" +#include "iostreamtop.h" + +void add_file(Quark proc_quark, Quark file, int fd, + unsigned long timestamp) +{ + Quark parent; + Quark file_pointer; + Quark old_file; + std::string filename; + std::string old_filename; + std::string path; + int flag; + + if (state_system->attributeExists(proc_quark, "threadparent")) { + parent = state_system->getQuark(proc_quark, "threadparent"); + insert_file(parent, fd, timestamp); + } + + path = path_name_from_fd(fd); + if (state_system->attributeExists(proc_quark, path + "/file")) { + file_pointer = state_system->getQuark(proc_quark, path); + get_current_attribute_value_quark(&file_pointer, "file", + old_file); + get_current_attribute_value_string(&old_file, "name", + old_filename); + get_current_attribute_value_string(&file, "name", filename); + if (old_filename != filename) { + /* Different file with same fd, we overwrite the + file pointer */ + modify_attribute(timestamp, &file_pointer, "file", + file); + } else { + modify_attribute(timestamp, &old_file, "flag", + __NR_open); + } + } else { + file_pointer = state_system->getQuark(proc_quark, path); + modify_attribute(timestamp, &file_pointer, "file", file); + + } + /* To easily retrieve file pointer from files_history */ + modify_attribute(timestamp, &file, "fd", fd); + + /* The file may have been created in the parent */ + get_current_attribute_value_int(&file, "flag", flag); + if (flag == -1) { + modify_attribute(timestamp, &file, "fd", fd); + modify_attribute(timestamp, &file, "flag", __NR_open); + increment_attribute(timestamp, NULL, "nbfiles"); + increment_attribute(timestamp, NULL, "nbnewfiles"); + } +} + +/* + * Edit the file + * Called by handled_statedump_filename + */ +void edit_file(unsigned long timestamp, Quark proc, Quark file, int fd) +{ + std::string path; + + path = path_name_from_fd(fd); + if (!state_system->attributeExists(proc, path)) { + add_file(proc, file, fd, timestamp); + } +} + +Quark create_file(Quark proc, std::string file_name, unsigned long timestamp) +{ + Quark file_history; + Quark old_newest; + bool has_old; + std::string current_path; + int index; + std::stringstream ss; + + if (!get_current_attribute_value_quark(&proc, "files_history/current", + file_history)) { + /* First file for process */ + file_history = state_system->getQuark( + proc, "files_history/file_0"); + } else { + + current_path = state_system->getFullAttributeName(file_history); + index = atoi(current_path.substr(current_path.find_last_of('_') + + 1).c_str()); + ss << "files_history/file_" << index + 1; + current_path = ss.str(); + file_history = state_system->getQuark( + proc, current_path); + } + + has_old = get_current_attribute_value_quark( + &proc,"files_history/current", old_newest); + modify_attribute(timestamp, &proc, "files_history/current", + file_history); + modify_attribute(timestamp, &file_history, "file/name", file_name); + modify_attribute(timestamp, &file_history, "file/read", 0); + modify_attribute(timestamp, &file_history, "file/write", 0); + modify_attribute(timestamp, &file_history, "file/flag", -1); + modify_attribute(timestamp, &file_history, "file/fd", -1); + modify_attribute(timestamp, &file_history, "file/birth", timestamp); + if (has_old) + modify_attribute(timestamp, &file_history, "next", old_newest); + else + nullify_attribute(timestamp, &file_history, "next"); + + return state_system->getQuark(file_history, "file"); +} + +void insert_file(Quark proc, int fd, unsigned long timestamp) +{ + Quark file; + Quark parent; + Quark parent_file; + bool parent_has_file; + std::string name; + + if (fd < 0) + return; + + if (get_file(proc, fd, file)) { + if (state_system->attributeExists(proc, "threadparent")) { + parent = state_system->getQuark(proc, "threadparent"); + parent_has_file = get_file(parent, fd, + parent_file); + if (parent_has_file) { + get_current_attribute_value_string(&parent_file, + "name", + name); + modify_attribute(timestamp, &file, "name", + name); + } + } + } else { + file = create_file(proc, "Unknown", timestamp); + add_file(proc, file, fd, timestamp); + } +} + +void close_file(unsigned long timestamp, Quark proc, int fd) +{ + Quark file; + bool file_found; + + file_found = get_current_attribute_value_quark(&proc, + path_name_from_fd(fd), + file); + if (file_found) { + modify_attribute(timestamp, &file, "flag", __NR_close); + decrement_attribute(timestamp, NULL, "nbfiles"); + } + increment_attribute(timestamp, NULL, "nbdeadfiles"); +} + +bool get_file(Quark proc_quark, int fd, Quark &file_quark) +{ + std::string path = path_name_from_fd(fd); + Quark file_pointer; + if (state_system->attributeExists(proc_quark, path)) { + file_pointer = state_system->getQuark(proc_quark, path); + return get_current_attribute_value_quark(&file_pointer, "file", + file_quark); + } else { + return false; + } +} + +void show_history(Quark proc) +{ + Quark file; + int fd; + std::string name; + + if (get_current_attribute_value_quark(&proc, "files_history", file)) { + do { + fd = -1; + name = ""; + get_current_attribute_value_int(&file, "file/fd", fd); + get_current_attribute_value_string( + &file, "file/name", name); + fprintf(stderr, "fd = %d, name = %s\n", fd, + name.c_str()); + } while (get_current_attribute_value_quark( + &file, "next", file)); + } + +} + +int update_iostream_ret(int tid, char *comm, unsigned long timestamp, + uint64_t cpu_id, int ret) +{ + Quark proc_quark; + Quark syscall_info_quark; + Quark file_quark; + Quark file_history_quark; + bool file_found; + int syscall_type; + int fd; + int err = 0; + + proc_quark = get_proc(tid, comm, timestamp); + + if (state_system->attributeExists(proc_quark, "syscall_info")) { + syscall_info_quark = state_system->getQuark(proc_quark, + "syscall_info"); + get_current_attribute_value_int(&syscall_info_quark, "type", + syscall_type); + if (syscall_type == __NR_read && ret > 0) { + increase_attribute(timestamp, &proc_quark, + "totalfileread", ret); + increase_attribute(timestamp, &proc_quark, + "fileread", ret); + get_current_attribute_value_int(&syscall_info_quark, + "fd", fd); + file_found = get_file(proc_quark, fd, + file_quark); + if (file_found) + increase_attribute(timestamp, &file_quark, + "read", ret); + } else if (syscall_type == __NR_write && ret > 0) { + + increase_attribute(timestamp, &proc_quark, + "totalfilewrite", ret); + increase_attribute(timestamp, &proc_quark, + "filewrite", ret); + get_current_attribute_value_int(&syscall_info_quark, + "fd", fd); + file_found = get_file(proc_quark, fd, + file_quark); + if (file_found) + increase_attribute(timestamp, &file_quark, + "write", ret); + } else if (syscall_type == __NR_open && ret > 0) { + file_history_quark = state_system->getQuark(proc_quark, + "files_history"); + file_quark = state_system->getQuark(file_history_quark, + "current/file"); + add_file(proc_quark, file_quark, ret, + timestamp); + modify_attribute(timestamp, &file_quark, "fd", fd); + } else { + err = -1; + } + } + return err; +} + +void update_syscall_info(unsigned long timestamp, int type, int cpu_id, + Quark proc, int fd) +{ + int tid; + + get_current_attribute_value_int(&proc, "tid", tid); + modify_attribute(timestamp, &proc, "syscall_info/type", type); + modify_attribute(timestamp, &proc, "syscall_info/cpu_id", cpu_id); + modify_attribute(timestamp, &proc, "syscall_info/tid", tid); + modify_attribute(timestamp, &proc, "syscall_info/fd", fd); +} + +enum bt_cb_ret handle_exit_syscall(struct bt_ctf_event *call_data, + void *private_data) +{ + const struct definition *scope; + unsigned long timestamp; + char *comm; + uint64_t ret, tid; + uint64_t cpu_id; + + timestamp = bt_ctf_get_timestamp(call_data); + if (timestamp == -1ULL) + goto error; + + comm = get_context_comm(call_data); + tid = get_context_tid(call_data); + + 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; + } + + cpu_id = get_cpu_id(call_data); + + /* + * 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( + 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) +{ + const struct definition *scope; + Quark proc; + unsigned long timestamp; + int cpu_id; + int tid; + char *procname; + int fd; + + timestamp = bt_ctf_get_timestamp(call_data); + if (timestamp == -1ULL) + goto error; + + tid = get_context_tid(call_data); + cpu_id = get_cpu_id(call_data); + + procname = get_context_comm(call_data); + + scope = bt_ctf_get_top_level_scope(call_data, + BT_EVENT_FIELDS); + fd = bt_ctf_get_uint64(bt_ctf_get_field(call_data, + scope, "_fd")); + if (bt_ctf_field_get_error()) { + fprintf(stderr, "Missing fd context info\n"); + goto error; + } + + proc = get_proc(tid, procname, timestamp); + update_syscall_info(timestamp, __NR_write, cpu_id, proc, fd); + + insert_file(proc, fd, timestamp); + + 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) +{ + const struct definition *scope; + Quark proc; + unsigned long timestamp; + uint64_t cpu_id; + int64_t tid; + char *procname; + int fd; + + timestamp = bt_ctf_get_timestamp(call_data); + if (timestamp == -1ULL) + goto error; + + tid = get_context_tid(call_data); + cpu_id = get_cpu_id(call_data); + + procname = get_context_comm(call_data); + + scope = bt_ctf_get_top_level_scope(call_data, + BT_EVENT_FIELDS); + fd = bt_ctf_get_uint64(bt_ctf_get_field(call_data, + scope, "_fd")); + if (bt_ctf_field_get_error()) { + fprintf(stderr, "Missing fd context info\n"); + goto error; + } + + proc = get_proc(tid, procname, timestamp); + update_syscall_info(timestamp, __NR_read, cpu_id, proc, fd); + + insert_file(proc, fd, timestamp); + + return BT_CB_OK; + +error: + return BT_CB_ERROR_STOP; +} + +enum bt_cb_ret handle_sys_open(struct bt_ctf_event *call_data, + void *private_data) +{ + Quark proc; + const struct definition *scope; + unsigned long timestamp; + uint64_t cpu_id; + int64_t tid; + char *procname; + char *file; + + timestamp = bt_ctf_get_timestamp(call_data); + if (timestamp == -1ULL) + goto error; + + tid = get_context_tid(call_data); + cpu_id = get_cpu_id(call_data); + + procname = get_context_comm(call_data); + + scope = bt_ctf_get_top_level_scope(call_data, + BT_EVENT_FIELDS); + file = bt_ctf_get_string(bt_ctf_get_field(call_data, + scope, "_filename")); + if (bt_ctf_field_get_error()) { + fprintf(stderr, "Missing file name context info\n"); + goto error; + } + + proc = get_proc(tid, procname, timestamp); + update_syscall_info(timestamp, __NR_open, cpu_id, proc, -1); + + create_file(proc, file, timestamp); + + return BT_CB_OK; + +error: + return BT_CB_ERROR_STOP; +} + +enum bt_cb_ret handle_sys_close(struct bt_ctf_event *call_data, + void *private_data) +{ + const struct definition *scope; + Quark proc; + unsigned long timestamp; + int64_t tid; + char *procname; + int fd; + + timestamp = bt_ctf_get_timestamp(call_data); + if (timestamp == -1ULL) + goto error; + + tid = get_context_tid(call_data); + + procname = get_context_comm(call_data); + + scope = bt_ctf_get_top_level_scope(call_data, + BT_EVENT_FIELDS); + fd = bt_ctf_get_uint64(bt_ctf_get_field(call_data, + scope, "_fd")); + if (bt_ctf_field_get_error()) { + fprintf(stderr, "Missing fd context info\n"); + goto error; + } + + proc = get_proc(tid, procname, timestamp); + + close_file(timestamp, proc, fd); + + return BT_CB_OK; + +error: + return BT_CB_ERROR_STOP; +} + +enum bt_cb_ret handle_statedump_file_descriptor( + struct bt_ctf_event *call_data, void *private_data) +{ + const struct definition *scope; + Quark parent; + Quark file; + unsigned long timestamp; + int64_t pid; + char *file_name; + int fd; + + timestamp = bt_ctf_get_timestamp(call_data); + if (timestamp == -1ULL) + goto error; + + scope = bt_ctf_get_top_level_scope(call_data, + BT_EVENT_FIELDS); + pid = bt_ctf_get_int64(bt_ctf_get_field(call_data, + scope, "_pid")); + 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); + fd = bt_ctf_get_int64(bt_ctf_get_field(call_data, + scope, "_fd")); + if (bt_ctf_field_get_error()) { + fprintf(stderr, "Missing fd context info\n"); + goto error; + } + + scope = bt_ctf_get_top_level_scope(call_data, + BT_EVENT_FIELDS); + file_name = bt_ctf_get_string(bt_ctf_get_field(call_data, + scope, "_filename")); + if (bt_ctf_field_get_error()) { + fprintf(stderr, "Missing file name context info\n"); + goto error; + } + + parent = get_proc_pid(pid, pid, timestamp); + create_file(parent, file_name, timestamp); + file = state_system->getQuark(parent, "files_history/current/file"); + edit_file(timestamp, parent, file, fd); + + return BT_CB_OK; + +error: + return BT_CB_ERROR_STOP; +} diff --git a/src/iostreamtop.h b/src/iostreamtop.h index 0124ac6..0776507 100644 --- a/src/iostreamtop.h +++ b/src/iostreamtop.h @@ -18,27 +18,32 @@ #ifndef _IOSTREANTOP_H #define _IOSTREAMTOP_H +extern "C" { #include #include +} #include #include #include -struct files *get_file(struct processtop *proc, int fd); -void show_table(GPtrArray *tab); -void insert_file(struct processtop *proc, int fd); +#include "common.h" + +bool get_file(Quark proc_quark, int fd, Quark &file_quark); +void insert_file(Quark proc, int fd, unsigned long timestamp); + +void show_history(Quark proc); enum bt_cb_ret handle_exit_syscall(struct bt_ctf_event *call_data, - void *private_data); + void *private_data); enum bt_cb_ret handle_sys_write(struct bt_ctf_event *call_data, - void *private_data); + void *private_data); enum bt_cb_ret handle_sys_read(struct bt_ctf_event *call_data, - void *private_data); + void *private_data); enum bt_cb_ret handle_sys_open(struct bt_ctf_event *call_data, - void *private_data); + void *private_data); enum bt_cb_ret handle_sys_close(struct bt_ctf_event *call_data, - void *private_data); -enum bt_cb_ret handle_statedump_file_descriptor(struct bt_ctf_event *call_data, - void *private_data); + void *private_data); +enum bt_cb_ret handle_statedump_file_descriptor( + struct bt_ctf_event *call_data, void *private_data); #endif /* _IOSTREAMTOP_H */ diff --git a/src/libStateIntegrationPrototype.cpp b/src/libStateIntegrationPrototype.cpp new file mode 100644 index 0000000..c8ae1a6 --- /dev/null +++ b/src/libStateIntegrationPrototype.cpp @@ -0,0 +1,3 @@ +#include "libStateIntegrationPrototype.h" + + diff --git a/src/libStateIntegrationPrototype.h b/src/libStateIntegrationPrototype.h new file mode 100644 index 0000000..64e58ac --- /dev/null +++ b/src/libStateIntegrationPrototype.h @@ -0,0 +1,31 @@ +#ifndef _LIB_STATE_INTEGRATION_PROTOTYPE_H_ +#define _LIB_STATE_INTEGRATION_PROTOTYPE_H_ + + + +#ifdef __cplusplus +extern "C" +{ +#endif +#include +#include +#include +#include +#include "common.h" +#ifdef __cplusplus +} +#endif + + + +const unsigned long p_refresh_display = 1 * NSEC_PER_SEC; + + + + +/* #ifdef __cplusplus */ +/* } */ +/* #endif */ + +#endif + diff --git a/src/lttngtop.c b/src/lttngtop.c deleted file mode 100644 index 2ee303a..0000000 --- a/src/lttngtop.c +++ /dev/null @@ -1,677 +0,0 @@ -/* - * Copyright (C) 2011-2012 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., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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) { - if (quit) - return NULL; - 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); - /* - * Prevent the 1 second delay when we hit ESC - */ - ESCDELAY = 0; - init_ncurses(); - - while (1) { - sem_wait(&timer); - sem_wait(&goodtodisplay); - sem_wait(&pause_sem); - - copy = g_ptr_array_index(copies, current_display_index); - assert(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; - 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 = g_new0(struct perfcounter, 1); - /* by default, make it visible in the UI */ - ret->visible = 1; - g_hash_table_insert(table, (gpointer) strdup(name), ret); - -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(const struct bt_ctf_event *event, - const struct definition *scope, - struct processtop *proc, - struct cputime *cpu) -{ - struct definition const * const *list = NULL; - const struct definition *field; - unsigned int count; - struct perfcounter *perfcounter; - GHashTableIter iter; - gpointer key; - int ret; - - if (!scope) - goto end; - - ret = bt_ctf_get_field_list(event, scope, &list, &count); - if (ret < 0) - goto end; - - if (count == 0) - goto end; - - g_hash_table_iter_init(&iter, global_perf_liszt); - while (g_hash_table_iter_next (&iter, &key, (gpointer) &perfcounter)) { - field = bt_ctf_get_field(event, scope, (char *) key); - if (field) { - int value = bt_ctf_get_uint64(field); - if (bt_ctf_field_get_error()) - continue; - update_perf_value(proc, cpu, (char *) key, value); - } - } - -end: - return; -} - -void update_perf_counter(struct processtop *proc, const struct bt_ctf_event *event) -{ - struct cputime *cpu; - const struct definition *scope; - - cpu = get_cpu(get_cpu_id(event)); - - 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); -} - -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; - unsigned long timestamp; - - timestamp = bt_ctf_get_timestamp(call_data); - if (timestamp == -1ULL) - goto error; - - pid = get_context_pid(call_data); - if (pid == -1ULL) { - goto error; - } - tid = get_context_tid(call_data); - if (tid == -1ULL) { - goto error; - } - ppid = get_context_ppid(call_data); - if (ppid == -1ULL) { - goto error; - } - comm = get_context_comm(call_data); - if (!comm) { - 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(); - global_perf_liszt = g_hash_table_new(g_str_hash, g_str_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); - - reset_global_counters(); - lttngtop.nbproc = 0; - lttngtop.nbthreads = 0; - lttngtop.nbfiles = 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 *fp) -{ - fprintf(fp, "LTTngTop %s\n\n", VERSION); - fprintf(fp, "Usage : lttngtop /path/to/trace\n"); -} - -/* - * 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; - 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; - const 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); - /* to get all the process from the statedumps */ - bt_ctf_iter_add_callback(iter, - g_quark_from_static_string( - "lttng_statedump_process_state"), - NULL, 0, handle_statedump_process_state, - 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); - bt_ctf_iter_add_callback(iter, - g_quark_from_static_string("sys_open"), - NULL, 0, handle_sys_open, NULL, NULL, NULL); - bt_ctf_iter_add_callback(iter, - g_quark_from_static_string("sys_close"), - NULL, 0, handle_sys_close, NULL, NULL, NULL); - bt_ctf_iter_add_callback(iter, - g_quark_from_static_string( - "lttng_statedump_file_descriptor"), - NULL, 0, handle_statedump_file_descriptor, - 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_ctf_iter_destroy(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 = -1; - - /* - * 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) { - close(dirfd); - ret = -1; - continue; - } 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, "[warning] [Context] opening trace \"%s\" from %s " - "for reading.\n", node->fts_accpath, path); - /* Allow to skip erroneous traces. */ - continue; - } - g_array_append_val(trace_ids, trace_id); - } - } - - g_array_free(trace_ids, TRUE); - return ret; - -error: - return ret; -} - -static int check_field_requirements(const struct bt_ctf_field_decl *const * field_list, - int field_cnt, int *tid_check, int *pid_check, - int *procname_check, int *ppid_check) -{ - int j; - struct perfcounter *global; - const char *name; - - for (j = 0; j < field_cnt; j++) { - name = bt_ctf_get_decl_field_name(field_list[j]); - if (*tid_check == 0) { - if (strncmp(name, "tid", 3) == 0) - (*tid_check)++; - } - if (*pid_check == 0) { - if (strncmp(name, "tid", 3) == 0) - (*pid_check)++; - } - if (*ppid_check == 0) { - if (strncmp(name, "ppid", 4) == 0) - (*ppid_check)++; - } - if (*procname_check == 0) { - if (strncmp(name, "procname", 8) == 0) - (*procname_check)++; - } - if (strncmp(name, "perf_", 5) == 0) { - global = g_hash_table_lookup(global_perf_liszt, (gpointer) name); - if (!global) { - global = g_new0(struct perfcounter, 1); - /* by default, sort on the first perf context */ - if (g_hash_table_size(global_perf_liszt) == 0) - global->sort = 1; - global->visible = 1; - g_hash_table_insert(global_perf_liszt, (gpointer) strdup(name), global); - } - } - } - - if (*tid_check == 1 && *pid_check == 1 && *ppid_check == 1 && - *procname_check == 1) - return 0; - - return -1; -} - -/* - * check_requirements: check if the required context informations are available - * - * If each mandatory context information is available for at least in one - * event, return 0 otherwise return -1. - */ -int check_requirements(struct bt_context *ctx) -{ - unsigned int i, evt_cnt, field_cnt; - struct bt_ctf_event_decl *const * evt_list; - const struct bt_ctf_field_decl *const * field_list; - int tid_check = 0; - int pid_check = 0; - int procname_check = 0; - int ppid_check = 0; - int ret = 0; - - bt_ctf_get_event_decl_list(0, ctx, &evt_list, &evt_cnt); - for (i = 0; i < evt_cnt; i++) { - bt_ctf_get_decl_fields(evt_list[i], BT_STREAM_EVENT_CONTEXT, - &field_list, &field_cnt); - ret = check_field_requirements(field_list, field_cnt, - &tid_check, &pid_check, &procname_check, - &ppid_check); - - bt_ctf_get_decl_fields(evt_list[i], BT_EVENT_CONTEXT, - &field_list, &field_cnt); - ret = check_field_requirements(field_list, field_cnt, - &tid_check, &pid_check, &procname_check, - &ppid_check); - - bt_ctf_get_decl_fields(evt_list[i], BT_STREAM_PACKET_CONTEXT, - &field_list, &field_cnt); - ret = check_field_requirements(field_list, field_cnt, - &tid_check, &pid_check, &procname_check, - &ppid_check); - } - - if (tid_check == 0) { - ret = -1; - fprintf(stderr, "[error] missing tid context information\n"); - } - if (pid_check == 0) { - ret = -1; - fprintf(stderr, "[error] missing pid context information\n"); - } - if (ppid_check == 0) { - ret = -1; - fprintf(stderr, "[error] missing ppid context information\n"); - } - if (procname_check == 0) { - ret = -1; - fprintf(stderr, "[error] missing procname context information\n"); - } - - 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) { - fprintf(stderr, "[error] Opening the trace\n"); - goto end; - } - - ret = check_requirements(bt_ctx); - if (ret < 0) { - fprintf(stderr, "[error] some mandatory contexts were missing, exiting.\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); - pthread_join(timer_thread, NULL); - -end: - bt_context_put(bt_ctx); - return 0; -} diff --git a/src/lttngtop.cpp b/src/lttngtop.cpp new file mode 100644 index 0000000..43afe48 --- /dev/null +++ b/src/lttngtop.cpp @@ -0,0 +1,717 @@ +/* + * Copyright (C) 2011-2012 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +extern "C" { +#include +#include +#include +#include +} +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lttngtoptypes.h" +#include "cputop.h" +#include "iostreamtop.h" +#include "common.h" +#include "cursesdisplay.h" + +#define DEFAULT_FILE_ARRAY_SIZE 1 + +const char *opt_input_path; + +pthread_t display_thread; +pthread_t timer_thread; + +int quit = 0; +std::string history_file; + +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) { + if (quit) + return NULL; + sem_wait(&pause_sem); + sem_post(&pause_sem); + sem_post(&timer); + sleep(refresh_display/NSEC_PER_SEC); + } +} + +void *ncurses_display(void *p) +{ + sem_wait(&bootstrap); + /* + * Prevent the 1 second delay when we hit ESC + */ + ESCDELAY = 0; + init_ncurses(); + + while (1) { + sem_wait(&timer); + sem_wait(&goodtodisplay); + sem_wait(&pause_sem); + + display(); + + sem_post(&goodtoupdate); + sem_post(&pause_sem); + + if (quit) { + reset_ncurses(); + pthread_exit(0); + } + } +} + +/* + * We soft update each attribute during the execution for perfromance reasons. + * Here we write the actual intervals to disk, which are needed to query history. + */ +void create_intervals(unsigned long timestamp) +{ + StateValue::SharedPtr value; + + for (std::set::iterator i = modified_quarks.begin(); + i != modified_quarks.end(); + i++) { + value = state_system->getCurrentStateValue(*i); + /* To force the creation of an interval (which is what we want) + we need to pass a different value than the current value. + We reapply the correct value afterwards */ + if (std::tr1::dynamic_pointer_cast(value)) { + state_system->modifyAttribute(timestamp, *i, 0); + } else { + state_system->modifyAttribute(timestamp, *i, + StateValue::getNullValue()); + } + state_system->updateCurrentState(*i, value); + } + modified_quarks.clear(); +} + +/* + * 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 (first_display_update == 0) + first_display_update = timestamp; + + if (timestamp - last_display_update >= refresh_display) { + sem_wait(&goodtoupdate); + create_intervals(timestamp); + update_state_on_refresh(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 (specified by root) + */ +Quark get_perf_counter(unsigned long timestamp, Quark root, std::string name) +{ + Quark perf_counter; + + if (state_system->attributeExists(root, "perf/" + name)) { + return state_system->getQuark(root, "perf/" + name); + } else { + perf_counter = state_system->getQuark(root, "perf/" + name); + modify_attribute(timestamp, &perf_counter, "count", 0); + modify_attribute(timestamp, &perf_counter, "visible", 1); + add_in_sequence(timestamp, perf_counter, + state_system->getQuark(root, "perf")); + return perf_counter; + } +} + +void update_perf_value(unsigned long timestamp, Quark proc, Quark cpu, + std::string name, int value) +{ + Quark cpu_perf, process_perf; + int count; + + cpu_perf = get_perf_counter(timestamp, cpu, name); + get_current_attribute_value_int(&cpu_perf, "count", count); + if (count < value) { + process_perf = get_perf_counter(timestamp, proc, name); + increase_attribute( + timestamp, &process_perf, "count", value - count); + modify_attribute(timestamp, &cpu_perf, "count", value); + } +} + +void extract_perf_counter_scope(unsigned long timestamp, + const struct bt_ctf_event *event, + const struct definition *scope, + Quark proc, Quark cpu) +{ + struct definition const * const *list = NULL; + const struct definition *field; + unsigned int count; + int ret; + std::string key; + Quark perf_quark; + + if (!scope) + goto end; + + ret = bt_ctf_get_field_list(event, scope, &list, &count); + if (ret < 0) + goto end; + + if (count == 0) + goto end; + + if (get_current_attribute_value_quark( + NULL, "perf", perf_quark)) { + do { + get_current_attribute_value_string( + &perf_quark, "key", key); + field = bt_ctf_get_field(event, scope, key.c_str()); + if (field) { + int value = bt_ctf_get_uint64(field); + if (bt_ctf_field_get_error()) + continue; + update_perf_value( + timestamp, proc, cpu, key, value); + } + } while (get_current_attribute_value_quark( + &perf_quark, "next", perf_quark)); + } + + +end: + return; +} + +void update_perf_counter(unsigned long timestamp, Quark proc, + const struct bt_ctf_event *event) +{ + Quark cpu; + const struct definition *scope; + + cpu = get_cpu(get_cpu_id(event), timestamp); + + scope = bt_ctf_get_top_level_scope(event, BT_STREAM_EVENT_CONTEXT); + extract_perf_counter_scope(timestamp, event, scope, proc, cpu); + + scope = bt_ctf_get_top_level_scope(event, BT_STREAM_PACKET_CONTEXT); + extract_perf_counter_scope(timestamp, event, scope, proc, cpu); + + scope = bt_ctf_get_top_level_scope(event, BT_EVENT_CONTEXT); + extract_perf_counter_scope(timestamp, event, scope, proc, cpu); +} + +enum bt_cb_ret fix_process_table(struct bt_ctf_event *call_data, + void *private_data) +{ int pid, tid, ppid; + char *comm; + Quark parent, child; + unsigned long timestamp; + + timestamp = bt_ctf_get_timestamp(call_data); + if (timestamp == -1ULL) + goto error; + + pid = get_context_pid(call_data); + if ((unsigned long)pid == -1ULL) { + goto error; + } + tid = get_context_tid(call_data); + if ((unsigned long)tid == -1ULL) { + goto error; + } + ppid = get_context_ppid(call_data); + if ((unsigned long)ppid == -1ULL) { + goto error; + } + comm = get_context_comm(call_data); + if (!comm) { + goto error; + } + + /* find or create the current process */ + if (!find_process_tid(tid, child)) + child = add_proc(tid, comm, timestamp); + update_proc(timestamp, child, pid, tid, ppid, comm); + + if (pid != tid) { + /* find or create the parent */ + if (!find_process_tid(pid, parent)) { + parent = add_proc(pid, comm, timestamp); + modify_attribute(timestamp, &parent, "pid", pid); + } + + /* attach the parent to the current process */ + modify_attribute(timestamp, &parent, "threadparent", parent); + add_thread(timestamp, parent, child); + } + + update_perf_counter(timestamp, child, call_data); + + return BT_CB_OK; + +error: + return BT_CB_ERROR_STOP; +} + +void init_lttngtop() +{ + 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); + + // TODO: real file name + std::stringstream ss; + ss << "history" << time(NULL) << ".hst"; + history_file = ss.str(); + IntervalHistoryProvider *ihp = g_new(IntervalHistoryProvider, 1); + new (ihp) IntervalHistoryProvider(history_file); + state_system = g_new(StateSystem, 1); + new (state_system) StateSystem(ihp); + + /* Create global attributes */ + state_system->updateCurrentState( + state_system->getQuark("nbproc"), 0); + state_system->updateCurrentState( + state_system->getQuark("nbnewproc"), 0); + state_system->updateCurrentState( + state_system->getQuark("nbdeadproc"), 0); + state_system->updateCurrentState( + state_system->getQuark("nbthreads"), 0); + state_system->updateCurrentState( + state_system->getQuark("nbnewthreads"), 0); + state_system->updateCurrentState( + state_system->getQuark("nbdeadthreads"), 0); + state_system->updateCurrentState( + state_system->getQuark("nbfiles"), 0); + state_system->updateCurrentState( + state_system->getQuark("nbnewfiles"), 0); + state_system->updateCurrentState( + state_system->getQuark("nbdeadfiles"), 0); +} + +void usage(FILE *fp) +{ + fprintf(fp, "LTTngTop %s\n\n", VERSION); + fprintf(fp, "Usage : lttngtop /path/to/trace\n"); +} + +/* + * 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; + 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; + const struct bt_ctf_event *event; + int ret = 0; + + begin_pos.type = bt_iter_pos::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); + /* to get all the process from the statedumps */ + bt_ctf_iter_add_callback(iter, + g_quark_from_static_string( + "lttng_statedump_process_state"), + NULL, 0, handle_statedump_process_state, + 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); + bt_ctf_iter_add_callback(iter, + g_quark_from_static_string("sys_open"), + NULL, 0, handle_sys_open, NULL, NULL, NULL); + bt_ctf_iter_add_callback(iter, + g_quark_from_static_string("sys_close"), + NULL, 0, handle_sys_close, NULL, NULL, NULL); + bt_ctf_iter_add_callback(iter, + g_quark_from_static_string( + "lttng_statedump_file_descriptor"), + NULL, 0, handle_statedump_file_descriptor, + 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_ctf_iter_destroy(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 = -1; + + /* + * 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) { + close(dirfd); + ret = -1; + continue; + } 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, "[warning] [Context] opening trace \"%s\" from %s " + "for reading.\n", node->fts_accpath, path); + /* Allow to skip erroneous traces. */ + continue; + } + g_array_append_val(trace_ids, trace_id); + } + } + + g_array_free(trace_ids, TRUE); + return ret; + +error: + return ret; +} + +static int check_field_requirements(const struct bt_ctf_field_decl *const * field_list, + int field_cnt, int *tid_check, int *pid_check, + int *procname_check, int *ppid_check) +{ + int j; + const char *name; + Quark perf; + Quark perf_root = state_system->getQuark("perf"); + static bool first_perf_counter = true; + + for (j = 0; j < field_cnt; j++) { + name = bt_ctf_get_decl_field_name(field_list[j]); + if (*tid_check == 0) { + if (strncmp(name, "tid", 3) == 0) + (*tid_check)++; + } + if (*pid_check == 0) { + if (strncmp(name, "tid", 3) == 0) + (*pid_check)++; + } + if (*ppid_check == 0) { + if (strncmp(name, "ppid", 4) == 0) + (*ppid_check)++; + } + if (*procname_check == 0) { + if (strncmp(name, "procname", 8) == 0) + (*procname_check)++; + } + if (strncmp(name, "perf_", 5) == 0) { + if (!state_system->attributeExists( + perf_root, name+5)) { + perf = state_system->getQuark(perf_root, name+5); + /* by default, sort on the first perf context */ + if (first_perf_counter) { + modify_attribute(0, &perf, "sort", 1); + first_perf_counter = false; + } + modify_attribute(0, &perf, "visible", 1); + add_in_sequence(0, perf, perf_root); + } + } + } + + if (*tid_check == 1 && *pid_check == 1 && *ppid_check == 1 && + *procname_check == 1) + return 0; + + return -1; +} + +/* + * check_requirements: check if the required context informations are available + * + * If each mandatory context information is available for at least in one + * event, return 0 otherwise return -1. + */ +int check_requirements(struct bt_context *ctx) +{ + unsigned int i, evt_cnt, field_cnt; + struct bt_ctf_event_decl *const * evt_list; + const struct bt_ctf_field_decl *const * field_list; + int tid_check = 0; + int pid_check = 0; + int procname_check = 0; + int ppid_check = 0; + int ret = 0; + + bt_ctf_get_event_decl_list(0, ctx, &evt_list, &evt_cnt); + for (i = 0; i < evt_cnt; i++) { + bt_ctf_get_decl_fields(evt_list[i], BT_STREAM_EVENT_CONTEXT, + &field_list, &field_cnt); + ret = check_field_requirements(field_list, field_cnt, + &tid_check, &pid_check, &procname_check, + &ppid_check); + + bt_ctf_get_decl_fields(evt_list[i], BT_EVENT_CONTEXT, + &field_list, &field_cnt); + ret = check_field_requirements(field_list, field_cnt, + &tid_check, &pid_check, &procname_check, + &ppid_check); + + bt_ctf_get_decl_fields(evt_list[i], BT_STREAM_PACKET_CONTEXT, + &field_list, &field_cnt); + ret = check_field_requirements(field_list, field_cnt, + &tid_check, &pid_check, &procname_check, + &ppid_check); + } + + if (tid_check == 0) { + ret = -1; + fprintf(stderr, "[error] missing tid context information\n"); + } + if (pid_check == 0) { + ret = -1; + fprintf(stderr, "[error] missing pid context information\n"); + } + if (ppid_check == 0) { + ret = -1; + fprintf(stderr, "[error] missing ppid context information\n"); + } + if (procname_check == 0) { + ret = -1; + fprintf(stderr, "[error] missing procname context information\n"); + } + + 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) { + fprintf(stderr, "[error] Opening the trace\n"); + goto end; + } + + ret = check_requirements(bt_ctx); + if (ret < 0) { + fprintf(stderr, "[error] some mandatory contexts were missing, exiting.\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); + pthread_join(timer_thread, NULL); + +end: + bt_context_put(bt_ctx); + return 0; +} diff --git a/src/lttngtoptypes.h b/src/lttngtoptypes.h index c69ea01..3a75c9d 100644 --- a/src/lttngtoptypes.h +++ b/src/lttngtoptypes.h @@ -20,158 +20,6 @@ #include -struct lttngtop { - GPtrArray *process_table; /* struct processtop */ - GPtrArray *files_table; /* struct files */ - GPtrArray *cpu_table; /* struct cputime */ - 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; - /* Files managing */ - GPtrArray *process_files_table; - struct file_history *files_history; - GPtrArray *threads; - GHashTable *perf; - struct processtop *threadparent; - /* IO calculting */ - unsigned long totalfileread; - unsigned long totalfilewrite; - unsigned long fileread; - unsigned long filewrite; - struct syscalls *syscall_info; - unsigned long totalcpunsec; - unsigned long threadstotalcpunsec; -}; - -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; - int flag; - 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 file_history { - struct files *file; - struct file_history *next; -}; - -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; - uint64_t cpu_id; - unsigned int type; - unsigned int tid; - unsigned int fd; -}; - -struct signals { - int dest_pid; - int id; - unsigned long count; -}; - -struct file_info { - struct file_info *next; - char *name; - int fd; - int status; -}; - /* header for cputop display */ struct header_view { char *title;