From 715cf83c121d2aeac7ae030d49ff5a31bd6b9ac5 Mon Sep 17 00:00:00 2001 From: Pierre-Bernard Thiffault Date: Fri, 21 Dec 2012 13:39:17 -0500 Subject: [PATCH] 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 --- configure.ac | 1 + src/Makefile.am | 21 +- src/attributes.txt | 61 ++ src/common.c | 531 ---------- src/common.cpp | 1089 ++++++++++++++++++++ src/common.h | 148 ++- src/{cputop.c => cputop.cpp} | 63 +- src/cputop.h | 2 + src/{cursesdisplay.c => cursesdisplay.cpp} | 913 +++++++++------- src/cursesdisplay.h | 8 +- src/iostreamtop.c | 513 --------- src/iostreamtop.cpp | 540 ++++++++++ src/iostreamtop.h | 25 +- src/libStateIntegrationPrototype.cpp | 3 + src/libStateIntegrationPrototype.h | 31 + src/{lttngtop.c => lttngtop.cpp} | 258 +++-- src/lttngtoptypes.h | 152 --- 17 files changed, 2634 insertions(+), 1725 deletions(-) create mode 100644 src/attributes.txt delete mode 100644 src/common.c create mode 100644 src/common.cpp rename src/{cputop.c => cputop.cpp} (63%) rename src/{cursesdisplay.c => cursesdisplay.cpp} (58%) delete mode 100644 src/iostreamtop.c create mode 100644 src/iostreamtop.cpp create mode 100644 src/libStateIntegrationPrototype.cpp create mode 100644 src/libStateIntegrationPrototype.h rename src/{lttngtop.c => lttngtop.cpp} (71%) 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.cpp similarity index 63% rename from src/cputop.c rename to src/cputop.cpp index eb7afb3..4fde9a8 100644 --- a/src/cputop.c +++ b/src/cputop.cpp @@ -15,34 +15,65 @@ * 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) +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; + Quark cpu_quark; + Quark current_task_quark; + bool current_task_found; + int current_task_pid; + int current_task_tid; 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; + 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) - tmpcpu->current_task = get_proc(<tngtop, next_pid, next_comm, timestamp); + modify_attribute(timestamp, &cpu_quark, "current_task", + get_proc(next_pid, next_comm, timestamp)); else - tmpcpu->current_task = NULL; + nullify_attribute(timestamp, &cpu_quark, "current_task"); - tmpcpu->task_start = timestamp; + modify_attribute(timestamp, &cpu_quark, "task_start", timestamp); } enum bt_cb_ret handle_sched_switch(struct bt_ctf_event *call_data, @@ -127,7 +158,7 @@ enum bt_cb_ret handle_sched_process_free(struct bt_ctf_event *call_data, goto error; } - death_proc(<tngtop, tid, comm, timestamp); + death_proc(tid, comm, timestamp); return BT_CB_OK; 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.cpp similarity index 58% rename from src/cursesdisplay.c rename to src/cursesdisplay.cpp index beb7bdf..3cd273f 100644 --- a/src/cursesdisplay.c +++ b/src/cursesdisplay.cpp @@ -24,6 +24,16 @@ #include #include +#include +#include +#include +#include +#include +#include + +#define __STDC_FORMAT_MACROS +#include + #include "cursesdisplay.h" #include "lttngtoptypes.h" #include "iostreamtop.h" @@ -33,6 +43,11 @@ #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; @@ -45,9 +60,7 @@ 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; +Quark selected_process; int selected_ret; int selected_line = 0; /* select bar position */ @@ -70,6 +83,12 @@ 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); @@ -164,7 +183,7 @@ void print_digits(WINDOW *win, int first, int second) wprintw(win, ")"); } -void print_headers(int line, char *desc, int value, int first, int second) +void print_headers(int line, const char desc[], int value, int first, int second) { wattron(header, A_BOLD); mvwprintw(header, line, 4, "%s", desc); @@ -175,16 +194,16 @@ void print_headers(int line, char *desc, int value, int first, int second) wmove(header, line, 40); } -void set_window_title(WINDOW *win, char *title) +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(char *str) +void print_log(const char str[]) { - int i; + unsigned int i; int current_line = 1; int current_char = 1; char *tmp, *tmp2; @@ -221,43 +240,26 @@ void print_log(char *str) wrefresh(status); } -int process_selected(struct processtop *process) +int process_selected(Quark proc) { - 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; + int selected; + get_current_attribute_value_int(&proc, "selected", selected); + return selected; } void update_selected_processes() { - int i; - struct processtop *stored_process; - + Quark selected = state_system->getQuark(selected_process, "selected"); 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"); - } + state_system->updateCurrentState(selected, 0); + print_log("Process removed"); } else { - g_ptr_array_add(selected_processes, selected_process); + state_system->updateCurrentState(selected, 1); print_log("Process added"); } } -void print_key(WINDOW *win, char *key, char *desc, int toggle) +void print_key(WINDOW *win, const char key[], const char desc[], int toggle) { int pair; if (toggle > 0) @@ -333,14 +335,28 @@ static void scale_unit(uint64_t bytes, char *ret) 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; + 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; @@ -350,208 +366,324 @@ void update_header() { struct tm start, end; uint64_t ts_nsec_start, ts_nsec_end; - char io[4]; + char io[16]; + int cpu_nb; + int nbthreads, nbnewthreads, nbdeadthreads; + int nbfiles, nbnewfiles, nbdeadfiles; - ts_nsec_start = data->start % NSEC_PER_SEC; - start = format_timestamp(data->start); + ts_nsec_start = i_start % NSEC_PER_SEC; + start = format_timestamp(i_start); - ts_nsec_end = data->end % NSEC_PER_SEC; - end = format_timestamp(data->end); + 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 "[", + 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)); + + 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); } -gint sort_by_cpu_desc(gconstpointer p1, gconstpointer p2) +int sort_by_cpu_desc(const void *pp1, const void *pp2) { - 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) + 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 (totaln1 == totaln2) + if (total1 == total2) return 0; return -1; } -gint sort_by_tid_desc(gconstpointer p1, gconstpointer p2) +int sort_by_tid_desc(const void *pp1, const void *pp2) { - struct processtop *n1 = *(struct processtop **)p1; - struct processtop *n2 = *(struct processtop **)p2; - unsigned long totaln1 = n1->tid; - unsigned long totaln2 = n2->tid; + 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 (totaln1 < totaln2) + if (tid1 > tid2) return 1; - if (totaln1 == totaln2) + if (tid1 == tid2) return 0; return -1; } -gint sort_by_pid_desc(gconstpointer p1, gconstpointer p2) +int sort_by_pid_desc(const void *pp1, const void *pp2) { - struct processtop *n1 = *(struct processtop **)p1; - struct processtop *n2 = *(struct processtop **)p2; - unsigned long totaln1 = n1->pid; - unsigned long totaln2 = n2->pid; + 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 (totaln1 < totaln2) + if (pid1 > pid2) return 1; - if (totaln1 == totaln2) + if (pid1 == pid2) 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) +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 (totaln1 == totaln2) + if (total1 == total2) return 0; return -1; } -gint sort_by_process_write_desc(gconstpointer p1, gconstpointer p2) +int sort_by_process_write_desc(const void *pp1, const void *pp2) { - 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) + 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 (totaln1 == totaln2) + if (total1 == total2) return 0; return -1; } -gint sort_by_process_total_desc(gconstpointer p1, gconstpointer p2) +int sort_by_process_total_desc(const void *pp1, const void *pp2) { - 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) + 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 (totaln1 == totaln2) + if (total1 == total2) return 0; return -1; } -gint sort_by_file_read_desc(gconstpointer p1, gconstpointer p2) +int sort_by_file_read_desc(const void *pf1, const void *pf2) { - 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) + 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 (totaln1 == totaln2) + if (total1 == total2) return 0; return -1; } -gint sort_by_file_write_desc(gconstpointer p1, gconstpointer p2) +int sort_by_file_write_desc(const void *pf1, const void *pf2) { - 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) + 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 (totaln1 == totaln2) + if (total1 == total2) return 0; return -1; } -gint sort_by_file_fd_desc(gconstpointer p1, gconstpointer p2) +int sort_by_file_fd_desc(const void *pf1, const void *pf2) { - struct files *n1 = *(struct files **)p1; - struct files *n2 = *(struct files **)p2; - unsigned long totaln1; - unsigned long totaln2; + Quark f1 = *(Quark *)pf1; + Quark f2 = *(Quark *)pf2; + int fd1, fd2; - totaln1 = n1->fd; - totaln2 = n2->fd; + get_attribute_value_at_int(i_end, &f1, "file/fd", fd1); + get_attribute_value_at_int(i_end, &f2, "file/fd", fd2); - if (totaln1 < totaln2) + if (fd1 > fd2) return 1; - if (totaln1 == totaln2) + if (fd1 == fd2) return 0; return -1; } -gint sort_by_cpu_group_by_threads_desc(gconstpointer p1, gconstpointer p2) +int sort_by_cpu_group_by_threads_desc(const void *pp1, const void *pp2) { - 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) + 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 (totaln1 == totaln2) + if (total1 == total2) return 0; return -1; } void update_cputop_display() { - int i; + unsigned int i; int header_offset = 2; - struct processtop *tmp; 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; - elapsed = data->end - data->start; - maxcputime = elapsed * data->cpu_table->len / 100.0; + processes = g_new(Quark, proc_nb); + sequence_to_array(i_end, proc_root, processes, proc_nb); if (cputopview[0].sort == 1) - g_ptr_array_sort(data->process_table, sort_by_cpu_desc); + qsort(processes, proc_nb, sizeof(Quark), sort_by_cpu_desc); else if (cputopview[1].sort == 1) - g_ptr_array_sort(data->process_table, sort_by_pid_desc); + qsort(processes, proc_nb, sizeof(Quark), sort_by_pid_desc); else if (cputopview[2].sort == 1) - g_ptr_array_sort(data->process_table, sort_by_tid_desc); + qsort(processes, proc_nb, sizeof(Quark), sort_by_tid_desc); else if (cputopview[3].sort == 1) - g_ptr_array_sort(data->process_table, sort_by_cpu_desc); + qsort(processes, proc_nb, sizeof(Quark), sort_by_cpu_desc); else - g_ptr_array_sort(data->process_table, sort_by_cpu_desc); + qsort(processes, proc_nb, sizeof(Quark), sort_by_cpu_desc); set_window_title(center, "CPU Top"); wattron(center, A_BOLD); @@ -570,75 +702,81 @@ void update_cputop_display() 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) + 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(tmp)) { + if (process_selected(processes[i])) { wattron(center, COLOR_PAIR(6)); - mvwhline(center, current_line + header_offset, 1, ' ', COLS-3); + mvwhline(center, current_line + header_offset, 1, ' ', + COLS-3); } if (current_line == selected_line) { - selected_process = tmp; + selected_process = processes[i]; wattron(center, COLOR_PAIR(5)); - mvwhline(center, current_line + header_offset, 1, ' ', COLS-3); + 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", - tmp->totalcpunsec / maxcputime); + totalcpunsec / maxcputime); /* PID */ - mvwprintw(center, current_line + header_offset, 11, "%d", tmp->pid); + mvwprintw(center, current_line + header_offset, 11, "%d", pid); /* TID */ - mvwprintw(center, current_line + header_offset, 21, "%d", tmp->tid); + mvwprintw(center, current_line + header_offset, 21, "%d", tid); /* NAME */ - mvwprintw(center, current_line + header_offset, 31, "%s", tmp->comm); + 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); } -gint sort_perf(gconstpointer p1, gconstpointer p2, gpointer key) +int sort_perf(const void *pp1, const void *pp2) { - struct processtop *n1 = *(struct processtop **) p1; - struct processtop *n2 = *(struct processtop **) p2; + Quark p1 = *(Quark *)pp1; + Quark p2 = *(Quark *)pp2; + std::string key = sort_perf_key; + int total1; + int total2; + int tid1; + int tid2; - struct perfcounter *tmp1, *tmp2; - unsigned long totaln2 = 0; - unsigned long totaln1 = 0; - - if (!key) - return 0; + if (!get_attribute_value_at_int(i_end, &p1, "perf/" + key + "/count", + total1)) + total1 = 0; - tmp1 = g_hash_table_lookup(n1->perf, key); - if (!tmp1) - totaln1 = 0; - else - totaln1 = tmp1->count; + if (!get_attribute_value_at_int(i_end, &p2, "perf/" + key + "/count", + total2)) + total2 = 0; - tmp2 = g_hash_table_lookup(n2->perf, key); - if (!tmp2) - totaln2 = 0; - else - totaln2 = tmp2->count; - if (totaln1 < totaln2) + if (total1 < total2) return 1; - if (totaln1 == totaln2) { - totaln1 = n1->tid; - totaln2 = n2->tid; - if (totaln1 < totaln2) + 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(char *key, int line) +void print_key_title(const char key[], int line) { wattron(center, A_BOLD); mvwprintw(center, line, 1, "%s", key); @@ -650,56 +788,77 @@ void update_process_details() { unsigned long elapsed; double maxcputime; - struct processtop *tmp; - struct files *file_tmp; - int i, j = 0; + unsigned 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; + 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; - 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) { + 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++); - wprintw(center, "%d", tmp->pid); + get_attribute_value_at_int(i_end, &selected_process, "pid", pid); + wprintw(center, "%d", pid); print_key_title("PPID", line++); - wprintw(center, "%d", tmp->ppid); + get_attribute_value_at_int(i_end, &selected_process, "ppid", ppid); + wprintw(center, "%d", ppid); print_key_title("CPU", line++); - wprintw(center, "%1.2f %%", tmp->totalcpunsec/maxcputime); + get_attribute_value_at_ulong(i_end, &selected_process, "totalcpunsec", + totalcpunsec); + wprintw(center, "%1.2f %%", totalcpunsec/maxcputime); print_key_title("READ B/s", line++); - scale_unit(tmp->fileread, unit); + 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++); - scale_unit(tmp->filewrite, unit); + get_attribute_value_at_int(i_end, &selected_process, "filewrite", + filewrite); + scale_unit(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); + + 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++; @@ -717,56 +876,58 @@ void update_process_details() 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); - } + 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) - g_ptr_array_sort(newfilearray, sort_by_file_fd_desc); + qsort(files, file_nb, sizeof(Quark), sort_by_file_fd_desc); else if (fileview[1].sort == 1) - g_ptr_array_sort(newfilearray, sort_by_file_read_desc); + qsort(files, file_nb, sizeof(Quark), sort_by_file_read_desc); else if (fileview[2].sort == 1) - g_ptr_array_sort(newfilearray, sort_by_file_write_desc); + qsort(files, file_nb, sizeof(Quark), 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); + 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); - scale_unit(file_tmp->write, unit); + get_attribute_value_at_int(i_end, files + i, "write", write); + scale_unit(write, unit); mvwprintw(center, line + j, 21, "%s", unit); - snprintf(filename_buf, COLS - 25, "%s", file_tmp->name); + 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_ptr_array_free(newfilearray, TRUE); + g_free(global_perf); + g_free(files); } void update_perf() { - int i; + unsigned int i, j; int nblinedisplayed = 0; int current_line = 0; - struct processtop *tmp; int header_offset = 2; int perf_row = 40; - struct perfcounter *perfn1, *perfn2; - char *perf_key = NULL; int value; - GHashTableIter iter; - gpointer key; + 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); @@ -775,60 +936,78 @@ void update_perf() 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; */ + 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", - (char *) key + 5); + key.c_str() + 5); wattroff(center, A_UNDERLINE); perf_row += 20; } - if (perfn1->sort) { - perf_key = (char *) key; + if (sort_exists && sort) { + sort_perf_key = key; } } wattroff(center, A_BOLD); - g_ptr_array_sort_with_data(data->process_table, sort_perf, perf_key); + 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); - 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 (pid != tid) if (toggle_threads == -1) continue; - if (process_selected(tmp)) { + if (process_selected(procs[i])) { wattron(center, COLOR_PAIR(6)); - mvwhline(center, current_line + header_offset, 1, ' ', COLS-3); + mvwhline(center, current_line + header_offset, 1, ' ', + COLS-3); } if (current_line == selected_line) { - selected_process = tmp; + selected_process = procs[i]; wattron(center, COLOR_PAIR(5)); - mvwhline(center, current_line + header_offset, 1, ' ', COLS-3); + 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); + 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; - 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 + 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, "%d", value); perf_row += 20; } } @@ -838,18 +1017,23 @@ void update_perf() nblinedisplayed++; current_line++; } + g_free(global_perf); + g_free(procs); } void update_iostream() { - int i; + unsigned int i; int header_offset = 2; - struct processtop *tmp; int nblinedisplayed = 0; int current_line = 0; - int total = 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); @@ -869,50 +1053,71 @@ void update_iostream() 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) - g_ptr_array_sort(data->process_table, sort_by_process_read_desc); + qsort(procs, proc_nb, sizeof(Quark), sort_by_process_read_desc); else if (iostreamtopview[1].sort == 1) - g_ptr_array_sort(data->process_table, sort_by_process_write_desc); + qsort(procs, proc_nb, sizeof(Quark), + sort_by_process_write_desc); else if (iostreamtopview[2].sort == 1) - g_ptr_array_sort(data->process_table, sort_by_process_total_desc); + qsort(procs, proc_nb, sizeof(Quark), + 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) + 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(tmp)) { + if (process_selected(procs[i])) { wattron(center, COLOR_PAIR(6)); - mvwhline(center, current_line + header_offset, 1, ' ', COLS-3); + mvwhline(center, current_line + header_offset, 1, ' ', + COLS-3); } if (current_line == selected_line) { - selected_process = tmp; + selected_process = procs[i]; wattron(center, COLOR_PAIR(5)); - mvwhline(center, current_line + header_offset, 1, ' ', COLS-3); + 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); + 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", tmp->comm); + mvwprintw(center, current_line + header_offset, 22, "%s", + comm.c_str()); /* READ (bytes/sec) */ - scale_unit(tmp->fileread, unit); + scale_unit(read, unit); mvwprintw(center, current_line + header_offset, 40, "%s", unit); /* WRITE (bytes/sec) */ - scale_unit(tmp->filewrite, unit); + scale_unit(write, unit); mvwprintw(center, current_line + header_offset, 52, "%s", unit); /* TOTAL STREAM */ - total = tmp->totalfileread + tmp->totalfilewrite; - - scale_unit(total, unit); + scale_unit(totalread + totalwrite, unit); mvwprintw(center, current_line + header_offset, 64, "%s", unit); wattroff(center, COLOR_PAIR(6)); @@ -920,13 +1125,12 @@ void update_iostream() nblinedisplayed++; current_line++; } + g_free(procs); } void update_current_view() { sem_wait(&update_display_sem); - if (!data) - return; update_header(); werase(center); @@ -979,8 +1183,6 @@ void update_process_detail_pref(int *line_selected, int toggle_view, int toggle_ int i; int size; - if (!data) - return; if (pref_panel_window) { del_panel(pref_panel); delwin(pref_panel_window); @@ -1047,8 +1249,6 @@ 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); @@ -1117,8 +1317,6 @@ 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); @@ -1165,43 +1363,45 @@ void update_cpu_pref(int *line_selected, int toggle_view, int toggle_sort) void update_perf_sort(int *line_selected) { int i; - struct perfcounter *perf; - GList *perflist; 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); - 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); + for (i = 0; i < size; i++) { if (i != *line_selected) - perf->sort = 0; + modify_attribute(i_end, global_perf + i, "sort", 0); else - perf->sort = 1; - i++; - perflist = g_list_next(perflist); + 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; - struct perfcounter *perf; - GList *perflist; int size; + Quark *global_perf; + int visible; + int sort; + std::string key; - if (!data) - return; if (pref_panel_window) { del_panel(pref_panel); delwin(pref_panel_window); } - size = g_hash_table_size(global_perf_liszt); + + 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); @@ -1210,7 +1410,7 @@ void update_perf_pref(int *line_selected, int toggle_view, int toggle_sort) 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, + mvwprintw(pref_panel_window, size + 1, 1, " 's' : sort, space : toggle"); wattroff(pref_panel_window, A_BOLD); @@ -1224,30 +1424,35 @@ void update_perf_pref(int *line_selected, int toggle_view, int toggle_sort) 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); + 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) { - perf->visible = perf->visible == 1 ? 0: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 (perf->sort == 1) + + 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", - perf->visible == 1 ? 'x' : ' ', - (char *) perflist->data + 5); + visible == 1 ? 'x' : ' ', key.c_str() + 5); wattroff(pref_panel_window, A_BOLD); wattroff(pref_panel_window, COLOR_PAIR(5)); - i++; - perflist = g_list_next(perflist); } update_panels(); doupdate(); + + g_free(global_perf); } int update_preference_panel(int *line_selected, int toggle_view, int toggle_sort) @@ -1319,14 +1524,16 @@ void toggle_pref_panel(void) doupdate(); } -void display(unsigned int index) +void display() { - last_display_index = index; - currently_displayed_index = index; - data = g_ptr_array_index(copies, index); - if (!data) - return; - max_elements = data->process_table->len; + 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(); @@ -1395,14 +1602,16 @@ void *handle_keyboard(void *p) /* Navigate the history with arrows */ case KEY_LEFT: - if (currently_displayed_index > 0) { - currently_displayed_index--; + 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"); } - data = g_ptr_array_index(copies, currently_displayed_index); - max_elements = data->process_table->len; + 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) @@ -1412,13 +1621,16 @@ void *handle_keyboard(void *p) 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(); + 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); @@ -1533,8 +1745,7 @@ void *handle_keyboard(void *p) update_current_view(); break; default: - if (data) - update_current_view(); + update_current_view(); break; } update_footer(); 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.cpp similarity index 71% rename from src/lttngtop.c rename to src/lttngtop.cpp index 2ee303a..43afe48 100644 --- a/src/lttngtop.c +++ b/src/lttngtop.cpp @@ -15,14 +15,15 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#define _GNU_SOURCE #include #include #include +extern "C" { #include #include #include #include +} #include #include #include @@ -41,20 +42,18 @@ #include "lttngtoptypes.h" #include "cputop.h" #include "iostreamtop.h" -#include "cursesdisplay.h" #include "common.h" +#include "cursesdisplay.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; +std::string history_file; enum { OPT_NONE = 0, @@ -85,8 +84,6 @@ void *refresh_thread(void *p) void *ncurses_display(void *p) { - unsigned int current_display_index = 0; - sem_wait(&bootstrap); /* * Prevent the 1 second delay when we hit ESC @@ -99,9 +96,7 @@ void *ncurses_display(void *p) sem_wait(&goodtodisplay); sem_wait(&pause_sem); - copy = g_ptr_array_index(copies, current_display_index); - assert(copy); - display(current_display_index++); + display(); sem_post(&goodtoupdate); sem_post(&pause_sem); @@ -113,11 +108,38 @@ void *ncurses_display(void *p) } } +/* + * 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) +enum bt_cb_ret check_timestamp(struct bt_ctf_event *call_data, + void *private_data) { unsigned long timestamp; @@ -128,10 +150,13 @@ enum bt_cb_ret check_timestamp(struct bt_ctf_event *call_data, void *private_dat 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); - g_ptr_array_add(copies, get_copy_lttngtop(last_display_update, - timestamp)); + create_intervals(timestamp); + update_state_on_refresh(last_display_update, timestamp); sem_post(&goodtodisplay); sem_post(&bootstrap); last_display_update = timestamp; @@ -145,62 +170,51 @@ error: /* * 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) + * either a process or a cpu (specified by root) */ -struct perfcounter *get_perf_counter(const char *name, struct processtop *proc, - struct cputime *cpu) +Quark get_perf_counter(unsigned long timestamp, Quark root, std::string name) { - 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; + 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(struct processtop *proc, struct cputime *cpu, - const char *name, int value) +void update_perf_value(unsigned long timestamp, Quark proc, Quark cpu, + std::string 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; + 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(const struct bt_ctf_event *event, - const struct definition *scope, - struct processtop *proc, - struct cputime *cpu) +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; - struct perfcounter *perfcounter; - GHashTableIter iter; - gpointer key; int ret; + std::string key; + Quark perf_quark; if (!scope) goto end; @@ -212,44 +226,51 @@ void extract_perf_counter_scope(const struct bt_ctf_event *event, 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); - } + 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(struct processtop *proc, const struct bt_ctf_event *event) +void update_perf_counter(unsigned long timestamp, Quark proc, + const struct bt_ctf_event *event) { - struct cputime *cpu; + Quark cpu; const struct definition *scope; - cpu = get_cpu(get_cpu_id(event)); + cpu = get_cpu(get_cpu_id(event), timestamp); scope = bt_ctf_get_top_level_scope(event, BT_STREAM_EVENT_CONTEXT); - extract_perf_counter_scope(event, scope, proc, cpu); + 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(event, scope, proc, cpu); + extract_perf_counter_scope(timestamp, event, scope, proc, cpu); scope = bt_ctf_get_top_level_scope(event, BT_EVENT_CONTEXT); - extract_perf_counter_scope(event, scope, proc, cpu); + 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; + void *private_data) +{ int pid, tid, ppid; char *comm; - struct processtop *parent, *child; + Quark parent, child; unsigned long timestamp; timestamp = bt_ctf_get_timestamp(call_data); @@ -257,15 +278,15 @@ enum bt_cb_ret fix_process_table(struct bt_ctf_event *call_data, goto error; pid = get_context_pid(call_data); - if (pid == -1ULL) { + if ((unsigned long)pid == -1ULL) { goto error; } tid = get_context_tid(call_data); - if (tid == -1ULL) { + if ((unsigned long)tid == -1ULL) { goto error; } ppid = get_context_ppid(call_data); - if (ppid == -1ULL) { + if ((unsigned long)ppid == -1ULL) { goto error; } comm = get_context_comm(call_data); @@ -274,25 +295,23 @@ enum bt_cb_ret fix_process_table(struct bt_ctf_event *call_data, } /* 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 (!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 */ - parent = find_process_tid(<tngtop, pid, comm); - if (!parent) { - parent = add_proc(<tngtop, pid, comm, timestamp); - parent->pid = pid; + 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 */ - child->threadparent = parent; - add_thread(parent, child); + modify_attribute(timestamp, &parent, "threadparent", parent); + add_thread(timestamp, parent, child); } - update_perf_counter(child, call_data); + update_perf_counter(timestamp, child, call_data); return BT_CB_OK; @@ -302,9 +321,6 @@ error: 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); @@ -312,14 +328,34 @@ void init_lttngtop() 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(); + // 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) @@ -376,7 +412,7 @@ void iter_trace(struct bt_context *bt_ctx) const struct bt_ctf_event *event; int ret = 0; - begin_pos.type = BT_SEEK_BEGIN; + 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 */ @@ -534,8 +570,10 @@ static int check_field_requirements(const struct bt_ctf_field_decl *const * fiel int *procname_check, int *ppid_check) { int j; - struct perfcounter *global; 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]); @@ -556,14 +594,16 @@ static int check_field_requirements(const struct bt_ctf_field_decl *const * fiel (*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); + 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 (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 (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); } } } 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; -- 2.34.1