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 <pbthiffault@gmail.com>
Signed-off-by: Julien Desfossez <jdesfossez@efficios.com>
# Checks for programs.
AC_PROG_CC
+AC_PROG_CXX
AC_PROG_MAKE_SET
LT_INIT
AC_PROG_YACC
AM_CFLAGS = $(PACKAGE_CFLAGS)
+AM_CXXFLAGS = $(PACKAGE_CFLAGS)
bin_PROGRAMS = lttngtop
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
--- /dev/null
+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
+
+++ /dev/null
-/*
- * 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 <babeltrace/ctf/events.h>
-#include <stdlib.h>
-#include <linux/unistd.h>
-#include <string.h>
-#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;
-}
--- /dev/null
+/*
+ * 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 <babeltrace/ctf/events.h>
+}
+#include <stdlib.h>
+#include <linux/unistd.h>
+#include <string.h>
+#include "common.h"
+
+StateSystem *state_system;
+std::set<Quark> 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<IntegerStateValue>(
+ 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<IntegerStateValue>(
+ state_system->getCurrentStateValue(ql));
+ value_ptr_h = std::tr1::dynamic_pointer_cast<IntegerStateValue>(
+ 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<QuarkStateValue>(
+ 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<IntegerStateValue>(
+ 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<StringStateValue>(
+ 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<IntegerStateValue>(
+ 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<IntegerStateValue>(
+ interval_l.value);
+ value_ptr_h = std::tr1::dynamic_pointer_cast<IntegerStateValue>(
+ 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<QuarkStateValue>(
+ 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<IntegerStateValue>(
+ 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<StringStateValue>(
+ 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<std::string, int> perfs;
+ std::string key;
+ int val;
+ int ret;
+ Quark proc;
+ Quark perf;
+ std::map<std::string, int>::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<std::string> 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;
+}
#define _COMMON_H
#include <semaphore.h>
+extern "C" {
#include <babeltrace/ctf/events.h>
+}
+#include <state/IntervalHistoryProvider.hpp>
+#include <state/StateSystem.hpp>
+#include <state/statevalue/StateValue.hpp>
+#include <state/statevalue/IntegerStateValue.hpp>
+#include <state/statevalue/StringStateValue.hpp>
+#include <state/statevalue/NullStateValue.hpp>
+#include <state/statevalue/QuarkStateValue.hpp>
+#include <sstream>
+#include <map>
+#include <vector>
+#include <set>
+
#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<Quark> 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 */
+++ /dev/null
-/*
- * 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 <babeltrace/babeltrace.h>
-
-#include "lttngtoptypes.h"
-#include "common.h"
-#include "cputop.h"
-
-void update_cputop_data(unsigned long timestamp, int64_t cpu, int prev_pid,
- int next_pid, char *prev_comm, char *next_comm)
-{
- struct cputime *tmpcpu;
- unsigned long elapsed;
-
- tmpcpu = get_cpu(cpu);
-
- if (tmpcpu->current_task && tmpcpu->current_task->pid == prev_pid) {
- elapsed = timestamp - tmpcpu->task_start;
- tmpcpu->current_task->totalcpunsec += elapsed;
- tmpcpu->current_task->threadstotalcpunsec += elapsed;
- if (tmpcpu->current_task->pid != tmpcpu->current_task->tid)
- tmpcpu->current_task->threadparent->threadstotalcpunsec += elapsed;
- }
-
- if (next_pid != 0)
- tmpcpu->current_task = get_proc(<tngtop, next_pid, next_comm, timestamp);
- else
- tmpcpu->current_task = NULL;
-
- tmpcpu->task_start = timestamp;
-}
-
-enum bt_cb_ret handle_sched_switch(struct bt_ctf_event *call_data,
- void *private_data)
-{
- const struct definition *scope;
- unsigned long timestamp;
- uint64_t cpu_id;
- char *prev_comm, *next_comm;
- int prev_tid, next_tid;
-
- timestamp = bt_ctf_get_timestamp(call_data);
- if (timestamp == -1ULL)
- goto error;
-
- scope = bt_ctf_get_top_level_scope(call_data,
- BT_EVENT_FIELDS);
- prev_comm = bt_ctf_get_char_array(bt_ctf_get_field(call_data,
- scope, "_prev_comm"));
- if (bt_ctf_field_get_error()) {
- fprintf(stderr, "Missing prev_comm context info\n");
- goto error;
- }
-
- next_comm = bt_ctf_get_char_array(bt_ctf_get_field(call_data,
- scope, "_next_comm"));
- if (bt_ctf_field_get_error()) {
- fprintf(stderr, "Missing next_comm context info\n");
- goto error;
- }
-
- prev_tid = bt_ctf_get_int64(bt_ctf_get_field(call_data,
- scope, "_prev_tid"));
- if (bt_ctf_field_get_error()) {
- fprintf(stderr, "Missing prev_tid context info\n");
- goto error;
- }
-
- next_tid = bt_ctf_get_int64(bt_ctf_get_field(call_data,
- scope, "_next_tid"));
- if (bt_ctf_field_get_error()) {
- fprintf(stderr, "Missing next_tid context info\n");
- goto error;
- }
-
- cpu_id = get_cpu_id(call_data);
-
- update_cputop_data(timestamp, cpu_id, prev_tid, next_tid,
- prev_comm, next_comm);
-
- return BT_CB_OK;
-
-error:
- return BT_CB_ERROR_STOP;
-}
-
-enum bt_cb_ret handle_sched_process_free(struct bt_ctf_event *call_data,
- void *private_data)
-{
- const struct definition *scope;
- unsigned long timestamp;
- char *comm;
- int tid;
-
- timestamp = bt_ctf_get_timestamp(call_data);
- if (timestamp == -1ULL)
- goto error;
-
- scope = bt_ctf_get_top_level_scope(call_data,
- BT_EVENT_FIELDS);
- comm = bt_ctf_get_char_array(bt_ctf_get_field(call_data,
- scope, "_comm"));
- if (bt_ctf_field_get_error()) {
- fprintf(stderr, "Missing procname context info\n");
- goto error;
- }
-
- tid = bt_ctf_get_int64(bt_ctf_get_field(call_data,
- scope, "_tid"));
- if (bt_ctf_field_get_error()) {
- fprintf(stderr, "Missing tid field\n");
- goto error;
- }
-
- death_proc(<tngtop, tid, comm, timestamp);
-
- return BT_CB_OK;
-
-error:
- return BT_CB_ERROR_STOP;
-
-}
-
--- /dev/null
+/*
+ * 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 <babeltrace/babeltrace.h>
+}
+
+#include "lttngtoptypes.h"
+#include "common.h"
+#include "cputop.h"
+
+void update_cputop_data(unsigned long timestamp, int64_t cpu,
+ int prev_pid, int next_pid, char *prev_comm, char *next_comm)
+{
+ Quark cpu_quark;
+ Quark current_task_quark;
+ bool current_task_found;
+ int current_task_pid;
+ int current_task_tid;
+ unsigned long elapsed;
+ unsigned long task_start;
+ bool current_task_threadparent_found;
+ Quark current_task_threadparent;
+
+ cpu_quark = get_cpu(cpu, timestamp);
+ current_task_found = get_current_attribute_value_quark(&cpu_quark,
+ "current_task", current_task_quark);
+
+ if (current_task_found) {
+ get_current_attribute_value_int(¤t_task_quark, "pid",
+ current_task_pid);
+ if (current_task_pid == prev_pid) {
+ get_current_attribute_value_ulong(&cpu_quark,
+ "task_start", task_start);
+ elapsed = timestamp - task_start;
+ increase_attribute(timestamp, ¤t_task_quark,
+ "totalcpunsec", elapsed);
+ increase_attribute(timestamp, ¤t_task_quark,
+ "threadstotalcpunsec", elapsed);
+ get_current_attribute_value_int(¤t_task_quark,
+ "tid", current_task_tid);
+ if (current_task_tid != current_task_pid) {
+ current_task_threadparent_found =
+ get_current_attribute_value_quark(
+ ¤t_task_quark,
+ "threadparent",
+ current_task_threadparent);
+ if (current_task_threadparent_found)
+ increase_attribute(timestamp,
+ ¤t_task_threadparent,
+ "threadstotalcpuns", elapsed);
+ }
+ }
+ }
+
+ if (next_pid != 0)
+ modify_attribute(timestamp, &cpu_quark, "current_task",
+ get_proc(next_pid, next_comm, timestamp));
+ else
+ nullify_attribute(timestamp, &cpu_quark, "current_task");
+
+ modify_attribute(timestamp, &cpu_quark, "task_start", timestamp);
+}
+
+enum bt_cb_ret handle_sched_switch(struct bt_ctf_event *call_data,
+ void *private_data)
+{
+ const struct definition *scope;
+ unsigned long timestamp;
+ uint64_t cpu_id;
+ char *prev_comm, *next_comm;
+ int prev_tid, next_tid;
+
+ timestamp = bt_ctf_get_timestamp(call_data);
+ if (timestamp == -1ULL)
+ goto error;
+
+ scope = bt_ctf_get_top_level_scope(call_data,
+ BT_EVENT_FIELDS);
+ prev_comm = bt_ctf_get_char_array(bt_ctf_get_field(call_data,
+ scope, "_prev_comm"));
+ if (bt_ctf_field_get_error()) {
+ fprintf(stderr, "Missing prev_comm context info\n");
+ goto error;
+ }
+
+ next_comm = bt_ctf_get_char_array(bt_ctf_get_field(call_data,
+ scope, "_next_comm"));
+ if (bt_ctf_field_get_error()) {
+ fprintf(stderr, "Missing next_comm context info\n");
+ goto error;
+ }
+
+ prev_tid = bt_ctf_get_int64(bt_ctf_get_field(call_data,
+ scope, "_prev_tid"));
+ if (bt_ctf_field_get_error()) {
+ fprintf(stderr, "Missing prev_tid context info\n");
+ goto error;
+ }
+
+ next_tid = bt_ctf_get_int64(bt_ctf_get_field(call_data,
+ scope, "_next_tid"));
+ if (bt_ctf_field_get_error()) {
+ fprintf(stderr, "Missing next_tid context info\n");
+ goto error;
+ }
+
+ cpu_id = get_cpu_id(call_data);
+
+ update_cputop_data(timestamp, cpu_id, prev_tid, next_tid,
+ prev_comm, next_comm);
+
+ return BT_CB_OK;
+
+error:
+ return BT_CB_ERROR_STOP;
+}
+
+enum bt_cb_ret handle_sched_process_free(struct bt_ctf_event *call_data,
+ void *private_data)
+{
+ const struct definition *scope;
+ unsigned long timestamp;
+ char *comm;
+ int tid;
+
+ timestamp = bt_ctf_get_timestamp(call_data);
+ if (timestamp == -1ULL)
+ goto error;
+
+ scope = bt_ctf_get_top_level_scope(call_data,
+ BT_EVENT_FIELDS);
+ comm = bt_ctf_get_char_array(bt_ctf_get_field(call_data,
+ scope, "_comm"));
+ if (bt_ctf_field_get_error()) {
+ fprintf(stderr, "Missing procname context info\n");
+ goto error;
+ }
+
+ tid = bt_ctf_get_int64(bt_ctf_get_field(call_data,
+ scope, "_tid"));
+ if (bt_ctf_field_get_error()) {
+ fprintf(stderr, "Missing tid field\n");
+ goto error;
+ }
+
+ death_proc(tid, comm, timestamp);
+
+ return BT_CB_OK;
+
+error:
+ return BT_CB_ERROR_STOP;
+
+}
+
#ifndef _LTTNGTOP_H
#define _LTTNGTOP_H
+extern "C" {
#include <babeltrace/babeltrace.h>
#include <babeltrace/ctf/callbacks.h>
+}
#include <inttypes.h>
#include <glib.h>
+++ /dev/null
-/*
- * 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 <stdio.h>
-#include <stdlib.h>
-#include <signal.h>
-#include <string.h>
-#include <ncurses.h>
-#include <panel.h>
-#include <pthread.h>
-#include <semaphore.h>
-
-#include "cursesdisplay.h"
-#include "lttngtoptypes.h"
-#include "iostreamtop.h"
-#include "common.h"
-
-#define DEFAULT_DELAY 15
-#define MAX_LINE_LENGTH 50
-#define MAX_LOG_LINES 4
-
-/* to prevent concurrent updates of the different windows */
-sem_t update_display_sem;
-
-char *termtype;
-WINDOW *footer, *header, *center, *status;
-WINDOW *pref_panel_window = NULL;
-PANEL *pref_panel, *main_panel;
-
-int pref_panel_visible = 0;
-int pref_line_selected = 0;
-int pref_current_sort = 0;
-
-int last_display_index, currently_displayed_index;
-
-struct processtop *selected_process = NULL;
-int selected_ret;
-
-int selected_line = 0; /* select bar position */
-int selected_in_list = 0; /* selection relative to the whole list */
-int list_offset = 0; /* first index in the list to display (scroll) */
-int nb_log_lines = 0;
-char log_lines[MAX_LINE_LENGTH * MAX_LOG_LINES + MAX_LOG_LINES];
-
-int max_elements = 80;
-
-int toggle_threads = 1;
-int toggle_pause = -1;
-
-int max_center_lines;
-GPtrArray *selected_processes;
-
-pthread_t keyboard_thread;
-
-struct header_view cputopview[4];
-struct header_view iostreamtopview[3];
-struct header_view fileview[3];
-
-void reset_ncurses()
-{
- curs_set(1);
- endwin();
- exit(0);
-}
-
-static void handle_sigterm(int signal)
-{
- reset_ncurses();
-}
-
-void init_screen()
-{
- initscr();
- noecho();
- halfdelay(DEFAULT_DELAY);
- nonl();
- intrflush(stdscr, false);
- keypad(stdscr, true);
- curs_set(0);
-
- if (has_colors()) {
- start_color();
- init_pair(1, COLOR_RED, COLOR_BLACK); /* - */
- init_pair(2, COLOR_GREEN, COLOR_BLACK); /* + */
- init_pair(3, COLOR_BLACK, COLOR_WHITE); /* keys */
- init_pair(4, COLOR_WHITE, COLOR_GREEN); /* keys activated */
- init_pair(5, COLOR_WHITE, COLOR_BLUE); /* select line */
- init_pair(6, COLOR_WHITE, COLOR_GREEN); /* selected process */
- }
- termtype = getenv("TERM");
- if (!strcmp(termtype, "xterm") || !strcmp(termtype, "xterm-color") ||
- !strcmp(termtype, "vt220")) {
- define_key("\033[H", KEY_HOME);
- define_key("\033[F", KEY_END);
- define_key("\033OP", KEY_F(1));
- define_key("\033OQ", KEY_F(2));
- define_key("\033OR", KEY_F(3));
- define_key("\033OS", KEY_F(4));
- define_key("\0330U", KEY_F(6));
- define_key("\033[11~", KEY_F(1));
- define_key("\033[12~", KEY_F(2));
- define_key("\033[13~", KEY_F(3));
- define_key("\033[14~", KEY_F(4));
- define_key("\033[16~", KEY_F(6));
- define_key("\033[17;2~", KEY_F(18));
- }
- signal(SIGTERM, handle_sigterm);
- mousemask(BUTTON1_CLICKED, NULL);
- refresh();
-}
-
-WINDOW *create_window(int height, int width, int startx, int starty)
-{
- WINDOW *win;
- win = newwin(height, width, startx, starty);
- box(win, 0 , 0);
- wrefresh(win);
- return win;
-}
-
-WINDOW *create_window_no_border(int height, int width, int startx, int starty)
-{
- WINDOW *win;
- win = newwin(height, width, startx, starty);
- wrefresh(win);
- return win;
-}
-
-void print_digit(WINDOW *win, int digit)
-{
- if (digit < 0) {
- wattron(win, COLOR_PAIR(1));
- wprintw(win, "%d", digit);
- wattroff(win, COLOR_PAIR(1));
- } else if (digit > 0) {
- wattron(win, COLOR_PAIR(2));
- wprintw(win, "+%d", digit);
- wattroff(win, COLOR_PAIR(2));
- } else {
- wprintw(win, "0");
- }
-}
-
-void print_digits(WINDOW *win, int first, int second)
-{
- wprintw(win, "(");
- print_digit(win, first);
- wprintw(win, ", ");
- print_digit(win, second);
- wprintw(win, ")");
-}
-
-void print_headers(int line, char *desc, int value, int first, int second)
-{
- wattron(header, A_BOLD);
- mvwprintw(header, line, 4, "%s", desc);
- wattroff(header, A_BOLD);
- mvwprintw(header, line, 16, "%d", value);
- wmove(header, line, 24);
- print_digits(header, first, second);
- wmove(header, line, 40);
-}
-
-void set_window_title(WINDOW *win, char *title)
-{
- wattron(win, A_BOLD);
- mvwprintw(win, 0, 1, title);
- wattroff(win, A_BOLD);
-}
-
-void print_log(char *str)
-{
- int i;
- int current_line = 1;
- int current_char = 1;
- char *tmp, *tmp2;
- /* rotate the line buffer */
- if (nb_log_lines >= MAX_LOG_LINES) {
- tmp = strndup(log_lines, MAX_LINE_LENGTH * MAX_LOG_LINES + MAX_LOG_LINES);
- tmp2 = strchr(tmp, '\n');
- memset(log_lines, '\0', strlen(log_lines));
- strncat(log_lines, tmp2 + 1, strlen(tmp2) - 1);
- log_lines[strlen(log_lines)] = '\n';
- log_lines[strlen(log_lines)] = '\0';
- free(tmp);
- }
- nb_log_lines++;
-
- strncat(log_lines, str, MAX_LINE_LENGTH - 1);
-
- if (nb_log_lines < MAX_LOG_LINES)
- log_lines[strlen(log_lines)] = '\n';
- log_lines[strlen(log_lines)] = '\0';
-
- werase(status);
- box(status, 0 , 0);
- set_window_title(status, "Status");
- for (i = 0; i < strlen(log_lines); i++) {
- if (log_lines[i] == '\n') {
- wmove(status, ++current_line, 1);
- current_char = 1;
- } else {
- mvwprintw(status, current_line, current_char++, "%c",
- log_lines[i]);
- }
- }
- wrefresh(status);
-}
-
-int process_selected(struct processtop *process)
-{
- int i;
- struct processtop *stored_process;
-
- for (i = 0; i < selected_processes->len; i++) {
- stored_process = g_ptr_array_index(selected_processes, i);
- if (!stored_process)
- return 0;
- if (stored_process->tid == process->tid)
- return 1;
- }
- return 0;
-}
-
-void update_selected_processes()
-{
- int i;
- struct processtop *stored_process;
-
- if (process_selected(selected_process)) {
- for (i = 0; i < selected_processes->len; i++) {
- stored_process = g_ptr_array_index(selected_processes, i);
- if (!stored_process)
- return;
- if (stored_process->tid == selected_process->tid)
- g_ptr_array_remove(selected_processes,
- stored_process);
- print_log("Process removed");
- }
- } else {
- g_ptr_array_add(selected_processes, selected_process);
- print_log("Process added");
- }
-}
-
-void print_key(WINDOW *win, char *key, char *desc, int toggle)
-{
- int pair;
- if (toggle > 0)
- pair = 4;
- else
- pair = 3;
- wattron(win, COLOR_PAIR(pair));
- wprintw(footer, "%s", key);
- wattroff(win, COLOR_PAIR(pair));
- wprintw(footer, ":%s", desc);
-}
-
-void update_footer()
-{
- sem_wait(&update_display_sem);
- werase(footer);
- wmove(footer, 1, 1);
- print_key(footer, "F2", "CPUtop ", current_view == cpu);
- print_key(footer, "F3", "PerfTop ", current_view == perf);
- print_key(footer, "F4", "IOTop ", current_view == iostream);
- print_key(footer, "Enter", "Details ", current_view == process_details);
- print_key(footer, "Space", "Highlight ", 0);
- print_key(footer, "q", "Quit ", 0);
- print_key(footer, "r", "Pref ", 0);
- print_key(footer, "t", "Threads ", toggle_threads);
- print_key(footer, "p", "Pause ", toggle_pause);
-
- wrefresh(footer);
- sem_post(&update_display_sem);
-}
-
-void basic_header()
-{
- werase(header);
- box(header, 0 , 0);
- set_window_title(header, "Statistics for interval [gathering data...[");
- wattron(header, A_BOLD);
- mvwprintw(header, 1, 4, "CPUs");
- mvwprintw(header, 2, 4, "Threads");
- mvwprintw(header, 3, 4, "FDs");
- wattroff(header, A_BOLD);
- wrefresh(header);
-}
-
-struct tm format_timestamp(uint64_t timestamp)
-{
- struct tm tm;
- uint64_t ts_sec = 0, ts_nsec;
- time_t time_s;
-
- ts_nsec = timestamp;
- ts_sec += ts_nsec / NSEC_PER_SEC;
- ts_nsec = ts_nsec % NSEC_PER_SEC;
-
- time_s = (time_t) ts_sec;
-
- localtime_r(&time_s, &tm);
-
- return tm;
-}
-
-static void scale_unit(uint64_t bytes, char *ret)
-{
- if (bytes >= 1000000000)
- sprintf(ret, "%" PRIu64 "G", bytes/1000000000);
- if (bytes >= 1000000)
- sprintf(ret, "%" PRIu64 "M", bytes/1000000);
- else if (bytes >= 1000)
- sprintf(ret, "%" PRIu64 "K", bytes/1000);
- else
- sprintf(ret, "%" PRIu64, bytes);
-}
-
-uint64_t total_io()
-{
- int i;
- struct processtop *tmp;
- uint64_t total = 0;
-
- for (i = 0; i < data->process_table->len; i++) {
- tmp = g_ptr_array_index(data->process_table, i);
- total += tmp->fileread;
- total += tmp->filewrite;
- }
-
- return total;
-}
-
-void update_header()
-{
- struct tm start, end;
- uint64_t ts_nsec_start, ts_nsec_end;
- char io[4];
-
- ts_nsec_start = data->start % NSEC_PER_SEC;
- start = format_timestamp(data->start);
-
- ts_nsec_end = data->end % NSEC_PER_SEC;
- end = format_timestamp(data->end);
-
- werase(header);
- box(header, 0 , 0);
- set_window_title(header, "Statistics for interval ");
- wattron(header, A_BOLD);
-
- wprintw(header, "[%02d:%02d:%02d.%09" PRIu64 ", %02d:%02d:%02d.%09" PRIu64 "[",
- start.tm_hour, start.tm_min, start.tm_sec, ts_nsec_start,
- end.tm_hour, end.tm_min, end.tm_sec, ts_nsec_end);
- mvwprintw(header, 1, 4, "CPUs");
- wattroff(header, A_BOLD);
- wprintw(header, "\t%d\t(max/cpu : %0.2f%)", data->cpu_table->len,
- 100.0/data->cpu_table->len);
- print_headers(2, "Threads", data->nbthreads, data->nbnewthreads,
- -1*(data->nbdeadthreads));
- print_headers(3, "FDs", data->nbfiles, data->nbnewfiles,
- -1*(data->nbclosedfiles));
- scale_unit(total_io(), io);
- mvwprintw(header, 3, 43, "%sB/sec", io);
- wrefresh(header);
-}
-
-gint sort_by_cpu_desc(gconstpointer p1, gconstpointer p2)
-{
- struct processtop *n1 = *(struct processtop **)p1;
- struct processtop *n2 = *(struct processtop **)p2;
- unsigned long totaln1 = n1->totalcpunsec;
- unsigned long totaln2 = n2->totalcpunsec;
-
- if (totaln1 < totaln2)
- return 1;
- if (totaln1 == totaln2)
- return 0;
- return -1;
-}
-
-gint sort_by_tid_desc(gconstpointer p1, gconstpointer p2)
-{
- struct processtop *n1 = *(struct processtop **)p1;
- struct processtop *n2 = *(struct processtop **)p2;
- unsigned long totaln1 = n1->tid;
- unsigned long totaln2 = n2->tid;
-
- if (totaln1 < totaln2)
- return 1;
- if (totaln1 == totaln2)
- return 0;
- return -1;
-}
-
-gint sort_by_pid_desc(gconstpointer p1, gconstpointer p2)
-{
- struct processtop *n1 = *(struct processtop **)p1;
- struct processtop *n2 = *(struct processtop **)p2;
- unsigned long totaln1 = n1->pid;
- unsigned long totaln2 = n2->pid;
-
- if (totaln1 < totaln2)
- return 1;
- if (totaln1 == totaln2)
- return 0;
- return -1;
-}
-
-gint sort_by_process_read_desc(gconstpointer p1, gconstpointer p2)
-{
- struct processtop *n1 = *(struct processtop **)p1;
- struct processtop *n2 = *(struct processtop **)p2;
- unsigned long totaln1 = n1->fileread;
- unsigned long totaln2 = n2->fileread;
-
- if (totaln1 < totaln2)
- return 1;
- if (totaln1 == totaln2)
- return 0;
- return -1;
-}
-
-gint sort_by_process_write_desc(gconstpointer p1, gconstpointer p2)
-{
- struct processtop *n1 = *(struct processtop **)p1;
- struct processtop *n2 = *(struct processtop **)p2;
- unsigned long totaln1 = n1->filewrite;
- unsigned long totaln2 = n2->filewrite;
-
- if (totaln1 < totaln2)
- return 1;
- if (totaln1 == totaln2)
- return 0;
- return -1;
-}
-
-gint sort_by_process_total_desc(gconstpointer p1, gconstpointer p2)
-{
- struct processtop *n1 = *(struct processtop **)p1;
- struct processtop *n2 = *(struct processtop **)p2;
- unsigned long totaln1 = n1->totalfilewrite + n1->totalfileread;
- unsigned long totaln2 = n2->totalfilewrite + n2->totalfileread;
-
- if (totaln1 < totaln2)
- return 1;
- if (totaln1 == totaln2)
- return 0;
- return -1;
-}
-
-gint sort_by_file_read_desc(gconstpointer p1, gconstpointer p2)
-{
- struct files *n1 = *(struct files **)p1;
- struct files *n2 = *(struct files **)p2;
- unsigned long totaln1;
- unsigned long totaln2;
-
- totaln1 = n1->read;
- totaln2 = n2->read;
-
- if (totaln1 < totaln2)
- return 1;
- if (totaln1 == totaln2)
- return 0;
- return -1;
-}
-
-gint sort_by_file_write_desc(gconstpointer p1, gconstpointer p2)
-{
- struct files *n1 = *(struct files **)p1;
- struct files *n2 = *(struct files **)p2;
- unsigned long totaln1;
- unsigned long totaln2;
-
- totaln1 = n1->write;
- totaln2 = n2->write;
-
- if (totaln1 < totaln2)
- return 1;
- if (totaln1 == totaln2)
- return 0;
- return -1;
-}
-
-gint sort_by_file_fd_desc(gconstpointer p1, gconstpointer p2)
-{
- struct files *n1 = *(struct files **)p1;
- struct files *n2 = *(struct files **)p2;
- unsigned long totaln1;
- unsigned long totaln2;
-
- totaln1 = n1->fd;
- totaln2 = n2->fd;
-
- if (totaln1 < totaln2)
- return 1;
- if (totaln1 == totaln2)
- return 0;
- return -1;
-}
-
-gint sort_by_cpu_group_by_threads_desc(gconstpointer p1, gconstpointer p2)
-{
- struct processtop *n1 = *(struct processtop **)p1;
- struct processtop *n2 = *(struct processtop **)p2;
- unsigned long totaln1 = n1->threadstotalcpunsec;
- unsigned long totaln2 = n2->threadstotalcpunsec;
-
- if (totaln1 < totaln2)
- return 1;
- if (totaln1 == totaln2)
- return 0;
- return -1;
-}
-
-void update_cputop_display()
-{
- int i;
- int header_offset = 2;
- struct processtop *tmp;
- unsigned long elapsed;
- double maxcputime;
- int nblinedisplayed = 0;
- int current_line = 0;
- int column;
-
- elapsed = data->end - data->start;
- maxcputime = elapsed * data->cpu_table->len / 100.0;
-
- if (cputopview[0].sort == 1)
- g_ptr_array_sort(data->process_table, sort_by_cpu_desc);
- else if (cputopview[1].sort == 1)
- g_ptr_array_sort(data->process_table, sort_by_pid_desc);
- else if (cputopview[2].sort == 1)
- g_ptr_array_sort(data->process_table, sort_by_tid_desc);
- else if (cputopview[3].sort == 1)
- g_ptr_array_sort(data->process_table, sort_by_cpu_desc);
- else
- g_ptr_array_sort(data->process_table, sort_by_cpu_desc);
-
- set_window_title(center, "CPU Top");
- wattron(center, A_BOLD);
- column = 1;
- for (i = 0; i < 4; i++) {
- if (cputopview[i].sort) {
- wattron(center, A_UNDERLINE);
- pref_current_sort = i;
- }
- mvwprintw(center, 1, column, cputopview[i].title);
- wattroff(center, A_UNDERLINE);
- column += 10;
- }
- wattroff(center, A_BOLD);
-
- max_center_lines = LINES - 5 - 7 - 1 - header_offset;
-
- /* iterate the process (thread) list */
- for (i = list_offset; i < data->process_table->len &&
- nblinedisplayed < max_center_lines; i++) {
- tmp = g_ptr_array_index(data->process_table, i);
- if (tmp->pid != tmp->tid)
- if (toggle_threads == -1)
- continue;
-
- if (process_selected(tmp)) {
- wattron(center, COLOR_PAIR(6));
- mvwhline(center, current_line + header_offset, 1, ' ', COLS-3);
- }
- if (current_line == selected_line) {
- selected_process = tmp;
- wattron(center, COLOR_PAIR(5));
- mvwhline(center, current_line + header_offset, 1, ' ', COLS-3);
- }
- /* CPU(%) */
- mvwprintw(center, current_line + header_offset, 1, "%1.2f",
- tmp->totalcpunsec / maxcputime);
- /* PID */
- mvwprintw(center, current_line + header_offset, 11, "%d", tmp->pid);
- /* TID */
- mvwprintw(center, current_line + header_offset, 21, "%d", tmp->tid);
- /* NAME */
- mvwprintw(center, current_line + header_offset, 31, "%s", tmp->comm);
- wattroff(center, COLOR_PAIR(6));
- wattroff(center, COLOR_PAIR(5));
- nblinedisplayed++;
- current_line++;
- }
-}
-
-gint sort_perf(gconstpointer p1, gconstpointer p2, gpointer key)
-{
- struct processtop *n1 = *(struct processtop **) p1;
- struct processtop *n2 = *(struct processtop **) p2;
-
- struct perfcounter *tmp1, *tmp2;
- unsigned long totaln2 = 0;
- unsigned long totaln1 = 0;
-
- if (!key)
- return 0;
-
- tmp1 = g_hash_table_lookup(n1->perf, key);
- if (!tmp1)
- totaln1 = 0;
- else
- totaln1 = tmp1->count;
-
- tmp2 = g_hash_table_lookup(n2->perf, key);
- if (!tmp2)
- totaln2 = 0;
- else
- totaln2 = tmp2->count;
-
- if (totaln1 < totaln2)
- return 1;
- if (totaln1 == totaln2) {
- totaln1 = n1->tid;
- totaln2 = n2->tid;
- if (totaln1 < totaln2)
- return 1;
- return -1;
- }
- return -1;
-}
-
-void print_key_title(char *key, int line)
-{
- wattron(center, A_BOLD);
- mvwprintw(center, line, 1, "%s", key);
- mvwprintw(center, line, 30, " ");
- wattroff(center, A_BOLD);
-}
-
-void update_process_details()
-{
- unsigned long elapsed;
- double maxcputime;
- struct processtop *tmp;
- struct files *file_tmp;
- int i, j = 0;
- char unit[4];
- char filename_buf[COLS];
- int line = 1;
- int column;
- GPtrArray *newfilearray = g_ptr_array_new();
- GHashTableIter iter;
- struct perfcounter *perfn1, *perfn2;
- gpointer key;
-
- set_window_title(center, "Process details");
-
-
- tmp = find_process_tid(data,
- selected_process->tid,
- selected_process->comm);
- elapsed = data->end - data->start;
- maxcputime = elapsed * data->cpu_table->len / 100.0;
-
- print_key_title("Name", line++);
- wprintw(center, "%s", selected_process->comm);
- print_key_title("TID", line++);
- wprintw(center, "%d", selected_process->tid);
- if (!tmp) {
- print_key_title("Does not exit at this time", 3);
- return;
- }
-
- print_key_title("PID", line++);
- wprintw(center, "%d", tmp->pid);
- print_key_title("PPID", line++);
- wprintw(center, "%d", tmp->ppid);
- print_key_title("CPU", line++);
- wprintw(center, "%1.2f %%", tmp->totalcpunsec/maxcputime);
-
- print_key_title("READ B/s", line++);
- scale_unit(tmp->fileread, unit);
- wprintw(center, "%s", unit);
-
- print_key_title("WRITE B/s", line++);
- scale_unit(tmp->filewrite, unit);
- wprintw(center, "%s", unit);
-
- g_hash_table_iter_init(&iter, global_perf_liszt);
- while (g_hash_table_iter_next (&iter, &key, (gpointer) &perfn1)) {
- print_key_title((char *) key, line++);
- perfn2 = g_hash_table_lookup(tmp->perf, (char *) key);
- wprintw(center, "%d", perfn2 ? perfn2->count : 0);
- }
- line++;
-
- wattron(center, A_BOLD);
- column = 1;
- for (i = 0; i < 3; i++) {
- if (fileview[i].sort) {
- pref_current_sort = i;
- wattron(center, A_UNDERLINE);
- }
- mvwprintw(center, line, column, fileview[i].title);
- wattroff(center, A_UNDERLINE);
- column += 10;
- }
- mvwprintw(center, line++, column, "FILENAME");
- wattroff(center, A_BOLD);
-
- /*
- * since the process_files_table array could contain NULL file structures,
- * and that the positions inside the array is important (it is the FD), we
- * need to create a temporary array that we can sort.
- */
- for (i = 0; i < tmp->process_files_table->len; i++) {
- file_tmp = g_ptr_array_index(tmp->process_files_table, i);
- if (file_tmp)
- g_ptr_array_add(newfilearray, file_tmp);
- }
-
- if (fileview[0].sort == 1)
- g_ptr_array_sort(newfilearray, sort_by_file_fd_desc);
- else if (fileview[1].sort == 1)
- g_ptr_array_sort(newfilearray, sort_by_file_read_desc);
- else if (fileview[2].sort == 1)
- g_ptr_array_sort(newfilearray, sort_by_file_write_desc);
- else
- g_ptr_array_sort(newfilearray, sort_by_file_read_desc);
-
- for (i = selected_line; i < newfilearray->len &&
- i < (selected_line + max_center_lines - line + 2); i++) {
- file_tmp = g_ptr_array_index(newfilearray, i);
- if (!file_tmp)
- continue;
- mvwprintw(center, line + j, 1, "%d", file_tmp->fd);
- scale_unit(file_tmp->read, unit);
- mvwprintw(center, line + j, 11, "%s", unit);
- scale_unit(file_tmp->write, unit);
- mvwprintw(center, line + j, 21, "%s", unit);
- snprintf(filename_buf, COLS - 25, "%s", file_tmp->name);
- mvwprintw(center, line + j, 31, "%s", filename_buf);
- j++;
- }
- g_ptr_array_free(newfilearray, TRUE);
-}
-
-void update_perf()
-{
- int i;
- int nblinedisplayed = 0;
- int current_line = 0;
- struct processtop *tmp;
- int header_offset = 2;
- int perf_row = 40;
- struct perfcounter *perfn1, *perfn2;
- char *perf_key = NULL;
- int value;
- GHashTableIter iter;
- gpointer key;
-
- set_window_title(center, "Perf Top");
- wattron(center, A_BOLD);
- mvwprintw(center, 1, 1, "PID");
- mvwprintw(center, 1, 11, "TID");
- mvwprintw(center, 1, 22, "NAME");
-
- perf_row = 40;
- g_hash_table_iter_init(&iter, global_perf_liszt);
- while (g_hash_table_iter_next (&iter, &key, (gpointer) &perfn1)) {
- if (perfn1->visible) {
- if (perfn1->sort) {
- /* pref_current_sort = i; */
- wattron(center, A_UNDERLINE);
- }
- /* + 5 to strip the "perf_" prefix */
- mvwprintw(center, 1, perf_row, "%s",
- (char *) key + 5);
- wattroff(center, A_UNDERLINE);
- perf_row += 20;
- }
- if (perfn1->sort) {
- perf_key = (char *) key;
- }
- }
- wattroff(center, A_BOLD);
-
- g_ptr_array_sort_with_data(data->process_table, sort_perf, perf_key);
-
- for (i = 0; i < data->process_table->len &&
- nblinedisplayed < max_center_lines; i++) {
- tmp = g_ptr_array_index(data->process_table, i);
- if (tmp->pid != tmp->tid)
- if (toggle_threads == -1)
- continue;
-
- if (process_selected(tmp)) {
- wattron(center, COLOR_PAIR(6));
- mvwhline(center, current_line + header_offset, 1, ' ', COLS-3);
- }
- if (current_line == selected_line) {
- selected_process = tmp;
- wattron(center, COLOR_PAIR(5));
- mvwhline(center, current_line + header_offset, 1, ' ', COLS-3);
- }
-
- mvwprintw(center, current_line + header_offset, 1, "%d", tmp->pid);
- mvwprintw(center, current_line + header_offset, 11, "%d", tmp->tid);
- mvwprintw(center, current_line + header_offset, 22, "%s", tmp->comm);
-
- g_hash_table_iter_init(&iter, global_perf_liszt);
-
- perf_row = 40;
- while (g_hash_table_iter_next (&iter, &key, (gpointer) &perfn1)) {
- if (perfn1->visible) {
- perfn2 = g_hash_table_lookup(tmp->perf, (char *) key);
- if (perfn2)
- value = perfn2->count;
- else
- value = 0;
- mvwprintw(center, current_line + header_offset,
- perf_row, "%d", value);
- perf_row += 20;
- }
- }
-
- wattroff(center, COLOR_PAIR(6));
- wattroff(center, COLOR_PAIR(5));
- nblinedisplayed++;
- current_line++;
- }
-}
-
-void update_iostream()
-{
- int i;
- int header_offset = 2;
- struct processtop *tmp;
- int nblinedisplayed = 0;
- int current_line = 0;
- int total = 0;
- char unit[4];
- int column;
-
- set_window_title(center, "IO Top");
- wattron(center, A_BOLD);
- mvwprintw(center, 1, 1, "PID");
- mvwprintw(center, 1, 11, "TID");
- mvwprintw(center, 1, 22, "NAME");
- column = 40;
- for (i = 0; i < 3; i++) {
- if (iostreamtopview[i].sort) {
- pref_current_sort = i;
- wattron(center, A_UNDERLINE);
- }
- mvwprintw(center, 1, column, iostreamtopview[i].title);
- wattroff(center, A_UNDERLINE);
- column += 12;
- }
- wattroff(center, A_BOLD);
- wattroff(center, A_UNDERLINE);
-
- if (iostreamtopview[0].sort == 1)
- g_ptr_array_sort(data->process_table, sort_by_process_read_desc);
- else if (iostreamtopview[1].sort == 1)
- g_ptr_array_sort(data->process_table, sort_by_process_write_desc);
- else if (iostreamtopview[2].sort == 1)
- g_ptr_array_sort(data->process_table, sort_by_process_total_desc);
- else
- g_ptr_array_sort(data->process_table, sort_by_process_total_desc);
-
- for (i = list_offset; i < data->process_table->len &&
- nblinedisplayed < max_center_lines; i++) {
- tmp = g_ptr_array_index(data->process_table, i);
- if (tmp->pid != tmp->tid)
- if (toggle_threads == -1)
- continue;
-
- if (process_selected(tmp)) {
- wattron(center, COLOR_PAIR(6));
- mvwhline(center, current_line + header_offset, 1, ' ', COLS-3);
- }
- if (current_line == selected_line) {
- selected_process = tmp;
- wattron(center, COLOR_PAIR(5));
- mvwhline(center, current_line + header_offset, 1, ' ', COLS-3);
- }
- /* TGID */
- mvwprintw(center, current_line + header_offset, 1, "%d", tmp->pid);
- /* PID */
- mvwprintw(center, current_line + header_offset, 11, "%d", tmp->tid);
- /* NAME */
- mvwprintw(center, current_line + header_offset, 22, "%s", tmp->comm);
-
- /* READ (bytes/sec) */
- scale_unit(tmp->fileread, unit);
- mvwprintw(center, current_line + header_offset, 40, "%s", unit);
-
- /* WRITE (bytes/sec) */
- scale_unit(tmp->filewrite, unit);
- mvwprintw(center, current_line + header_offset, 52, "%s", unit);
-
- /* TOTAL STREAM */
- total = tmp->totalfileread + tmp->totalfilewrite;
-
- scale_unit(total, unit);
- mvwprintw(center, current_line + header_offset, 64, "%s", unit);
-
- wattroff(center, COLOR_PAIR(6));
- wattroff(center, COLOR_PAIR(5));
- nblinedisplayed++;
- current_line++;
- }
-}
-
-void update_current_view()
-{
- sem_wait(&update_display_sem);
- if (!data)
- return;
- update_header();
-
- werase(center);
- box(center, 0, 0);
- switch (current_view) {
- case cpu:
- update_cputop_display();
- break;
- case perf:
- update_perf();
- break;
- case process_details:
- update_process_details();
- break;
- case iostream:
- update_iostream();
- break;
- case tree:
- update_cputop_display();
- break;
- default:
- break;
- }
- update_panels();
- doupdate();
- sem_post(&update_display_sem);
-}
-
-void update_process_detail_sort(int *line_selected)
-{
- int i;
- int size;
-
- size = 3;
-
- if (*line_selected > (size - 1))
- *line_selected = size - 1;
- else if (*line_selected < 0)
- *line_selected = 0;
-
- if (fileview[*line_selected].sort == 1)
- fileview[*line_selected].reverse = 1;
- for (i = 0; i < size; i++)
- fileview[i].sort = 0;
- fileview[*line_selected].sort = 1;
-}
-
-void update_process_detail_pref(int *line_selected, int toggle_view, int toggle_sort)
-{
- int i;
- int size;
-
- if (!data)
- return;
- if (pref_panel_window) {
- del_panel(pref_panel);
- delwin(pref_panel_window);
- }
- size = 3;
-
- pref_panel_window = create_window(size + 2, 30, 10, 10);
- pref_panel = new_panel(pref_panel_window);
-
- werase(pref_panel_window);
- box(pref_panel_window, 0 , 0);
- set_window_title(pref_panel_window, "Process Detail Preferences ");
- wattron(pref_panel_window, A_BOLD);
- mvwprintw(pref_panel_window, size + 1, 1,
- " 's' : sort, space : toggle");
- wattroff(pref_panel_window, A_BOLD);
-
- if (*line_selected > (size - 1))
- *line_selected = size - 1;
- else if (*line_selected < 0)
- *line_selected = 0;
- if (toggle_sort == 1) {
- update_process_detail_sort(line_selected);
- update_current_view();
- }
-
- for (i = 0; i < size; i++) {
- if (i == *line_selected) {
- wattron(pref_panel_window, COLOR_PAIR(5));
- mvwhline(pref_panel_window, i + 1, 1, ' ', 30 - 2);
- }
- if (fileview[i].sort == 1)
- wattron(pref_panel_window, A_BOLD);
- mvwprintw(pref_panel_window, i + 1, 1, "[-] %s",
- fileview[i].title);
- wattroff(pref_panel_window, A_BOLD);
- wattroff(pref_panel_window, COLOR_PAIR(5));
-
- }
- update_panels();
- doupdate();
-}
-
-void update_iostream_sort(int *line_selected)
-{
- int i;
- int size;
-
- size = 3;
- if (*line_selected > (size - 1))
- *line_selected = size - 1;
- else if (*line_selected < 0)
- *line_selected = 0;
- if (iostreamtopview[*line_selected].sort == 1)
- iostreamtopview[*line_selected].reverse = 1;
- for (i = 0; i < size; i++)
- iostreamtopview[i].sort = 0;
- iostreamtopview[*line_selected].sort = 1;
-
-}
-
-void update_iostream_pref(int *line_selected, int toggle_view, int toggle_sort)
-{
- int i;
- int size;
-
- if (!data)
- return;
- if (pref_panel_window) {
- del_panel(pref_panel);
- delwin(pref_panel_window);
- }
- size = 3;
-
- pref_panel_window = create_window(size + 2, 30, 10, 10);
- pref_panel = new_panel(pref_panel_window);
-
- werase(pref_panel_window);
- box(pref_panel_window, 0 , 0);
- set_window_title(pref_panel_window, "IOTop Preferences ");
- wattron(pref_panel_window, A_BOLD);
- mvwprintw(pref_panel_window, size + 1, 1,
- " 's' : sort, space : toggle");
- wattroff(pref_panel_window, A_BOLD);
-
- if (*line_selected > (size - 1))
- *line_selected = size - 1;
- else if (*line_selected < 0)
- *line_selected = 0;
- if (toggle_sort == 1) {
- update_iostream_sort(line_selected);
- update_current_view();
- }
-
- for (i = 0; i < size; i++) {
- if (i == *line_selected) {
- wattron(pref_panel_window, COLOR_PAIR(5));
- mvwhline(pref_panel_window, i + 1, 1, ' ', 30 - 2);
- }
- if (iostreamtopview[i].sort == 1)
- wattron(pref_panel_window, A_BOLD);
- mvwprintw(pref_panel_window, i + 1, 1, "[-] %s",
- iostreamtopview[i].title);
- wattroff(pref_panel_window, A_BOLD);
- wattroff(pref_panel_window, COLOR_PAIR(5));
-
- }
- update_panels();
- doupdate();
-}
-
-void update_cpu_sort(int *line_selected)
-{
- int i;
- int size = 3;
-
- if (*line_selected > (size - 1))
- *line_selected = size - 1;
- else if (*line_selected < 0)
- *line_selected = 0;
-
- /* special case, we don't support sorting by procname for now */
- if (*line_selected != 3) {
- if (cputopview[*line_selected].sort == 1)
- cputopview[*line_selected].reverse = 1;
- for (i = 0; i < size; i++)
- cputopview[i].sort = 0;
- cputopview[*line_selected].sort = 1;
- }
-}
-
-void update_cpu_pref(int *line_selected, int toggle_view, int toggle_sort)
-{
- int i;
- int size;
-
- if (!data)
- return;
- if (pref_panel_window) {
- del_panel(pref_panel);
- delwin(pref_panel_window);
- }
- size = 4;
-
- pref_panel_window = create_window(size + 2, 30, 10, 10);
- pref_panel = new_panel(pref_panel_window);
-
- werase(pref_panel_window);
- box(pref_panel_window, 0 , 0);
- set_window_title(pref_panel_window, "CPUTop Preferences ");
- wattron(pref_panel_window, A_BOLD);
- mvwprintw(pref_panel_window, size + 1, 1,
- " 's' : sort, space : toggle");
- wattroff(pref_panel_window, A_BOLD);
-
- if (*line_selected > (size - 1))
- *line_selected = size - 1;
- else if (*line_selected < 0)
- *line_selected = 0;
- if (toggle_sort == 1) {
- update_cpu_sort(line_selected);
- update_current_view();
- }
-
- for (i = 0; i < size; i++) {
- if (i == *line_selected) {
- wattron(pref_panel_window, COLOR_PAIR(5));
- mvwhline(pref_panel_window, i + 1, 1, ' ', 30 - 2);
- }
- if (cputopview[i].sort == 1)
- wattron(pref_panel_window, A_BOLD);
- mvwprintw(pref_panel_window, i + 1, 1, "[-] %s",
- cputopview[i].title);
- wattroff(pref_panel_window, A_BOLD);
- wattroff(pref_panel_window, COLOR_PAIR(5));
-
- }
- update_panels();
- doupdate();
-}
-
-void update_perf_sort(int *line_selected)
-{
- int i;
- struct perfcounter *perf;
- GList *perflist;
- int size;
-
- size = g_hash_table_size(global_perf_liszt);
- if (*line_selected > (size - 1))
- *line_selected = size - 1;
- else if (*line_selected < 0)
- *line_selected = 0;
-
- i = 0;
- perflist = g_list_first(g_hash_table_get_keys(global_perf_liszt));
- while (perflist) {
- perf = g_hash_table_lookup(global_perf_liszt, perflist->data);
- if (i != *line_selected)
- perf->sort = 0;
- else
- perf->sort = 1;
- i++;
- perflist = g_list_next(perflist);
- }
-}
-
-void update_perf_pref(int *line_selected, int toggle_view, int toggle_sort)
-{
- int i;
- struct perfcounter *perf;
- GList *perflist;
- int size;
-
- if (!data)
- return;
- if (pref_panel_window) {
- del_panel(pref_panel);
- delwin(pref_panel_window);
- }
- size = g_hash_table_size(global_perf_liszt);
-
- pref_panel_window = create_window(size + 2, 30, 10, 10);
- pref_panel = new_panel(pref_panel_window);
-
- werase(pref_panel_window);
- box(pref_panel_window, 0 , 0);
- set_window_title(pref_panel_window, "Perf Preferences ");
- wattron(pref_panel_window, A_BOLD);
- mvwprintw(pref_panel_window, g_hash_table_size(global_perf_liszt) + 1, 1,
- " 's' : sort, space : toggle");
- wattroff(pref_panel_window, A_BOLD);
-
- if (*line_selected > (size - 1))
- *line_selected = size - 1;
- else if (*line_selected < 0)
- *line_selected = 0;
-
- if (toggle_sort == 1) {
- update_perf_sort(line_selected);
- update_current_view();
- }
-
- i = 0;
- perflist = g_list_first(g_hash_table_get_keys(global_perf_liszt));
- while (perflist) {
- perf = g_hash_table_lookup(global_perf_liszt, perflist->data);
- if (i == *line_selected && toggle_view == 1) {
- perf->visible = perf->visible == 1 ? 0:1;
- update_current_view();
- }
- if (i == *line_selected) {
- wattron(pref_panel_window, COLOR_PAIR(5));
- mvwhline(pref_panel_window, i + 1, 1, ' ', 30 - 2);
- }
- if (perf->sort == 1)
- wattron(pref_panel_window, A_BOLD);
- mvwprintw(pref_panel_window, i + 1, 1, "[%c] %s",
- perf->visible == 1 ? 'x' : ' ',
- (char *) perflist->data + 5);
- wattroff(pref_panel_window, A_BOLD);
- wattroff(pref_panel_window, COLOR_PAIR(5));
- i++;
- perflist = g_list_next(perflist);
- }
- update_panels();
- doupdate();
-}
-
-int update_preference_panel(int *line_selected, int toggle_view, int toggle_sort)
-{
- int ret = 0;
-
- switch(current_view) {
- case perf:
- update_perf_pref(line_selected, toggle_view, toggle_sort);
- break;
- case cpu:
- update_cpu_pref(line_selected, toggle_view, toggle_sort);
- break;
- case iostream:
- update_iostream_pref(line_selected, toggle_view, toggle_sort);
- break;
- case process_details:
- update_process_detail_pref(line_selected, toggle_view, toggle_sort);
- break;
- default:
- ret = -1;
- break;
- }
-
- return ret;
-}
-
-int update_sort(int *line_selected)
-{
- int ret = 0;
-
- switch(current_view) {
- case perf:
- update_perf_sort(line_selected);
- break;
- case cpu:
- update_cpu_sort(line_selected);
- break;
- case iostream:
- update_iostream_sort(line_selected);
- break;
- case process_details:
- update_process_detail_sort(line_selected);
- break;
- default:
- ret = -1;
- break;
- }
-
- return ret;
-}
-
-
-void toggle_pref_panel(void)
-{
- int ret;
-
- if (pref_panel_visible) {
- hide_panel(pref_panel);
- pref_panel_visible = 0;
- } else {
- ret = update_preference_panel(&pref_line_selected, 0, 0);
- if (ret < 0)
- return;
- show_panel(pref_panel);
- pref_panel_visible = 1;
- }
- update_panels();
- doupdate();
-}
-
-void display(unsigned int index)
-{
- last_display_index = index;
- currently_displayed_index = index;
- data = g_ptr_array_index(copies, index);
- if (!data)
- return;
- max_elements = data->process_table->len;
- update_current_view();
- update_footer();
- update_panels();
- doupdate();
-}
-
-void pause_display()
-{
- toggle_pause = 1;
- print_log("Pause");
- sem_wait(&pause_sem);
-}
-
-void resume_display()
-{
- toggle_pause = -1;
- print_log("Resume");
- sem_post(&pause_sem);
-}
-
-void *handle_keyboard(void *p)
-{
- int ch;
- while((ch = getch())) {
- switch(ch) {
- /* Move the cursor and scroll */
- case 'j':
- case KEY_DOWN:
- if (pref_panel_visible) {
- pref_line_selected++;
- update_preference_panel(&pref_line_selected, 0, 0);
- } else {
- if (selected_line < (max_center_lines - 1) &&
- selected_line < max_elements - 1) {
- selected_line++;
- selected_in_list++;
- } else if (selected_in_list < (max_elements - 1)
- && (list_offset < (max_elements - max_center_lines))) {
- selected_in_list++;
- list_offset++;
- }
- update_current_view();
- }
- break;
- case KEY_NPAGE:
- break;
- case 'k':
- case KEY_UP:
- if (pref_panel_visible) {
- if (pref_line_selected > 0)
- pref_line_selected--;
- update_preference_panel(&pref_line_selected, 0, 0);
- } else {
- if (selected_line > 0) {
- selected_line--;
- selected_in_list--;
- } else if (selected_in_list > 0 && list_offset > 0) {
- selected_in_list--;
- list_offset--;
- }
- update_current_view();
- }
- break;
- case KEY_PPAGE:
- break;
-
- /* Navigate the history with arrows */
- case KEY_LEFT:
- if (currently_displayed_index > 0) {
- currently_displayed_index--;
- print_log("Going back in time");
- } else {
- print_log("Cannot rewind, last data is already displayed");
- }
- data = g_ptr_array_index(copies, currently_displayed_index);
- max_elements = data->process_table->len;
-
- /* we force to pause the display when moving in time */
- if (toggle_pause < 0)
- pause_display();
-
- update_current_view();
- update_footer();
- break;
- case KEY_RIGHT:
- if (currently_displayed_index < last_display_index) {
- currently_displayed_index++;
- print_log("Going forward in time");
- data = g_ptr_array_index(copies, currently_displayed_index);
- max_elements = data->process_table->len;
- update_current_view();
- update_footer();
- } else {
- print_log("Manually moving forward");
- sem_post(&timer);
- if (toggle_pause > 0) {
- sem_post(&pause_sem);
- update_current_view();
- sem_wait(&pause_sem);
- }
- }
-
- break;
- case ' ':
- if (pref_panel_visible) {
- update_preference_panel(&pref_line_selected, 1, 0);
- } else {
- update_selected_processes();
- update_current_view();
- }
- break;
- case 's':
- if (pref_panel_visible)
- update_preference_panel(&pref_line_selected, 0, 1);
- break;
- case '>':
- /* perf uses a hashtable, it is ordered backward */
- if (current_view == perf) {
- pref_current_sort--;
- } else if (!pref_panel_visible) {
- pref_current_sort++;
- }
- update_sort(&pref_current_sort);
- update_current_view();
- break;
- case '<':
- /* perf uses a hashtable, it is ordered backward */
- if (current_view == perf) {
- pref_current_sort++;
- } else if (!pref_panel_visible) {
- pref_current_sort--;
- }
- update_sort(&pref_current_sort);
- update_current_view();
- break;
-
- case 13: /* FIXME : KEY_ENTER ?? */
- if (pref_panel_visible)
- break;
- if (current_view != process_details) {
- previous_view = current_view;
- current_view = process_details;
- } else {
- current_view = previous_view;
- previous_view = process_details;
- }
- update_current_view();
- break;
-
- case KEY_F(1):
- if (pref_panel_visible)
- toggle_pref_panel();
- current_view = cpu;
- selected_line = 0;
- update_current_view();
- break;
- case KEY_F(2):
- if (pref_panel_visible)
- toggle_pref_panel();
- current_view = cpu;
- selected_line = 0;
- update_current_view();
- break;
- case KEY_F(3):
- if (pref_panel_visible)
- toggle_pref_panel();
- current_view = perf;
- selected_line = 0;
- update_current_view();
- break;
- case KEY_F(4):
- if (pref_panel_visible)
- toggle_pref_panel();
- current_view = iostream;
- selected_line = 0;
- update_current_view();
- break;
- case KEY_F(10):
- case 'q':
- reset_ncurses();
- break;
- case 't':
- toggle_threads *= -1;
- update_current_view();
- break;
- case 'p':
- if (toggle_pause < 0) {
- pause_display();
- } else {
- resume_display();
- }
- break;
- case 'r':
- toggle_pref_panel();
- break;
- /* ESCAPE, but slow to process, don't know why */
- case 27:
- if (pref_panel_visible)
- toggle_pref_panel();
- else if (current_view == process_details) {
- current_view = previous_view;
- previous_view = process_details;
- }
- update_current_view();
- break;
- default:
- if (data)
- update_current_view();
- break;
- }
- update_footer();
- }
- return NULL;
-}
-
-void init_view_headers()
-{
- cputopview[0].title = strdup("CPU(%)");
- cputopview[0].sort = 1;
- cputopview[1].title = strdup("PID");
- cputopview[2].title = strdup("TID");
- cputopview[3].title = strdup("NAME");
-
- iostreamtopview[0].title = strdup("R (B/sec)");
- iostreamtopview[1].title = strdup("W (B/sec)");
- iostreamtopview[2].title = strdup("Total (B)");
- iostreamtopview[2].sort = 1;
-
- fileview[0].title = strdup("FD");
- fileview[1].title = strdup("READ");
- fileview[1].sort = 1;
- fileview[2].title = strdup("WRITE");
-}
-
-void init_ncurses()
-{
- selected_processes = g_ptr_array_new();
- sem_init(&update_display_sem, 0, 1);
- init_view_headers();
- init_screen();
-
- header = create_window(5, COLS - 1, 0, 0);
- center = create_window(LINES - 5 - 7, COLS - 1, 5, 0);
- status = create_window(MAX_LOG_LINES + 2, COLS - 1, LINES - 7, 0);
- footer = create_window(1, COLS - 1, LINES - 1, 0);
-
- print_log("Starting display");
-
- main_panel = new_panel(center);
-
- current_view = cpu;
-
- basic_header();
- update_footer();
-
- pthread_create(&keyboard_thread, NULL, handle_keyboard, (void *)NULL);
-}
--- /dev/null
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <ncurses.h>
+#include <panel.h>
+#include <pthread.h>
+#include <semaphore.h>
+
+#include <state/StateSystem.hpp>
+#include <state/statevalue/StateValue.hpp>
+#include <state/statevalue/IntegerStateValue.hpp>
+#include <state/statevalue/StringStateValue.hpp>
+#include <state/statevalue/NullStateValue.hpp>
+#include <state/statevalue/QuarkStateValue.hpp>
+
+#define __STDC_FORMAT_MACROS
+#include <inttypes.h>
+
+#include "cursesdisplay.h"
+#include "lttngtoptypes.h"
+#include "iostreamtop.h"
+#include "common.h"
+
+#define DEFAULT_DELAY 15
+#define MAX_LINE_LENGTH 50
+#define MAX_LOG_LINES 4
+
+using namespace State;
+
+enum view_list current_view;
+enum view_list previous_view;
+
+/* to prevent concurrent updates of the different windows */
+sem_t update_display_sem;
+
+char *termtype;
+WINDOW *footer, *header, *center, *status;
+WINDOW *pref_panel_window = NULL;
+PANEL *pref_panel, *main_panel;
+
+int pref_panel_visible = 0;
+int pref_line_selected = 0;
+int pref_current_sort = 0;
+
+Quark selected_process;
+int selected_ret;
+
+int selected_line = 0; /* select bar position */
+int selected_in_list = 0; /* selection relative to the whole list */
+int list_offset = 0; /* first index in the list to display (scroll) */
+int nb_log_lines = 0;
+char log_lines[MAX_LINE_LENGTH * MAX_LOG_LINES + MAX_LOG_LINES];
+
+int max_elements = 80;
+
+int toggle_threads = 1;
+int toggle_pause = -1;
+
+int max_center_lines;
+GPtrArray *selected_processes;
+
+pthread_t keyboard_thread;
+
+struct header_view cputopview[4];
+struct header_view iostreamtopview[3];
+struct header_view fileview[3];
+
+unsigned long i_start = 0;
+unsigned long i_end = 0;
+
+std::string sort_perf_key;
+
+
+void reset_ncurses()
+{
+ curs_set(1);
+ endwin();
+ exit(0);
+}
+
+static void handle_sigterm(int signal)
+{
+ reset_ncurses();
+}
+
+void init_screen()
+{
+ initscr();
+ noecho();
+ halfdelay(DEFAULT_DELAY);
+ nonl();
+ intrflush(stdscr, false);
+ keypad(stdscr, true);
+ curs_set(0);
+
+ if (has_colors()) {
+ start_color();
+ init_pair(1, COLOR_RED, COLOR_BLACK); /* - */
+ init_pair(2, COLOR_GREEN, COLOR_BLACK); /* + */
+ init_pair(3, COLOR_BLACK, COLOR_WHITE); /* keys */
+ init_pair(4, COLOR_WHITE, COLOR_GREEN); /* keys activated */
+ init_pair(5, COLOR_WHITE, COLOR_BLUE); /* select line */
+ init_pair(6, COLOR_WHITE, COLOR_GREEN); /* selected process */
+ }
+ termtype = getenv("TERM");
+ if (!strcmp(termtype, "xterm") || !strcmp(termtype, "xterm-color") ||
+ !strcmp(termtype, "vt220")) {
+ define_key("\033[H", KEY_HOME);
+ define_key("\033[F", KEY_END);
+ define_key("\033OP", KEY_F(1));
+ define_key("\033OQ", KEY_F(2));
+ define_key("\033OR", KEY_F(3));
+ define_key("\033OS", KEY_F(4));
+ define_key("\0330U", KEY_F(6));
+ define_key("\033[11~", KEY_F(1));
+ define_key("\033[12~", KEY_F(2));
+ define_key("\033[13~", KEY_F(3));
+ define_key("\033[14~", KEY_F(4));
+ define_key("\033[16~", KEY_F(6));
+ define_key("\033[17;2~", KEY_F(18));
+ }
+ signal(SIGTERM, handle_sigterm);
+ mousemask(BUTTON1_CLICKED, NULL);
+ refresh();
+}
+
+WINDOW *create_window(int height, int width, int startx, int starty)
+{
+ WINDOW *win;
+ win = newwin(height, width, startx, starty);
+ box(win, 0 , 0);
+ wrefresh(win);
+ return win;
+}
+
+WINDOW *create_window_no_border(int height, int width, int startx, int starty)
+{
+ WINDOW *win;
+ win = newwin(height, width, startx, starty);
+ wrefresh(win);
+ return win;
+}
+
+void print_digit(WINDOW *win, int digit)
+{
+ if (digit < 0) {
+ wattron(win, COLOR_PAIR(1));
+ wprintw(win, "%d", digit);
+ wattroff(win, COLOR_PAIR(1));
+ } else if (digit > 0) {
+ wattron(win, COLOR_PAIR(2));
+ wprintw(win, "+%d", digit);
+ wattroff(win, COLOR_PAIR(2));
+ } else {
+ wprintw(win, "0");
+ }
+}
+
+void print_digits(WINDOW *win, int first, int second)
+{
+ wprintw(win, "(");
+ print_digit(win, first);
+ wprintw(win, ", ");
+ print_digit(win, second);
+ wprintw(win, ")");
+}
+
+void print_headers(int line, const char desc[], int value, int first, int second)
+{
+ wattron(header, A_BOLD);
+ mvwprintw(header, line, 4, "%s", desc);
+ wattroff(header, A_BOLD);
+ mvwprintw(header, line, 16, "%d", value);
+ wmove(header, line, 24);
+ print_digits(header, first, second);
+ wmove(header, line, 40);
+}
+
+void set_window_title(WINDOW *win, const char title[])
+{
+ wattron(win, A_BOLD);
+ mvwprintw(win, 0, 1, title);
+ wattroff(win, A_BOLD);
+}
+
+void print_log(const char str[])
+{
+ unsigned int i;
+ int current_line = 1;
+ int current_char = 1;
+ char *tmp, *tmp2;
+ /* rotate the line buffer */
+ if (nb_log_lines >= MAX_LOG_LINES) {
+ tmp = strndup(log_lines, MAX_LINE_LENGTH * MAX_LOG_LINES + MAX_LOG_LINES);
+ tmp2 = strchr(tmp, '\n');
+ memset(log_lines, '\0', strlen(log_lines));
+ strncat(log_lines, tmp2 + 1, strlen(tmp2) - 1);
+ log_lines[strlen(log_lines)] = '\n';
+ log_lines[strlen(log_lines)] = '\0';
+ free(tmp);
+ }
+ nb_log_lines++;
+
+ strncat(log_lines, str, MAX_LINE_LENGTH - 1);
+
+ if (nb_log_lines < MAX_LOG_LINES)
+ log_lines[strlen(log_lines)] = '\n';
+ log_lines[strlen(log_lines)] = '\0';
+
+ werase(status);
+ box(status, 0 , 0);
+ set_window_title(status, "Status");
+ for (i = 0; i < strlen(log_lines); i++) {
+ if (log_lines[i] == '\n') {
+ wmove(status, ++current_line, 1);
+ current_char = 1;
+ } else {
+ mvwprintw(status, current_line, current_char++, "%c",
+ log_lines[i]);
+ }
+ }
+ wrefresh(status);
+}
+
+int process_selected(Quark proc)
+{
+ int selected;
+ get_current_attribute_value_int(&proc, "selected", selected);
+ return selected;
+}
+
+void update_selected_processes()
+{
+ Quark selected = state_system->getQuark(selected_process, "selected");
+ if (process_selected(selected_process)) {
+ state_system->updateCurrentState(selected, 0);
+ print_log("Process removed");
+ } else {
+ state_system->updateCurrentState(selected, 1);
+ print_log("Process added");
+ }
+}
+
+void print_key(WINDOW *win, const char key[], const char desc[], int toggle)
+{
+ int pair;
+ if (toggle > 0)
+ pair = 4;
+ else
+ pair = 3;
+ wattron(win, COLOR_PAIR(pair));
+ wprintw(footer, "%s", key);
+ wattroff(win, COLOR_PAIR(pair));
+ wprintw(footer, ":%s", desc);
+}
+
+void update_footer()
+{
+ sem_wait(&update_display_sem);
+ werase(footer);
+ wmove(footer, 1, 1);
+ print_key(footer, "F2", "CPUtop ", current_view == cpu);
+ print_key(footer, "F3", "PerfTop ", current_view == perf);
+ print_key(footer, "F4", "IOTop ", current_view == iostream);
+ print_key(footer, "Enter", "Details ", current_view == process_details);
+ print_key(footer, "Space", "Highlight ", 0);
+ print_key(footer, "q", "Quit ", 0);
+ print_key(footer, "r", "Pref ", 0);
+ print_key(footer, "t", "Threads ", toggle_threads);
+ print_key(footer, "p", "Pause ", toggle_pause);
+
+ wrefresh(footer);
+ sem_post(&update_display_sem);
+}
+
+void basic_header()
+{
+ werase(header);
+ box(header, 0 , 0);
+ set_window_title(header, "Statistics for interval [gathering data...[");
+ wattron(header, A_BOLD);
+ mvwprintw(header, 1, 4, "CPUs");
+ mvwprintw(header, 2, 4, "Threads");
+ mvwprintw(header, 3, 4, "FDs");
+ wattroff(header, A_BOLD);
+ wrefresh(header);
+}
+
+struct tm format_timestamp(uint64_t timestamp)
+{
+ struct tm tm;
+ uint64_t ts_sec = 0, ts_nsec;
+ time_t time_s;
+
+ ts_nsec = timestamp;
+ ts_sec += ts_nsec / NSEC_PER_SEC;
+ ts_nsec = ts_nsec % NSEC_PER_SEC;
+
+ time_s = (time_t) ts_sec;
+
+ localtime_r(&time_s, &tm);
+
+ return tm;
+}
+
+static void scale_unit(uint64_t bytes, char *ret)
+{
+ if (bytes >= 1000000000)
+ sprintf(ret, "%" PRIu64 "G", bytes/1000000000);
+ if (bytes >= 1000000)
+ sprintf(ret, "%" PRIu64 "M", bytes/1000000);
+ else if (bytes >= 1000)
+ sprintf(ret, "%" PRIu64 "K", bytes/1000);
+ else
+ sprintf(ret, "%" PRIu64, bytes);
+}
+
+uint64_t total_io()
+{
+ uint64_t total = 0;
+ int read, write;
+ unsigned long birth;
+ unsigned long real_start;
+ Quark proc;
+
+ if (get_attribute_value_at_quark(i_end, NULL, "proc", proc)) {
+ do {
+ get_attribute_value_at_ulong(
+ i_end, &proc, "birth", birth);
+ if (birth > i_start)
+ real_start = birth;
+ else
+ real_start = i_start;
+ get_interval_value_int(real_start, i_end, &proc,
+ "fileread", read);
+ get_interval_value_int(real_start, i_end, &proc,
+ "filewrite", write);
+ total += read + write;
+
+ } while (get_attribute_value_at_quark(
+ i_end, &proc, "next", proc));
+ }
+
+ return total;
+}
+
+void update_header()
+{
+ struct tm start, end;
+ uint64_t ts_nsec_start, ts_nsec_end;
+ char io[16];
+ int cpu_nb;
+ int nbthreads, nbnewthreads, nbdeadthreads;
+ int nbfiles, nbnewfiles, nbdeadfiles;
+
+ ts_nsec_start = i_start % NSEC_PER_SEC;
+ start = format_timestamp(i_start);
+
+ ts_nsec_end = i_end % NSEC_PER_SEC;
+ end = format_timestamp(i_end);
+
+ werase(header);
+ box(header, 0 , 0);
+ set_window_title(header, "Statistics for interval ");
+ wattron(header, A_BOLD);
+
+ wprintw(header,
+ "[%02d:%02d:%02d.%09" PRIu64 ", %02d:%02d:%02d.%09" PRIu64 "[",
+ start.tm_hour, start.tm_min, start.tm_sec, ts_nsec_start,
+ end.tm_hour, end.tm_min, end.tm_sec, ts_nsec_end);
+ mvwprintw(header, 1, 4, "CPUs");
+ wattroff(header, A_BOLD);
+
+ cpu_nb = get_sequence_length(i_end, state_system->getQuark("cpu"));
+ get_attribute_value_at_int(i_end, NULL, "nbthreads", nbthreads);
+ get_interval_value_int(i_start, i_end, NULL, "nbnewthreads",
+ nbnewthreads);
+ get_interval_value_int(i_start, i_end, NULL, "nbdeadthreads",
+ nbdeadthreads);
+ get_attribute_value_at_int(i_end, NULL, "nbfiles", nbfiles);
+ get_interval_value_int(i_start, i_end, NULL, "nbnewfiles", nbnewfiles);
+ get_interval_value_int(i_start, i_end, NULL, "nbdeadfiles",
+ nbdeadfiles);
+
+ wprintw(header, "\t%d\t(max/cpu : %0.2f%)", cpu_nb,
+ 100.0/cpu_nb);
+ print_headers(2, "Threads", nbthreads, nbnewthreads,-1*nbdeadthreads);
+ print_headers(3, "FDs", nbfiles, nbnewfiles,-1*nbdeadfiles);
+ scale_unit(total_io(), io);
+ mvwprintw(header, 3, 43, "%sB/sec", io);
+ wrefresh(header);
+}
+
+int sort_by_cpu_desc(const void *pp1, const void *pp2)
+{
+ Quark p1 = *(Quark *)pp1;
+ Quark p2 = *(Quark *)pp2;
+ unsigned long total1, total2;
+ unsigned long birth1, birth2;
+ unsigned long start1 = i_start;
+ unsigned long start2 = i_start;
+
+ get_attribute_value_at_ulong(i_end, &p1, "birth", birth1);
+ get_attribute_value_at_ulong(i_end, &p2, "birth", birth2);
+ if (birth1 > i_start)
+ start1 = birth1;
+ if (birth2 > i_start)
+ start2 = birth2;
+
+ get_interval_value_ulong(start1, i_end, &p1, "totalcpunsec", total1);
+ get_interval_value_ulong(start2, i_end, &p2, "totalcpunsec", total2);
+
+ if (total1 > total2)
+ return 1;
+ if (total1 == total2)
+ return 0;
+ return -1;
+}
+
+int sort_by_tid_desc(const void *pp1, const void *pp2)
+{
+ Quark p1 = *(Quark *)pp1;
+ Quark p2 = *(Quark *)pp2;
+ int tid1, tid2;
+
+ get_attribute_value_at_int(i_end, &p1, "tid", tid1);
+ get_attribute_value_at_int(i_end, &p2, "tid", tid2);
+
+ if (tid1 > tid2)
+ return 1;
+ if (tid1 == tid2)
+ return 0;
+ return -1;
+}
+
+int sort_by_pid_desc(const void *pp1, const void *pp2)
+{
+ Quark p1 = *(Quark *)pp1;
+ Quark p2 = *(Quark *)pp2;
+ int pid1, pid2;
+
+ get_attribute_value_at_int(i_end, &p1, "pid", pid1);
+ get_attribute_value_at_int(i_end, &p2, "pid", pid2);
+
+ if (pid1 > pid2)
+ return 1;
+ if (pid1 == pid2)
+ return 0;
+ return -1;
+}
+
+
+int sort_by_process_read_desc(const void *pp1, const void *pp2)
+{
+ Quark p1 = *(Quark *)pp1;
+ Quark p2 = *(Quark *)pp2;
+ int total1, total2;
+ unsigned long birth1, birth2;
+ unsigned long start1 = i_start;
+ unsigned long start2 = i_start;
+
+ get_attribute_value_at_ulong(i_end, &p1, "birth", birth1);
+ get_attribute_value_at_ulong(i_end, &p2, "birth", birth2);
+ if (birth1 > i_start)
+ start1 = birth1;
+ if (birth2 > i_start)
+ start2 = birth2;
+
+ get_interval_value_int(start1, i_end, &p1, "fileread", total1);
+ get_interval_value_int(start2, i_end, &p2, "fileread", total2);
+
+ if (total1 > total2)
+ return 1;
+ if (total1 == total2)
+ return 0;
+ return -1;
+}
+
+int sort_by_process_write_desc(const void *pp1, const void *pp2)
+{
+ Quark p1 = *(Quark *)pp1;
+ Quark p2 = *(Quark *)pp2;
+ int total1, total2;
+ unsigned long birth1, birth2;
+ unsigned long start1 = i_start;
+ unsigned long start2 = i_start;
+
+ get_attribute_value_at_ulong(i_end, &p1, "birth", birth1);
+ get_attribute_value_at_ulong(i_end, &p2, "birth", birth2);
+ if (birth1 > i_start)
+ start1 = birth1;
+ if (birth2 > i_start)
+ start2 = birth2;
+
+ get_interval_value_int(start1, i_end, &p1, "filewrite", total1);
+ get_interval_value_int(start2, i_end, &p2, "filewrite", total2);
+
+ if (total1 > total2)
+ return 1;
+ if (total1 == total2)
+ return 0;
+ return -1;
+}
+
+int sort_by_process_total_desc(const void *pp1, const void *pp2)
+{
+ Quark p1 = *(Quark *)pp1;
+ Quark p2 = *(Quark *)pp2;
+ int p1Read, p2Read;
+ int p1Write, p2Write;
+ int total1, total2;
+ unsigned long birth1, birth2;
+ unsigned long start1 = i_start;
+ unsigned long start2 = i_start;
+
+ get_attribute_value_at_ulong(i_end, &p1, "birth", birth1);
+ get_attribute_value_at_ulong(i_end, &p2, "birth", birth2);
+ if (birth1 > i_start)
+ start1 = birth1;
+ if (birth2 > i_start)
+ start2 = birth2;
+
+ get_interval_value_int(start1, i_end, &p1, "fileread", p1Read);
+ get_interval_value_int(start2, i_end, &p2, "fileread", p2Read);
+ get_interval_value_int(start1, i_end, &p1, "filewrite", p1Write);
+ get_interval_value_int(start2, i_end, &p2, "filewrite", p2Write);
+
+ total1 = p1Read + p1Write;
+ total2 = p2Read + p2Write;
+
+ if (total1 > total2)
+ return 1;
+ if (total1 == total2)
+ return 0;
+ return -1;
+}
+
+int sort_by_file_read_desc(const void *pf1, const void *pf2)
+{
+ Quark f1 = *(Quark *)pf1;
+ Quark f2 = *(Quark *)pf2;
+ int total1, total2;
+ unsigned long birth1, birth2;
+ unsigned long start1 = i_start;
+ unsigned long start2 = i_start;
+
+ get_attribute_value_at_ulong(i_end, &f1, "file/birth", birth1);
+ get_attribute_value_at_ulong(i_end, &f2, "file/birth", birth2);
+ if (birth1 > i_start)
+ start1 = birth1;
+ if (birth2 > i_start)
+ start2 = birth2;
+
+ get_interval_value_int(start1, i_end, &f1, "file/read", total1);
+ get_interval_value_int(start2, i_end, &f2, "file/read", total2);
+
+ if (total1 > total2)
+ return 1;
+ if (total1 == total2)
+ return 0;
+ return -1;
+}
+
+int sort_by_file_write_desc(const void *pf1, const void *pf2)
+{
+ Quark f1 = *(Quark *)pf1;
+ Quark f2 = *(Quark *)pf2;
+ int total1, total2;
+ unsigned long birth1, birth2;
+ unsigned long start1 = i_start;
+ unsigned long start2 = i_start;
+
+ get_attribute_value_at_ulong(i_end, &f1, "file/birth", birth1);
+ get_attribute_value_at_ulong(i_end, &f2, "file/birth", birth2);
+ if (birth1 > i_start)
+ start1 = birth1;
+ if (birth2 > i_start)
+ start2 = birth2;
+
+ get_interval_value_int(start1, i_end, &f1, "file/write", total1);
+ get_interval_value_int(start2, i_end, &f2, "file/write", total2);
+
+ if (total1 > total2)
+ return 1;
+ if (total1 == total2)
+ return 0;
+ return -1;
+}
+
+int sort_by_file_fd_desc(const void *pf1, const void *pf2)
+{
+ Quark f1 = *(Quark *)pf1;
+ Quark f2 = *(Quark *)pf2;
+ int fd1, fd2;
+
+ get_attribute_value_at_int(i_end, &f1, "file/fd", fd1);
+ get_attribute_value_at_int(i_end, &f2, "file/fd", fd2);
+
+ if (fd1 > fd2)
+ return 1;
+ if (fd1 == fd2)
+ return 0;
+ return -1;
+}
+
+int sort_by_cpu_group_by_threads_desc(const void *pp1, const void *pp2)
+{
+ Quark p1 = *(Quark *)pp1;
+ Quark p2 = *(Quark *)pp2;
+ unsigned long total1, total2;
+ unsigned long birth1, birth2;
+ unsigned long start1 = i_start;
+ unsigned long start2 = i_start;
+
+ get_attribute_value_at_ulong(i_end, &p1, "birth", birth1);
+ get_attribute_value_at_ulong(i_end, &p2, "birth", birth2);
+ if (birth1 > i_start)
+ start1 = birth1;
+ if (birth2 > i_start)
+ start2 = birth2;
+
+ get_interval_value_ulong(start1, i_end, &p1, "threadstotalcpunsec",
+ total1);
+ get_interval_value_ulong(start2, i_end, &p2, "threadstotalcpunsec",
+ total2);
+
+ if (total1 > total2)
+ return 1;
+ if (total1 == total2)
+ return 0;
+ return -1;
+}
+
+void update_cputop_display()
+{
+ unsigned int i;
+ int header_offset = 2;
+ unsigned long elapsed;
+ double maxcputime;
+ int nblinedisplayed = 0;
+ int current_line = 0;
+ int column;
+ Quark *processes;
+ int tid, pid;
+ unsigned long totalcpunsec;
+ std::string comm;
+
+ Quark cpu_root = state_system->getQuark("cpu");
+ Quark proc_root = state_system->getQuark("proc");
+ unsigned int cpu_nb = get_sequence_length(i_end, cpu_root);
+ unsigned int proc_nb = get_sequence_length(i_end, proc_root);
+
+ elapsed = i_end - i_start;
+ maxcputime = elapsed * cpu_nb / 100.0;
+
+ processes = g_new(Quark, proc_nb);
+ sequence_to_array(i_end, proc_root, processes, proc_nb);
+
+ if (cputopview[0].sort == 1)
+ qsort(processes, proc_nb, sizeof(Quark), sort_by_cpu_desc);
+ else if (cputopview[1].sort == 1)
+ qsort(processes, proc_nb, sizeof(Quark), sort_by_pid_desc);
+ else if (cputopview[2].sort == 1)
+ qsort(processes, proc_nb, sizeof(Quark), sort_by_tid_desc);
+ else if (cputopview[3].sort == 1)
+ qsort(processes, proc_nb, sizeof(Quark), sort_by_cpu_desc);
+ else
+ qsort(processes, proc_nb, sizeof(Quark), sort_by_cpu_desc);
+
+ set_window_title(center, "CPU Top");
+ wattron(center, A_BOLD);
+ column = 1;
+ for (i = 0; i < 4; i++) {
+ if (cputopview[i].sort) {
+ wattron(center, A_UNDERLINE);
+ pref_current_sort = i;
+ }
+ mvwprintw(center, 1, column, cputopview[i].title);
+ wattroff(center, A_UNDERLINE);
+ column += 10;
+ }
+ wattroff(center, A_BOLD);
+
+ max_center_lines = LINES - 5 - 7 - 1 - header_offset;
+
+ /* iterate the process (thread) list */
+ for (i = list_offset; i < proc_nb && nblinedisplayed < max_center_lines;
+ i++) {
+ get_attribute_value_at_int(i_end, processes + i, "tid", tid);
+ get_attribute_value_at_int(i_end, processes + i, "pid", pid);
+
+ if (pid != tid)
+ if (toggle_threads == -1)
+ continue;
+
+ if (process_selected(processes[i])) {
+ wattron(center, COLOR_PAIR(6));
+ mvwhline(center, current_line + header_offset, 1, ' ',
+ COLS-3);
+ }
+ if (current_line == selected_line) {
+ selected_process = processes[i];
+ wattron(center, COLOR_PAIR(5));
+ mvwhline(center, current_line + header_offset, 1, ' ',
+ COLS-3);
+ }
+ /* CPU(%) */
+ get_interval_value_ulong(i_start, i_end, processes + i,
+ "totalcpunsec", totalcpunsec);
+ mvwprintw(center, current_line + header_offset, 1, "%1.2f",
+ totalcpunsec / maxcputime);
+ /* PID */
+ mvwprintw(center, current_line + header_offset, 11, "%d", pid);
+ /* TID */
+ mvwprintw(center, current_line + header_offset, 21, "%d", tid);
+ /* NAME */
+ get_attribute_value_at_string(i_end, processes + i, "comm",
+ comm);
+ mvwprintw(center, current_line + header_offset, 31, "%s",
+ comm.c_str());
+ wattroff(center, COLOR_PAIR(6));
+ wattroff(center, COLOR_PAIR(5));
+ nblinedisplayed++;
+ current_line++;
+ }
+
+ g_free(processes);
+}
+
+int sort_perf(const void *pp1, const void *pp2)
+{
+ Quark p1 = *(Quark *)pp1;
+ Quark p2 = *(Quark *)pp2;
+ std::string key = sort_perf_key;
+ int total1;
+ int total2;
+ int tid1;
+ int tid2;
+
+ if (!get_attribute_value_at_int(i_end, &p1, "perf/" + key + "/count",
+ total1))
+ total1 = 0;
+
+ if (!get_attribute_value_at_int(i_end, &p2, "perf/" + key + "/count",
+ total2))
+ total2 = 0;
+
+
+ if (total1 < total2)
+ return 1;
+ if (total1 == total2) {
+ get_attribute_value_at_int(i_end, &p1, "tid", tid1);
+ get_attribute_value_at_int(i_end, &p2, "tid", tid2);
+ if (tid1 < tid2)
+ return 1;
+ return -1;
+ }
+ return -1;
+}
+
+void print_key_title(const char key[], int line)
+{
+ wattron(center, A_BOLD);
+ mvwprintw(center, line, 1, "%s", key);
+ mvwprintw(center, line, 30, " ");
+ wattroff(center, A_BOLD);
+}
+
+void update_process_details()
+{
+ unsigned long elapsed;
+ double maxcputime;
+ unsigned int i, j = 0;
+ char unit[4];
+ char filename_buf[COLS];
+ int line = 1;
+ int column;
+ unsigned int cpu_nb;
+ std::string comm;
+ int tid = -1, pid, ppid, fileread, filewrite;
+ unsigned long totalcpunsec;
+ Quark *global_perf;
+ unsigned int global_perf_nb;
+ std::string key;
+ int count;
+ Quark *files;
+ unsigned int file_nb;
+ int fd, read, write;
+ std::string name;
+
+ set_window_title(center, "Process details");
+
+ elapsed = i_end - i_start;
+ cpu_nb = get_sequence_length(i_end, state_system->getQuark("cpu"));
+ maxcputime = elapsed * cpu_nb / 100.0;
+
+ get_attribute_value_at_int(i_end, &selected_process, "tid", tid);
+ if (tid == -1) {
+ print_key_title("Does not exit at this time", 3);
+ return;
+ }
+
+ print_key_title("Name", line++);
+ get_attribute_value_at_string(i_end, &selected_process, "comm", comm);
+ wprintw(center, "%s", comm.c_str());
+ print_key_title("TID", line++);
+ wprintw(center, "%d", tid);
+
+ print_key_title("PID", line++);
+ get_attribute_value_at_int(i_end, &selected_process, "pid", pid);
+ wprintw(center, "%d", pid);
+ print_key_title("PPID", line++);
+ get_attribute_value_at_int(i_end, &selected_process, "ppid", ppid);
+ wprintw(center, "%d", ppid);
+ print_key_title("CPU", line++);
+ get_attribute_value_at_ulong(i_end, &selected_process, "totalcpunsec",
+ totalcpunsec);
+ wprintw(center, "%1.2f %%", totalcpunsec/maxcputime);
+
+ print_key_title("READ B/s", line++);
+ get_attribute_value_at_int(i_end, &selected_process, "fileread",
+ fileread);
+ scale_unit(fileread, unit);
+ wprintw(center, "%s", unit);
+
+ print_key_title("WRITE B/s", line++);
+ get_attribute_value_at_int(i_end, &selected_process, "filewrite",
+ filewrite);
+ scale_unit(filewrite, unit);
+ wprintw(center, "%s", unit);
+
+
+ global_perf_nb = get_global_perf_list_size(i_end);
+ global_perf = g_new(Quark, global_perf_nb);
+ get_global_perf_list(i_end, global_perf, global_perf_nb);
+ for (i = 0; i < global_perf_nb; i++) {
+ get_attribute_value_at_string(i_end, global_perf + i, "key",
+ key);
+ print_key_title(key.c_str(), line++);
+ if (!get_attribute_value_at_int(i_end, global_perf + i, "count",
+ count))
+ count = 0;
+ wprintw(center, "%d", count);
+ }
+ line++;
+
+ wattron(center, A_BOLD);
+ column = 1;
+ for (i = 0; i < 3; i++) {
+ if (fileview[i].sort) {
+ pref_current_sort = i;
+ wattron(center, A_UNDERLINE);
+ }
+ mvwprintw(center, line, column, fileview[i].title);
+ wattroff(center, A_UNDERLINE);
+ column += 10;
+ }
+ mvwprintw(center, line++, column, "FILENAME");
+ wattroff(center, A_BOLD);
+
+ file_nb = get_number_of_opened_files(i_end, selected_process);
+ files = g_new(Quark, file_nb);
+ get_opened_files(i_end, selected_process, files, file_nb);
+
+ if (fileview[0].sort == 1)
+ qsort(files, file_nb, sizeof(Quark), sort_by_file_fd_desc);
+ else if (fileview[1].sort == 1)
+ qsort(files, file_nb, sizeof(Quark), sort_by_file_read_desc);
+ else if (fileview[2].sort == 1)
+ qsort(files, file_nb, sizeof(Quark), sort_by_file_write_desc);
+ else
+ qsort(files, file_nb, sizeof(Quark), sort_by_file_fd_desc);
+
+ for (i = selected_line;
+ i < file_nb && i < (selected_line + max_center_lines - line + 2);
+ i++) {
+ get_attribute_value_at_int(i_end, files + i, "fd", fd);
+ mvwprintw(center, line + j, 1, "%d", fd);
+ get_attribute_value_at_int(i_end, files + i, "read", read);
+ scale_unit(read, unit);
+ mvwprintw(center, line + j, 11, "%s", unit);
+ get_attribute_value_at_int(i_end, files + i, "write", write);
+ scale_unit(write, unit);
+ mvwprintw(center, line + j, 21, "%s", unit);
+ get_attribute_value_at_string(i_end, files + i, "name", name);
+ snprintf(filename_buf, COLS - 25, "%s", name.c_str());
+ mvwprintw(center, line + j, 31, "%s", filename_buf);
+ j++;
+ }
+ g_free(global_perf);
+ g_free(files);
+}
+
+void update_perf()
+{
+ unsigned int i, j;
+ int nblinedisplayed = 0;
+ int current_line = 0;
+ int header_offset = 2;
+ int perf_row = 40;
+ int value;
+ unsigned int global_perf_nb;
+ Quark *global_perf;
+ int sort = 0;
+ int visible = 0;
+ std::string key;
+ bool sort_exists;
+ unsigned int proc_nb;
+ Quark *procs;
+ Quark proc_beg;
+ int tid, pid;
+ std::string comm;
+
+ set_window_title(center, "Perf Top");
+ wattron(center, A_BOLD);
+ mvwprintw(center, 1, 1, "PID");
+ mvwprintw(center, 1, 11, "TID");
+ mvwprintw(center, 1, 22, "NAME");
+
+ perf_row = 40;
+ global_perf_nb = get_global_perf_list_size(i_end);
+ global_perf = g_new(Quark, global_perf_nb);
+ get_global_perf_list(i_end, global_perf, global_perf_nb);
+ for (i = 0; i < global_perf_nb; i++) {
+ get_attribute_value_at_int(i_end, global_perf + i, "visible",
+ visible);
+ get_attribute_value_at_string(i_end, global_perf + i, "key",
+ key);
+ sort_exists = get_attribute_value_at_int(i_end, global_perf + i,
+ "sort", sort);
+ if (visible) {
+ if (sort_exists && sort) {
+ wattron(center, A_UNDERLINE);
+ }
+ /* + 5 to strip the "perf_" prefix */
+ mvwprintw(center, 1, perf_row, "%s",
+ key.c_str() + 5);
+ wattroff(center, A_UNDERLINE);
+ perf_row += 20;
+ }
+ if (sort_exists && sort) {
+ sort_perf_key = key;
+ }
+ }
+ wattroff(center, A_BOLD);
+
+ proc_beg = state_system->getQuark("proc");
+ proc_nb = get_sequence_length(i_end, proc_beg);
+ procs = g_new(Quark, proc_nb);
+ sequence_to_array(i_end, proc_beg, procs, proc_nb);
+ for (i = 0; i < proc_nb && nblinedisplayed < max_center_lines; i++) {
+ get_attribute_value_at_int(i_end, procs + i, "tid", tid);
+ get_attribute_value_at_int(i_end, procs + i, "pid", pid);
+ get_attribute_value_at_string(i_end, procs + 1, "comm", comm);
+
+ if (pid != tid)
+ if (toggle_threads == -1)
+ continue;
+
+ if (process_selected(procs[i])) {
+ wattron(center, COLOR_PAIR(6));
+ mvwhline(center, current_line + header_offset, 1, ' ',
+ COLS-3);
+ }
+ if (current_line == selected_line) {
+ selected_process = procs[i];
+ wattron(center, COLOR_PAIR(5));
+ mvwhline(center, current_line + header_offset, 1, ' ',
+ COLS-3);
+ }
+
+ mvwprintw(center, current_line + header_offset, 1, "%d", pid);
+ mvwprintw(center, current_line + header_offset, 11, "%d", tid);
+ mvwprintw(center, current_line + header_offset, 22, "%s",
+ comm.c_str());
+
+ perf_row = 40;
+ for (j = 0; j < global_perf_nb; j++) {
+ get_attribute_value_at_int(i_end, global_perf + j,
+ "visible", visible);
+ get_attribute_value_at_string(i_end, global_perf + j,
+ "key", key);
+
+ if (visible) {
+ if (!get_interval_value_int(i_start, i_end,
+ procs + i, "perf/" +
+ key + "/count",
+ value)) {
+ value = 0;
+ }
+ mvwprintw(center, current_line + header_offset,
+ perf_row, "%d", value);
+ perf_row += 20;
+ }
+ }
+
+ wattroff(center, COLOR_PAIR(6));
+ wattroff(center, COLOR_PAIR(5));
+ nblinedisplayed++;
+ current_line++;
+ }
+ g_free(global_perf);
+ g_free(procs);
+}
+
+void update_iostream()
+{
+ unsigned int i;
+ int header_offset = 2;
+ int nblinedisplayed = 0;
+ int current_line = 0;
+ char unit[4];
+ int column;
+ unsigned int proc_nb;
+ Quark *procs;
+ Quark proc_root;
+ int pid, tid, read, write, totalread, totalwrite;
+ std::string comm;
+
+ set_window_title(center, "IO Top");
+ wattron(center, A_BOLD);
+ mvwprintw(center, 1, 1, "PID");
+ mvwprintw(center, 1, 11, "TID");
+ mvwprintw(center, 1, 22, "NAME");
+ column = 40;
+ for (i = 0; i < 3; i++) {
+ if (iostreamtopview[i].sort) {
+ pref_current_sort = i;
+ wattron(center, A_UNDERLINE);
+ }
+ mvwprintw(center, 1, column, iostreamtopview[i].title);
+ wattroff(center, A_UNDERLINE);
+ column += 12;
+ }
+ wattroff(center, A_BOLD);
+ wattroff(center, A_UNDERLINE);
+
+ proc_root = state_system->getQuark("proc");
+ proc_nb = get_sequence_length(i_end, proc_root);
+ procs = g_new(Quark, proc_nb);
+ sequence_to_array(i_end, proc_root, procs, proc_nb);
+
+ if (iostreamtopview[0].sort == 1)
+ qsort(procs, proc_nb, sizeof(Quark), sort_by_process_read_desc);
+ else if (iostreamtopview[1].sort == 1)
+ qsort(procs, proc_nb, sizeof(Quark),
+ sort_by_process_write_desc);
+ else if (iostreamtopview[2].sort == 1)
+ qsort(procs, proc_nb, sizeof(Quark),
+ sort_by_process_total_desc);
+ else
+ qsort(procs, proc_nb, sizeof(Quark),
+ sort_by_process_total_desc);
+
+ for (i = list_offset; i < proc_nb && nblinedisplayed < max_center_lines;
+ i++) {
+ get_attribute_value_at_int(i_end, procs + i, "tid", tid);
+ get_attribute_value_at_int(i_end, procs + i, "pid", pid);
+ get_attribute_value_at_string(i_end, procs + i, "comm", comm);
+ get_attribute_value_at_int(i_end, procs + i, "fileread",
+ read);
+ get_attribute_value_at_int(i_end, procs + i, "filewrite",
+ write);
+ get_attribute_value_at_int(i_end, procs + i, "totalfileread",
+ totalread);
+ get_attribute_value_at_int(i_end, procs + i, "totalfilewrite",
+ totalwrite);
+
+
+ if (pid != tid)
+ if (toggle_threads == -1)
+ continue;
+
+ if (process_selected(procs[i])) {
+ wattron(center, COLOR_PAIR(6));
+ mvwhline(center, current_line + header_offset, 1, ' ',
+ COLS-3);
+ }
+ if (current_line == selected_line) {
+ selected_process = procs[i];
+ wattron(center, COLOR_PAIR(5));
+ mvwhline(center, current_line + header_offset, 1, ' ',
+ COLS-3);
+ }
+ /* PID */
+ mvwprintw(center, current_line + header_offset, 1, "%d", pid);
+ /* TID */
+ mvwprintw(center, current_line + header_offset, 11, "%d", tid);
+ /* NAME */
+ mvwprintw(center, current_line + header_offset, 22, "%s",
+ comm.c_str());
+
+ /* READ (bytes/sec) */
+ scale_unit(read, unit);
+ mvwprintw(center, current_line + header_offset, 40, "%s", unit);
+
+ /* WRITE (bytes/sec) */
+ scale_unit(write, unit);
+ mvwprintw(center, current_line + header_offset, 52, "%s", unit);
+
+ /* TOTAL STREAM */
+ scale_unit(totalread + totalwrite, unit);
+ mvwprintw(center, current_line + header_offset, 64, "%s", unit);
+
+ wattroff(center, COLOR_PAIR(6));
+ wattroff(center, COLOR_PAIR(5));
+ nblinedisplayed++;
+ current_line++;
+ }
+ g_free(procs);
+}
+
+void update_current_view()
+{
+ sem_wait(&update_display_sem);
+ update_header();
+
+ werase(center);
+ box(center, 0, 0);
+ switch (current_view) {
+ case cpu:
+ update_cputop_display();
+ break;
+ case perf:
+ update_perf();
+ break;
+ case process_details:
+ update_process_details();
+ break;
+ case iostream:
+ update_iostream();
+ break;
+ case tree:
+ update_cputop_display();
+ break;
+ default:
+ break;
+ }
+ update_panels();
+ doupdate();
+ sem_post(&update_display_sem);
+}
+
+void update_process_detail_sort(int *line_selected)
+{
+ int i;
+ int size;
+
+ size = 3;
+
+ if (*line_selected > (size - 1))
+ *line_selected = size - 1;
+ else if (*line_selected < 0)
+ *line_selected = 0;
+
+ if (fileview[*line_selected].sort == 1)
+ fileview[*line_selected].reverse = 1;
+ for (i = 0; i < size; i++)
+ fileview[i].sort = 0;
+ fileview[*line_selected].sort = 1;
+}
+
+void update_process_detail_pref(int *line_selected, int toggle_view, int toggle_sort)
+{
+ int i;
+ int size;
+
+ if (pref_panel_window) {
+ del_panel(pref_panel);
+ delwin(pref_panel_window);
+ }
+ size = 3;
+
+ pref_panel_window = create_window(size + 2, 30, 10, 10);
+ pref_panel = new_panel(pref_panel_window);
+
+ werase(pref_panel_window);
+ box(pref_panel_window, 0 , 0);
+ set_window_title(pref_panel_window, "Process Detail Preferences ");
+ wattron(pref_panel_window, A_BOLD);
+ mvwprintw(pref_panel_window, size + 1, 1,
+ " 's' : sort, space : toggle");
+ wattroff(pref_panel_window, A_BOLD);
+
+ if (*line_selected > (size - 1))
+ *line_selected = size - 1;
+ else if (*line_selected < 0)
+ *line_selected = 0;
+ if (toggle_sort == 1) {
+ update_process_detail_sort(line_selected);
+ update_current_view();
+ }
+
+ for (i = 0; i < size; i++) {
+ if (i == *line_selected) {
+ wattron(pref_panel_window, COLOR_PAIR(5));
+ mvwhline(pref_panel_window, i + 1, 1, ' ', 30 - 2);
+ }
+ if (fileview[i].sort == 1)
+ wattron(pref_panel_window, A_BOLD);
+ mvwprintw(pref_panel_window, i + 1, 1, "[-] %s",
+ fileview[i].title);
+ wattroff(pref_panel_window, A_BOLD);
+ wattroff(pref_panel_window, COLOR_PAIR(5));
+
+ }
+ update_panels();
+ doupdate();
+}
+
+void update_iostream_sort(int *line_selected)
+{
+ int i;
+ int size;
+
+ size = 3;
+ if (*line_selected > (size - 1))
+ *line_selected = size - 1;
+ else if (*line_selected < 0)
+ *line_selected = 0;
+ if (iostreamtopview[*line_selected].sort == 1)
+ iostreamtopview[*line_selected].reverse = 1;
+ for (i = 0; i < size; i++)
+ iostreamtopview[i].sort = 0;
+ iostreamtopview[*line_selected].sort = 1;
+
+}
+
+void update_iostream_pref(int *line_selected, int toggle_view, int toggle_sort)
+{
+ int i;
+ int size;
+
+ if (pref_panel_window) {
+ del_panel(pref_panel);
+ delwin(pref_panel_window);
+ }
+ size = 3;
+
+ pref_panel_window = create_window(size + 2, 30, 10, 10);
+ pref_panel = new_panel(pref_panel_window);
+
+ werase(pref_panel_window);
+ box(pref_panel_window, 0 , 0);
+ set_window_title(pref_panel_window, "IOTop Preferences ");
+ wattron(pref_panel_window, A_BOLD);
+ mvwprintw(pref_panel_window, size + 1, 1,
+ " 's' : sort, space : toggle");
+ wattroff(pref_panel_window, A_BOLD);
+
+ if (*line_selected > (size - 1))
+ *line_selected = size - 1;
+ else if (*line_selected < 0)
+ *line_selected = 0;
+ if (toggle_sort == 1) {
+ update_iostream_sort(line_selected);
+ update_current_view();
+ }
+
+ for (i = 0; i < size; i++) {
+ if (i == *line_selected) {
+ wattron(pref_panel_window, COLOR_PAIR(5));
+ mvwhline(pref_panel_window, i + 1, 1, ' ', 30 - 2);
+ }
+ if (iostreamtopview[i].sort == 1)
+ wattron(pref_panel_window, A_BOLD);
+ mvwprintw(pref_panel_window, i + 1, 1, "[-] %s",
+ iostreamtopview[i].title);
+ wattroff(pref_panel_window, A_BOLD);
+ wattroff(pref_panel_window, COLOR_PAIR(5));
+
+ }
+ update_panels();
+ doupdate();
+}
+
+void update_cpu_sort(int *line_selected)
+{
+ int i;
+ int size = 3;
+
+ if (*line_selected > (size - 1))
+ *line_selected = size - 1;
+ else if (*line_selected < 0)
+ *line_selected = 0;
+
+ /* special case, we don't support sorting by procname for now */
+ if (*line_selected != 3) {
+ if (cputopview[*line_selected].sort == 1)
+ cputopview[*line_selected].reverse = 1;
+ for (i = 0; i < size; i++)
+ cputopview[i].sort = 0;
+ cputopview[*line_selected].sort = 1;
+ }
+}
+
+void update_cpu_pref(int *line_selected, int toggle_view, int toggle_sort)
+{
+ int i;
+ int size;
+
+ if (pref_panel_window) {
+ del_panel(pref_panel);
+ delwin(pref_panel_window);
+ }
+ size = 4;
+
+ pref_panel_window = create_window(size + 2, 30, 10, 10);
+ pref_panel = new_panel(pref_panel_window);
+
+ werase(pref_panel_window);
+ box(pref_panel_window, 0 , 0);
+ set_window_title(pref_panel_window, "CPUTop Preferences ");
+ wattron(pref_panel_window, A_BOLD);
+ mvwprintw(pref_panel_window, size + 1, 1,
+ " 's' : sort, space : toggle");
+ wattroff(pref_panel_window, A_BOLD);
+
+ if (*line_selected > (size - 1))
+ *line_selected = size - 1;
+ else if (*line_selected < 0)
+ *line_selected = 0;
+ if (toggle_sort == 1) {
+ update_cpu_sort(line_selected);
+ update_current_view();
+ }
+
+ for (i = 0; i < size; i++) {
+ if (i == *line_selected) {
+ wattron(pref_panel_window, COLOR_PAIR(5));
+ mvwhline(pref_panel_window, i + 1, 1, ' ', 30 - 2);
+ }
+ if (cputopview[i].sort == 1)
+ wattron(pref_panel_window, A_BOLD);
+ mvwprintw(pref_panel_window, i + 1, 1, "[-] %s",
+ cputopview[i].title);
+ wattroff(pref_panel_window, A_BOLD);
+ wattroff(pref_panel_window, COLOR_PAIR(5));
+
+ }
+ update_panels();
+ doupdate();
+}
+
+void update_perf_sort(int *line_selected)
+{
+ int i;
+ int size;
+ Quark *global_perf;
+
+ size = get_global_perf_list_size(i_end);
+ global_perf = g_new(Quark, size);
+ get_global_perf_list(i_end, global_perf, size);
+
+ if (*line_selected > (size - 1))
+ *line_selected = size - 1;
+ else if (*line_selected < 0)
+ *line_selected = 0;
+
+ for (i = 0; i < size; i++) {
+ if (i != *line_selected)
+ modify_attribute(i_end, global_perf + i, "sort", 0);
+ else
+ modify_attribute(i_end, global_perf + i, "sort", 1);
+ }
+
+ g_free(global_perf);
+}
+
+void update_perf_pref(int *line_selected, int toggle_view, int toggle_sort)
+{
+ int i;
+ int size;
+ Quark *global_perf;
+ int visible;
+ int sort;
+ std::string key;
+
+ if (pref_panel_window) {
+ del_panel(pref_panel);
+ delwin(pref_panel_window);
+ }
+
+ size = get_global_perf_list_size(i_end);
+ global_perf = g_new(Quark, size);
+ get_global_perf_list(i_end, global_perf, size);
+
+ pref_panel_window = create_window(size + 2, 30, 10, 10);
+ pref_panel = new_panel(pref_panel_window);
+
+ werase(pref_panel_window);
+ box(pref_panel_window, 0 , 0);
+ set_window_title(pref_panel_window, "Perf Preferences ");
+ wattron(pref_panel_window, A_BOLD);
+ mvwprintw(pref_panel_window, size + 1, 1,
+ " 's' : sort, space : toggle");
+ wattroff(pref_panel_window, A_BOLD);
+
+ if (*line_selected > (size - 1))
+ *line_selected = size - 1;
+ else if (*line_selected < 0)
+ *line_selected = 0;
+
+ if (toggle_sort == 1) {
+ update_perf_sort(line_selected);
+ update_current_view();
+ }
+
+ for (i = 0; i < size; i++) {
+ get_attribute_value_at_int(i_end, global_perf + i,
+ "visible", visible);
+ get_attribute_value_at_string(i_end, global_perf + i, "key",
+ key);
+ if (i == *line_selected && toggle_view == 1) {
+ visible = visible == 1 ? 0 : 1;
+ modify_attribute(i_end, global_perf + i, "visible",
+ visible);
+ update_current_view();
+ }
+ if (i == *line_selected) {
+ wattron(pref_panel_window, COLOR_PAIR(5));
+ mvwhline(pref_panel_window, i + 1, 1, ' ', 30 - 2);
+ }
+
+ if (get_attribute_value_at_int(i_end, global_perf + 1, "sort",
+ sort) && sort) {
+ wattron(pref_panel_window, A_BOLD);
+ }
+ mvwprintw(pref_panel_window, i + 1, 1, "[%c] %s",
+ visible == 1 ? 'x' : ' ', key.c_str() + 5);
+ wattroff(pref_panel_window, A_BOLD);
+ wattroff(pref_panel_window, COLOR_PAIR(5));
+ }
+ update_panels();
+ doupdate();
+
+ g_free(global_perf);
+}
+
+int update_preference_panel(int *line_selected, int toggle_view, int toggle_sort)
+{
+ int ret = 0;
+
+ switch(current_view) {
+ case perf:
+ update_perf_pref(line_selected, toggle_view, toggle_sort);
+ break;
+ case cpu:
+ update_cpu_pref(line_selected, toggle_view, toggle_sort);
+ break;
+ case iostream:
+ update_iostream_pref(line_selected, toggle_view, toggle_sort);
+ break;
+ case process_details:
+ update_process_detail_pref(line_selected, toggle_view, toggle_sort);
+ break;
+ default:
+ ret = -1;
+ break;
+ }
+
+ return ret;
+}
+
+int update_sort(int *line_selected)
+{
+ int ret = 0;
+
+ switch(current_view) {
+ case perf:
+ update_perf_sort(line_selected);
+ break;
+ case cpu:
+ update_cpu_sort(line_selected);
+ break;
+ case iostream:
+ update_iostream_sort(line_selected);
+ break;
+ case process_details:
+ update_process_detail_sort(line_selected);
+ break;
+ default:
+ ret = -1;
+ break;
+ }
+
+ return ret;
+}
+
+
+void toggle_pref_panel(void)
+{
+ int ret;
+
+ if (pref_panel_visible) {
+ hide_panel(pref_panel);
+ pref_panel_visible = 0;
+ } else {
+ ret = update_preference_panel(&pref_line_selected, 0, 0);
+ if (ret < 0)
+ return;
+ show_panel(pref_panel);
+ pref_panel_visible = 1;
+ }
+ update_panels();
+ doupdate();
+}
+
+void display()
+{
+ if (i_start == 0)
+ i_start = first_display_update;
+ else
+ i_start += refresh_display;
+ i_end = i_start + refresh_display;
+
+ max_elements = get_sequence_length(
+ i_end, state_system->getQuark("proc"));
+ update_current_view();
+ update_footer();
+ update_panels();
+ doupdate();
+}
+
+void pause_display()
+{
+ toggle_pause = 1;
+ print_log("Pause");
+ sem_wait(&pause_sem);
+}
+
+void resume_display()
+{
+ toggle_pause = -1;
+ print_log("Resume");
+ sem_post(&pause_sem);
+}
+
+void *handle_keyboard(void *p)
+{
+ int ch;
+ while((ch = getch())) {
+ switch(ch) {
+ /* Move the cursor and scroll */
+ case 'j':
+ case KEY_DOWN:
+ if (pref_panel_visible) {
+ pref_line_selected++;
+ update_preference_panel(&pref_line_selected, 0, 0);
+ } else {
+ if (selected_line < (max_center_lines - 1) &&
+ selected_line < max_elements - 1) {
+ selected_line++;
+ selected_in_list++;
+ } else if (selected_in_list < (max_elements - 1)
+ && (list_offset < (max_elements - max_center_lines))) {
+ selected_in_list++;
+ list_offset++;
+ }
+ update_current_view();
+ }
+ break;
+ case KEY_NPAGE:
+ break;
+ case 'k':
+ case KEY_UP:
+ if (pref_panel_visible) {
+ if (pref_line_selected > 0)
+ pref_line_selected--;
+ update_preference_panel(&pref_line_selected, 0, 0);
+ } else {
+ if (selected_line > 0) {
+ selected_line--;
+ selected_in_list--;
+ } else if (selected_in_list > 0 && list_offset > 0) {
+ selected_in_list--;
+ list_offset--;
+ }
+ update_current_view();
+ }
+ break;
+ case KEY_PPAGE:
+ break;
+
+ /* Navigate the history with arrows */
+ case KEY_LEFT:
+ if ((i_start - refresh_display) >=
+ first_display_update) {
+ i_start -= refresh_display;
+ i_end = i_start + refresh_display;
+ print_log("Going back in time");
+ } else {
+ print_log("Cannot rewind, last data is already displayed");
+ }
+ max_elements = get_sequence_length(
+ i_end, state_system->getQuark("proc"));
+
+ /* we force to pause the display when moving in time */
+ if (toggle_pause < 0)
+ pause_display();
+
+ update_current_view();
+ update_footer();
+ break;
+ case KEY_RIGHT:
+ if ((i_end + refresh_display) <=
+ last_display_update) {
+ i_end += refresh_display;
+ i_start = i_end - refresh_display;
+ print_log("Going forward in time");
+ max_elements = get_sequence_length(
+ i_end, state_system->getQuark(
+ "proc"));
+ update_current_view();
+ update_footer();
+ } else {
+ print_log("Manually moving forward");
+ sem_post(&timer);
+ if (toggle_pause > 0) {
+ sem_post(&pause_sem);
+ update_current_view();
+ sem_wait(&pause_sem);
+ }
+ }
+
+ break;
+ case ' ':
+ if (pref_panel_visible) {
+ update_preference_panel(&pref_line_selected, 1, 0);
+ } else {
+ update_selected_processes();
+ update_current_view();
+ }
+ break;
+ case 's':
+ if (pref_panel_visible)
+ update_preference_panel(&pref_line_selected, 0, 1);
+ break;
+ case '>':
+ /* perf uses a hashtable, it is ordered backward */
+ if (current_view == perf) {
+ pref_current_sort--;
+ } else if (!pref_panel_visible) {
+ pref_current_sort++;
+ }
+ update_sort(&pref_current_sort);
+ update_current_view();
+ break;
+ case '<':
+ /* perf uses a hashtable, it is ordered backward */
+ if (current_view == perf) {
+ pref_current_sort++;
+ } else if (!pref_panel_visible) {
+ pref_current_sort--;
+ }
+ update_sort(&pref_current_sort);
+ update_current_view();
+ break;
+
+ case 13: /* FIXME : KEY_ENTER ?? */
+ if (pref_panel_visible)
+ break;
+ if (current_view != process_details) {
+ previous_view = current_view;
+ current_view = process_details;
+ } else {
+ current_view = previous_view;
+ previous_view = process_details;
+ }
+ update_current_view();
+ break;
+
+ case KEY_F(1):
+ if (pref_panel_visible)
+ toggle_pref_panel();
+ current_view = cpu;
+ selected_line = 0;
+ update_current_view();
+ break;
+ case KEY_F(2):
+ if (pref_panel_visible)
+ toggle_pref_panel();
+ current_view = cpu;
+ selected_line = 0;
+ update_current_view();
+ break;
+ case KEY_F(3):
+ if (pref_panel_visible)
+ toggle_pref_panel();
+ current_view = perf;
+ selected_line = 0;
+ update_current_view();
+ break;
+ case KEY_F(4):
+ if (pref_panel_visible)
+ toggle_pref_panel();
+ current_view = iostream;
+ selected_line = 0;
+ update_current_view();
+ break;
+ case KEY_F(10):
+ case 'q':
+ reset_ncurses();
+ break;
+ case 't':
+ toggle_threads *= -1;
+ update_current_view();
+ break;
+ case 'p':
+ if (toggle_pause < 0) {
+ pause_display();
+ } else {
+ resume_display();
+ }
+ break;
+ case 'r':
+ toggle_pref_panel();
+ break;
+ /* ESCAPE, but slow to process, don't know why */
+ case 27:
+ if (pref_panel_visible)
+ toggle_pref_panel();
+ else if (current_view == process_details) {
+ current_view = previous_view;
+ previous_view = process_details;
+ }
+ update_current_view();
+ break;
+ default:
+ update_current_view();
+ break;
+ }
+ update_footer();
+ }
+ return NULL;
+}
+
+void init_view_headers()
+{
+ cputopview[0].title = strdup("CPU(%)");
+ cputopview[0].sort = 1;
+ cputopview[1].title = strdup("PID");
+ cputopview[2].title = strdup("TID");
+ cputopview[3].title = strdup("NAME");
+
+ iostreamtopview[0].title = strdup("R (B/sec)");
+ iostreamtopview[1].title = strdup("W (B/sec)");
+ iostreamtopview[2].title = strdup("Total (B)");
+ iostreamtopview[2].sort = 1;
+
+ fileview[0].title = strdup("FD");
+ fileview[1].title = strdup("READ");
+ fileview[1].sort = 1;
+ fileview[2].title = strdup("WRITE");
+}
+
+void init_ncurses()
+{
+ selected_processes = g_ptr_array_new();
+ sem_init(&update_display_sem, 0, 1);
+ init_view_headers();
+ init_screen();
+
+ header = create_window(5, COLS - 1, 0, 0);
+ center = create_window(LINES - 5 - 7, COLS - 1, 5, 0);
+ status = create_window(MAX_LOG_LINES + 2, COLS - 1, LINES - 7, 0);
+ footer = create_window(1, COLS - 1, LINES - 1, 0);
+
+ print_log("Starting display");
+
+ main_panel = new_panel(center);
+
+ current_view = cpu;
+
+ basic_header();
+ update_footer();
+
+ pthread_create(&keyboard_thread, NULL, handle_keyboard, (void *)NULL);
+}
#define CURSESDISPLAY_H
#include <glib.h>
+extern "C" {
#include <ncurses.h>
+}
#include "common.h"
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();
+++ /dev/null
-/*
- * Copyright (C) 2011-2012 Mathieu Bain <mathieu.bain@polymtl.ca>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License Version 2 as
- * published by the Free Software Foundation;
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include <stdlib.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <string.h>
-#include <babeltrace/babeltrace.h>
-
-#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;
-}
--- /dev/null
+/*
+ * Copyright (C) 2011-2012 Mathieu Bain <mathieu.bain@polymtl.ca>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+extern "C"
+{
+#include <babeltrace/babeltrace.h>
+}
+
+#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;
+}
#ifndef _IOSTREANTOP_H
#define _IOSTREAMTOP_H
+extern "C" {
#include <babeltrace/babeltrace.h>
#include <babeltrace/ctf/events.h>
+}
#include <inttypes.h>
#include <glib.h>
#include <asm/unistd.h>
-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 */
--- /dev/null
+#include "libStateIntegrationPrototype.h"
+
+
--- /dev/null
+#ifndef _LIB_STATE_INTEGRATION_PROTOTYPE_H_
+#define _LIB_STATE_INTEGRATION_PROTOTYPE_H_
+
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+#include <babeltrace/babeltrace.h>
+#include <babeltrace/ctf/callbacks.h>
+#include <babeltrace/ctf/events.h>
+#include <asm/unistd.h>
+#include "common.h"
+#ifdef __cplusplus
+}
+#endif
+
+
+
+const unsigned long p_refresh_display = 1 * NSEC_PER_SEC;
+
+
+
+
+/* #ifdef __cplusplus */
+/* } */
+/* #endif */
+
+#endif
+
+++ /dev/null
-/*
- * Copyright (C) 2011-2012 Julien Desfossez
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License Version 2 as
- * published by the Free Software Foundation;
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#define _GNU_SOURCE
-#include <config.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <babeltrace/babeltrace.h>
-#include <babeltrace/ctf/events.h>
-#include <babeltrace/ctf/callbacks.h>
-#include <babeltrace/ctf/iterator.h>
-#include <fcntl.h>
-#include <pthread.h>
-#include <popt.h>
-#include <stdlib.h>
-#include <ftw.h>
-#include <dirent.h>
-#include <ctype.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <string.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <fts.h>
-#include <assert.h>
-
-#include "lttngtoptypes.h"
-#include "cputop.h"
-#include "iostreamtop.h"
-#include "cursesdisplay.h"
-#include "common.h"
-
-#define DEFAULT_FILE_ARRAY_SIZE 1
-
-const char *opt_input_path;
-
-struct lttngtop *copy;
-pthread_t display_thread;
-pthread_t timer_thread;
-
-unsigned long refresh_display = 1 * NSEC_PER_SEC;
-unsigned long last_display_update = 0;
-int quit = 0;
-
-enum {
- OPT_NONE = 0,
- OPT_HELP,
- OPT_LIST,
- OPT_VERBOSE,
- OPT_DEBUG,
- OPT_NAMES,
-};
-
-static struct poptOption long_options[] = {
- /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
- { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL },
- { NULL, 0, 0, NULL, 0, NULL, NULL },
-};
-
-void *refresh_thread(void *p)
-{
- while (1) {
- if (quit)
- return NULL;
- sem_wait(&pause_sem);
- sem_post(&pause_sem);
- sem_post(&timer);
- sleep(refresh_display/NSEC_PER_SEC);
- }
-}
-
-void *ncurses_display(void *p)
-{
- unsigned int current_display_index = 0;
-
- sem_wait(&bootstrap);
- /*
- * Prevent the 1 second delay when we hit ESC
- */
- ESCDELAY = 0;
- init_ncurses();
-
- while (1) {
- sem_wait(&timer);
- sem_wait(&goodtodisplay);
- sem_wait(&pause_sem);
-
- copy = g_ptr_array_index(copies, current_display_index);
- assert(copy);
- display(current_display_index++);
-
- sem_post(&goodtoupdate);
- sem_post(&pause_sem);
-
- if (quit) {
- reset_ncurses();
- pthread_exit(0);
- }
- }
-}
-
-/*
- * hook on each event to check the timestamp and refresh the display if
- * necessary
- */
-enum bt_cb_ret check_timestamp(struct bt_ctf_event *call_data, void *private_data)
-{
- unsigned long timestamp;
-
- timestamp = bt_ctf_get_timestamp(call_data);
- if (timestamp == -1ULL)
- goto error;
-
- if (last_display_update == 0)
- last_display_update = timestamp;
-
- if (timestamp - last_display_update >= refresh_display) {
- sem_wait(&goodtoupdate);
- g_ptr_array_add(copies, get_copy_lttngtop(last_display_update,
- timestamp));
- sem_post(&goodtodisplay);
- sem_post(&bootstrap);
- last_display_update = timestamp;
- }
- return BT_CB_OK;
-
-error:
- fprintf(stderr, "check_timestamp callback error\n");
- return BT_CB_ERROR_STOP;
-}
-
-/*
- * get_perf_counter : get or create and return a perf_counter struct for
- * either a process or a cpu (only one of the 2 parameters mandatory)
- */
-struct perfcounter *get_perf_counter(const char *name, struct processtop *proc,
- struct cputime *cpu)
-{
- struct perfcounter *ret;
- GHashTable *table;
-
- if (proc)
- table = proc->perf;
- else if (cpu)
- table = cpu->perf;
- else
- goto error;
-
- ret = g_hash_table_lookup(table, (gpointer) name);
- if (ret)
- goto end;
-
- ret = g_new0(struct perfcounter, 1);
- /* by default, make it visible in the UI */
- ret->visible = 1;
- g_hash_table_insert(table, (gpointer) strdup(name), ret);
-
-end:
- return ret;
-
-error:
- return NULL;
-}
-
-void update_perf_value(struct processtop *proc, struct cputime *cpu,
- const char *name, int value)
-{
- struct perfcounter *cpu_perf, *process_perf;
-
- cpu_perf = get_perf_counter(name, NULL, cpu);
- if (cpu_perf->count < value) {
- process_perf = get_perf_counter(name, proc, NULL);
- process_perf->count += value - cpu_perf->count;
- cpu_perf->count = value;
- }
-}
-
-void extract_perf_counter_scope(const struct bt_ctf_event *event,
- const struct definition *scope,
- struct processtop *proc,
- struct cputime *cpu)
-{
- struct definition const * const *list = NULL;
- const struct definition *field;
- unsigned int count;
- struct perfcounter *perfcounter;
- GHashTableIter iter;
- gpointer key;
- int ret;
-
- if (!scope)
- goto end;
-
- ret = bt_ctf_get_field_list(event, scope, &list, &count);
- if (ret < 0)
- goto end;
-
- if (count == 0)
- goto end;
-
- g_hash_table_iter_init(&iter, global_perf_liszt);
- while (g_hash_table_iter_next (&iter, &key, (gpointer) &perfcounter)) {
- field = bt_ctf_get_field(event, scope, (char *) key);
- if (field) {
- int value = bt_ctf_get_uint64(field);
- if (bt_ctf_field_get_error())
- continue;
- update_perf_value(proc, cpu, (char *) key, value);
- }
- }
-
-end:
- return;
-}
-
-void update_perf_counter(struct processtop *proc, const struct bt_ctf_event *event)
-{
- struct cputime *cpu;
- const struct definition *scope;
-
- cpu = get_cpu(get_cpu_id(event));
-
- scope = bt_ctf_get_top_level_scope(event, BT_STREAM_EVENT_CONTEXT);
- extract_perf_counter_scope(event, scope, proc, cpu);
-
- scope = bt_ctf_get_top_level_scope(event, BT_STREAM_PACKET_CONTEXT);
- extract_perf_counter_scope(event, scope, proc, cpu);
-
- scope = bt_ctf_get_top_level_scope(event, BT_EVENT_CONTEXT);
- extract_perf_counter_scope(event, scope, proc, cpu);
-}
-
-enum bt_cb_ret fix_process_table(struct bt_ctf_event *call_data,
- void *private_data)
-{
- int pid, tid, ppid;
- char *comm;
- struct processtop *parent, *child;
- unsigned long timestamp;
-
- timestamp = bt_ctf_get_timestamp(call_data);
- if (timestamp == -1ULL)
- goto error;
-
- pid = get_context_pid(call_data);
- if (pid == -1ULL) {
- goto error;
- }
- tid = get_context_tid(call_data);
- if (tid == -1ULL) {
- goto error;
- }
- ppid = get_context_ppid(call_data);
- if (ppid == -1ULL) {
- goto error;
- }
- comm = get_context_comm(call_data);
- if (!comm) {
- goto error;
- }
-
- /* find or create the current process */
- child = find_process_tid(<tngtop, tid, comm);
- if (!child)
- child = add_proc(<tngtop, tid, comm, timestamp);
- update_proc(child, pid, tid, ppid, comm);
-
- if (pid != tid) {
- /* find or create the parent */
- parent = find_process_tid(<tngtop, pid, comm);
- if (!parent) {
- parent = add_proc(<tngtop, pid, comm, timestamp);
- parent->pid = pid;
- }
-
- /* attach the parent to the current process */
- child->threadparent = parent;
- add_thread(parent, child);
- }
-
- update_perf_counter(child, call_data);
-
- return BT_CB_OK;
-
-error:
- return BT_CB_ERROR_STOP;
-}
-
-void init_lttngtop()
-{
- copies = g_ptr_array_new();
- global_perf_liszt = g_hash_table_new(g_str_hash, g_str_equal);
-
- sem_init(&goodtodisplay, 0, 0);
- sem_init(&goodtoupdate, 0, 1);
- sem_init(&timer, 0, 1);
- sem_init(&bootstrap, 0, 0);
- sem_init(&pause_sem, 0, 1);
- sem_init(&end_trace_sem, 0, 0);
-
- reset_global_counters();
- lttngtop.nbproc = 0;
- lttngtop.nbthreads = 0;
- lttngtop.nbfiles = 0;
-
- lttngtop.process_table = g_ptr_array_new();
- lttngtop.files_table = g_ptr_array_new();
- lttngtop.cpu_table = g_ptr_array_new();
-}
-
-void usage(FILE *fp)
-{
- fprintf(fp, "LTTngTop %s\n\n", VERSION);
- fprintf(fp, "Usage : lttngtop /path/to/trace\n");
-}
-
-/*
- * Return 0 if caller should continue, < 0 if caller should return
- * error, > 0 if caller should exit without reporting error.
- */
-static int parse_options(int argc, char **argv)
-{
- poptContext pc;
- int opt, ret = 0;
-
- if (argc == 1) {
- usage(stdout);
- return 1; /* exit cleanly */
- }
-
- pc = poptGetContext(NULL, argc, (const char **) argv, long_options, 0);
- poptReadDefaultConfig(pc, 0);
-
- while ((opt = poptGetNextOpt(pc)) != -1) {
- switch (opt) {
- case OPT_HELP:
- usage(stdout);
- ret = 1; /* exit cleanly */
- goto end;
- default:
- ret = -EINVAL;
- goto end;
- }
- }
-
- opt_input_path = poptGetArg(pc);
- if (!opt_input_path) {
- ret = -EINVAL;
- goto end;
- }
-end:
- if (pc) {
- poptFreeContext(pc);
- }
- return ret;
-}
-
-void iter_trace(struct bt_context *bt_ctx)
-{
- struct bt_ctf_iter *iter;
- struct bt_iter_pos begin_pos;
- const struct bt_ctf_event *event;
- int ret = 0;
-
- begin_pos.type = BT_SEEK_BEGIN;
- iter = bt_ctf_iter_create(bt_ctx, &begin_pos, NULL);
-
- /* at each event check if we need to refresh */
- bt_ctf_iter_add_callback(iter, 0, NULL, 0,
- check_timestamp,
- NULL, NULL, NULL);
- /* at each event, verify the status of the process table */
- bt_ctf_iter_add_callback(iter, 0, NULL, 0,
- fix_process_table,
- NULL, NULL, NULL);
- /* to handle the scheduling events */
- bt_ctf_iter_add_callback(iter,
- g_quark_from_static_string("sched_switch"),
- NULL, 0, handle_sched_switch, NULL, NULL, NULL);
- /* to clean up the process table */
- bt_ctf_iter_add_callback(iter,
- g_quark_from_static_string("sched_process_free"),
- NULL, 0, handle_sched_process_free, NULL, NULL, NULL);
- /* to get all the process from the statedumps */
- bt_ctf_iter_add_callback(iter,
- g_quark_from_static_string(
- "lttng_statedump_process_state"),
- NULL, 0, handle_statedump_process_state,
- NULL, NULL, NULL);
-
- /* for IO top */
- bt_ctf_iter_add_callback(iter,
- g_quark_from_static_string("exit_syscall"),
- NULL, 0, handle_exit_syscall, NULL, NULL, NULL);
- bt_ctf_iter_add_callback(iter,
- g_quark_from_static_string("sys_write"),
- NULL, 0, handle_sys_write, NULL, NULL, NULL);
- bt_ctf_iter_add_callback(iter,
- g_quark_from_static_string("sys_read"),
- NULL, 0, handle_sys_read, NULL, NULL, NULL);
- bt_ctf_iter_add_callback(iter,
- g_quark_from_static_string("sys_open"),
- NULL, 0, handle_sys_open, NULL, NULL, NULL);
- bt_ctf_iter_add_callback(iter,
- g_quark_from_static_string("sys_close"),
- NULL, 0, handle_sys_close, NULL, NULL, NULL);
- bt_ctf_iter_add_callback(iter,
- g_quark_from_static_string(
- "lttng_statedump_file_descriptor"),
- NULL, 0, handle_statedump_file_descriptor,
- NULL, NULL, NULL);
-
- while ((event = bt_ctf_iter_read_event(iter)) != NULL) {
- ret = bt_iter_next(bt_ctf_get_iter(iter));
- if (ret < 0)
- goto end_iter;
- }
-
- /* block until quit, we reached the end of the trace */
- sem_wait(&end_trace_sem);
-
-end_iter:
- bt_ctf_iter_destroy(iter);
-}
-
-/*
- * bt_context_add_traces_recursive: Open a trace recursively
- * (copied from BSD code in converter/babeltrace.c)
- *
- * Find each trace present in the subdirectory starting from the given
- * path, and add them to the context. The packet_seek parameter can be
- * NULL: this specify to use the default format packet_seek.
- *
- * Return: 0 on success, nonzero on failure.
- * Unable to open toplevel: failure.
- * Unable to open some subdirectory or file: warn and continue;
- */
-int bt_context_add_traces_recursive(struct bt_context *ctx, const char *path,
- const char *format_str,
- void (*packet_seek)(struct stream_pos *pos,
- size_t offset, int whence))
-{
- FTS *tree;
- FTSENT *node;
- GArray *trace_ids;
- char lpath[PATH_MAX];
- char * const paths[2] = { lpath, NULL };
- int ret = -1;
-
- /*
- * Need to copy path, because fts_open can change it.
- * It is the pointer array, not the strings, that are constant.
- */
- strncpy(lpath, path, PATH_MAX);
- lpath[PATH_MAX - 1] = '\0';
-
- tree = fts_open(paths, FTS_NOCHDIR | FTS_LOGICAL, 0);
- if (tree == NULL) {
- fprintf(stderr, "[error] [Context] Cannot traverse \"%s\" for reading.\n",
- path);
- return -EINVAL;
- }
-
- trace_ids = g_array_new(FALSE, TRUE, sizeof(int));
-
- while ((node = fts_read(tree))) {
- int dirfd, metafd;
-
- if (!(node->fts_info & FTS_D))
- continue;
-
- dirfd = open(node->fts_accpath, 0);
- if (dirfd < 0) {
- fprintf(stderr, "[error] [Context] Unable to open trace "
- "directory file descriptor.\n");
- ret = dirfd;
- goto error;
- }
- metafd = openat(dirfd, "metadata", O_RDONLY);
- if (metafd < 0) {
- close(dirfd);
- ret = -1;
- continue;
- } else {
- int trace_id;
-
- ret = close(metafd);
- if (ret < 0) {
- perror("close");
- goto error;
- }
- ret = close(dirfd);
- if (ret < 0) {
- perror("close");
- goto error;
- }
-
- trace_id = bt_context_add_trace(ctx,
- node->fts_accpath, format_str,
- packet_seek, NULL, NULL);
- if (trace_id < 0) {
- fprintf(stderr, "[warning] [Context] opening trace \"%s\" from %s "
- "for reading.\n", node->fts_accpath, path);
- /* Allow to skip erroneous traces. */
- continue;
- }
- g_array_append_val(trace_ids, trace_id);
- }
- }
-
- g_array_free(trace_ids, TRUE);
- return ret;
-
-error:
- return ret;
-}
-
-static int check_field_requirements(const struct bt_ctf_field_decl *const * field_list,
- int field_cnt, int *tid_check, int *pid_check,
- int *procname_check, int *ppid_check)
-{
- int j;
- struct perfcounter *global;
- const char *name;
-
- for (j = 0; j < field_cnt; j++) {
- name = bt_ctf_get_decl_field_name(field_list[j]);
- if (*tid_check == 0) {
- if (strncmp(name, "tid", 3) == 0)
- (*tid_check)++;
- }
- if (*pid_check == 0) {
- if (strncmp(name, "tid", 3) == 0)
- (*pid_check)++;
- }
- if (*ppid_check == 0) {
- if (strncmp(name, "ppid", 4) == 0)
- (*ppid_check)++;
- }
- if (*procname_check == 0) {
- if (strncmp(name, "procname", 8) == 0)
- (*procname_check)++;
- }
- if (strncmp(name, "perf_", 5) == 0) {
- global = g_hash_table_lookup(global_perf_liszt, (gpointer) name);
- if (!global) {
- global = g_new0(struct perfcounter, 1);
- /* by default, sort on the first perf context */
- if (g_hash_table_size(global_perf_liszt) == 0)
- global->sort = 1;
- global->visible = 1;
- g_hash_table_insert(global_perf_liszt, (gpointer) strdup(name), global);
- }
- }
- }
-
- if (*tid_check == 1 && *pid_check == 1 && *ppid_check == 1 &&
- *procname_check == 1)
- return 0;
-
- return -1;
-}
-
-/*
- * check_requirements: check if the required context informations are available
- *
- * If each mandatory context information is available for at least in one
- * event, return 0 otherwise return -1.
- */
-int check_requirements(struct bt_context *ctx)
-{
- unsigned int i, evt_cnt, field_cnt;
- struct bt_ctf_event_decl *const * evt_list;
- const struct bt_ctf_field_decl *const * field_list;
- int tid_check = 0;
- int pid_check = 0;
- int procname_check = 0;
- int ppid_check = 0;
- int ret = 0;
-
- bt_ctf_get_event_decl_list(0, ctx, &evt_list, &evt_cnt);
- for (i = 0; i < evt_cnt; i++) {
- bt_ctf_get_decl_fields(evt_list[i], BT_STREAM_EVENT_CONTEXT,
- &field_list, &field_cnt);
- ret = check_field_requirements(field_list, field_cnt,
- &tid_check, &pid_check, &procname_check,
- &ppid_check);
-
- bt_ctf_get_decl_fields(evt_list[i], BT_EVENT_CONTEXT,
- &field_list, &field_cnt);
- ret = check_field_requirements(field_list, field_cnt,
- &tid_check, &pid_check, &procname_check,
- &ppid_check);
-
- bt_ctf_get_decl_fields(evt_list[i], BT_STREAM_PACKET_CONTEXT,
- &field_list, &field_cnt);
- ret = check_field_requirements(field_list, field_cnt,
- &tid_check, &pid_check, &procname_check,
- &ppid_check);
- }
-
- if (tid_check == 0) {
- ret = -1;
- fprintf(stderr, "[error] missing tid context information\n");
- }
- if (pid_check == 0) {
- ret = -1;
- fprintf(stderr, "[error] missing pid context information\n");
- }
- if (ppid_check == 0) {
- ret = -1;
- fprintf(stderr, "[error] missing ppid context information\n");
- }
- if (procname_check == 0) {
- ret = -1;
- fprintf(stderr, "[error] missing procname context information\n");
- }
-
- return ret;
-}
-
-int main(int argc, char **argv)
-{
- int ret;
- struct bt_context *bt_ctx = NULL;
-
- ret = parse_options(argc, argv);
- if (ret < 0) {
- fprintf(stdout, "Error parsing options.\n\n");
- usage(stdout);
- exit(EXIT_FAILURE);
- } else if (ret > 0) {
- exit(EXIT_SUCCESS);
- }
-
- init_lttngtop();
-
- bt_ctx = bt_context_create();
- ret = bt_context_add_traces_recursive(bt_ctx, opt_input_path, "ctf", NULL);
- if (ret < 0) {
- fprintf(stderr, "[error] Opening the trace\n");
- goto end;
- }
-
- ret = check_requirements(bt_ctx);
- if (ret < 0) {
- fprintf(stderr, "[error] some mandatory contexts were missing, exiting.\n");
- goto end;
- }
-
- pthread_create(&display_thread, NULL, ncurses_display, (void *) NULL);
- pthread_create(&timer_thread, NULL, refresh_thread, (void *) NULL);
-
- iter_trace(bt_ctx);
-
- quit = 1;
- pthread_join(display_thread, NULL);
- pthread_join(timer_thread, NULL);
-
-end:
- bt_context_put(bt_ctx);
- return 0;
-}
--- /dev/null
+/*
+ * 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 <config.h>
+#include <stdio.h>
+#include <stdint.h>
+extern "C" {
+#include <babeltrace/babeltrace.h>
+#include <babeltrace/ctf/events.h>
+#include <babeltrace/ctf/callbacks.h>
+#include <babeltrace/ctf/iterator.h>
+}
+#include <fcntl.h>
+#include <pthread.h>
+#include <popt.h>
+#include <stdlib.h>
+#include <ftw.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <fts.h>
+#include <assert.h>
+
+#include "lttngtoptypes.h"
+#include "cputop.h"
+#include "iostreamtop.h"
+#include "common.h"
+#include "cursesdisplay.h"
+
+#define DEFAULT_FILE_ARRAY_SIZE 1
+
+const char *opt_input_path;
+
+pthread_t display_thread;
+pthread_t timer_thread;
+
+int quit = 0;
+std::string history_file;
+
+enum {
+ OPT_NONE = 0,
+ OPT_HELP,
+ OPT_LIST,
+ OPT_VERBOSE,
+ OPT_DEBUG,
+ OPT_NAMES,
+};
+
+static struct poptOption long_options[] = {
+ /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
+ { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL },
+ { NULL, 0, 0, NULL, 0, NULL, NULL },
+};
+
+void *refresh_thread(void *p)
+{
+ while (1) {
+ if (quit)
+ return NULL;
+ sem_wait(&pause_sem);
+ sem_post(&pause_sem);
+ sem_post(&timer);
+ sleep(refresh_display/NSEC_PER_SEC);
+ }
+}
+
+void *ncurses_display(void *p)
+{
+ sem_wait(&bootstrap);
+ /*
+ * Prevent the 1 second delay when we hit ESC
+ */
+ ESCDELAY = 0;
+ init_ncurses();
+
+ while (1) {
+ sem_wait(&timer);
+ sem_wait(&goodtodisplay);
+ sem_wait(&pause_sem);
+
+ display();
+
+ sem_post(&goodtoupdate);
+ sem_post(&pause_sem);
+
+ if (quit) {
+ reset_ncurses();
+ pthread_exit(0);
+ }
+ }
+}
+
+/*
+ * We soft update each attribute during the execution for perfromance reasons.
+ * Here we write the actual intervals to disk, which are needed to query history.
+ */
+void create_intervals(unsigned long timestamp)
+{
+ StateValue::SharedPtr value;
+
+ for (std::set<Quark>::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<NullStateValue>(value)) {
+ state_system->modifyAttribute(timestamp, *i, 0);
+ } else {
+ state_system->modifyAttribute(timestamp, *i,
+ StateValue::getNullValue());
+ }
+ state_system->updateCurrentState(*i, value);
+ }
+ modified_quarks.clear();
+}
+
+/*
+ * hook on each event to check the timestamp and refresh the display if
+ * necessary
+ */
+enum bt_cb_ret check_timestamp(struct bt_ctf_event *call_data,
+ void *private_data)
+{
+ unsigned long timestamp;
+
+ timestamp = bt_ctf_get_timestamp(call_data);
+ if (timestamp == -1ULL)
+ goto error;
+
+ if (last_display_update == 0)
+ last_display_update = timestamp;
+
+ if (first_display_update == 0)
+ first_display_update = timestamp;
+
+ if (timestamp - last_display_update >= refresh_display) {
+ sem_wait(&goodtoupdate);
+ create_intervals(timestamp);
+ update_state_on_refresh(last_display_update, timestamp);
+ sem_post(&goodtodisplay);
+ sem_post(&bootstrap);
+ last_display_update = timestamp;
+ }
+ return BT_CB_OK;
+
+error:
+ fprintf(stderr, "check_timestamp callback error\n");
+ return BT_CB_ERROR_STOP;
+}
+
+/*
+ * get_perf_counter : get or create and return a perf_counter struct for
+ * either a process or a cpu (specified by root)
+ */
+Quark get_perf_counter(unsigned long timestamp, Quark root, std::string name)
+{
+ Quark perf_counter;
+
+ if (state_system->attributeExists(root, "perf/" + name)) {
+ return state_system->getQuark(root, "perf/" + name);
+ } else {
+ perf_counter = state_system->getQuark(root, "perf/" + name);
+ modify_attribute(timestamp, &perf_counter, "count", 0);
+ modify_attribute(timestamp, &perf_counter, "visible", 1);
+ add_in_sequence(timestamp, perf_counter,
+ state_system->getQuark(root, "perf"));
+ return perf_counter;
+ }
+}
+
+void update_perf_value(unsigned long timestamp, Quark proc, Quark cpu,
+ std::string name, int value)
+{
+ Quark cpu_perf, process_perf;
+ int count;
+
+ cpu_perf = get_perf_counter(timestamp, cpu, name);
+ get_current_attribute_value_int(&cpu_perf, "count", count);
+ if (count < value) {
+ process_perf = get_perf_counter(timestamp, proc, name);
+ increase_attribute(
+ timestamp, &process_perf, "count", value - count);
+ modify_attribute(timestamp, &cpu_perf, "count", value);
+ }
+}
+
+void extract_perf_counter_scope(unsigned long timestamp,
+ const struct bt_ctf_event *event,
+ const struct definition *scope,
+ Quark proc, Quark cpu)
+{
+ struct definition const * const *list = NULL;
+ const struct definition *field;
+ unsigned int count;
+ int ret;
+ std::string key;
+ Quark perf_quark;
+
+ if (!scope)
+ goto end;
+
+ ret = bt_ctf_get_field_list(event, scope, &list, &count);
+ if (ret < 0)
+ goto end;
+
+ if (count == 0)
+ goto end;
+
+ if (get_current_attribute_value_quark(
+ NULL, "perf", perf_quark)) {
+ do {
+ get_current_attribute_value_string(
+ &perf_quark, "key", key);
+ field = bt_ctf_get_field(event, scope, key.c_str());
+ if (field) {
+ int value = bt_ctf_get_uint64(field);
+ if (bt_ctf_field_get_error())
+ continue;
+ update_perf_value(
+ timestamp, proc, cpu, key, value);
+ }
+ } while (get_current_attribute_value_quark(
+ &perf_quark, "next", perf_quark));
+ }
+
+
+end:
+ return;
+}
+
+void update_perf_counter(unsigned long timestamp, Quark proc,
+ const struct bt_ctf_event *event)
+{
+ Quark cpu;
+ const struct definition *scope;
+
+ cpu = get_cpu(get_cpu_id(event), timestamp);
+
+ scope = bt_ctf_get_top_level_scope(event, BT_STREAM_EVENT_CONTEXT);
+ extract_perf_counter_scope(timestamp, event, scope, proc, cpu);
+
+ scope = bt_ctf_get_top_level_scope(event, BT_STREAM_PACKET_CONTEXT);
+ extract_perf_counter_scope(timestamp, event, scope, proc, cpu);
+
+ scope = bt_ctf_get_top_level_scope(event, BT_EVENT_CONTEXT);
+ extract_perf_counter_scope(timestamp, event, scope, proc, cpu);
+}
+
+enum bt_cb_ret fix_process_table(struct bt_ctf_event *call_data,
+ void *private_data)
+{ int pid, tid, ppid;
+ char *comm;
+ Quark parent, child;
+ unsigned long timestamp;
+
+ timestamp = bt_ctf_get_timestamp(call_data);
+ if (timestamp == -1ULL)
+ goto error;
+
+ pid = get_context_pid(call_data);
+ if ((unsigned long)pid == -1ULL) {
+ goto error;
+ }
+ tid = get_context_tid(call_data);
+ if ((unsigned long)tid == -1ULL) {
+ goto error;
+ }
+ ppid = get_context_ppid(call_data);
+ if ((unsigned long)ppid == -1ULL) {
+ goto error;
+ }
+ comm = get_context_comm(call_data);
+ if (!comm) {
+ goto error;
+ }
+
+ /* find or create the current process */
+ if (!find_process_tid(tid, child))
+ child = add_proc(tid, comm, timestamp);
+ update_proc(timestamp, child, pid, tid, ppid, comm);
+
+ if (pid != tid) {
+ /* find or create the parent */
+ if (!find_process_tid(pid, parent)) {
+ parent = add_proc(pid, comm, timestamp);
+ modify_attribute(timestamp, &parent, "pid", pid);
+ }
+
+ /* attach the parent to the current process */
+ modify_attribute(timestamp, &parent, "threadparent", parent);
+ add_thread(timestamp, parent, child);
+ }
+
+ update_perf_counter(timestamp, child, call_data);
+
+ return BT_CB_OK;
+
+error:
+ return BT_CB_ERROR_STOP;
+}
+
+void init_lttngtop()
+{
+ sem_init(&goodtodisplay, 0, 0);
+ sem_init(&goodtoupdate, 0, 1);
+ sem_init(&timer, 0, 1);
+ sem_init(&bootstrap, 0, 0);
+ sem_init(&pause_sem, 0, 1);
+ sem_init(&end_trace_sem, 0, 0);
+
+ // TODO: real file name
+ std::stringstream ss;
+ ss << "history" << time(NULL) << ".hst";
+ history_file = ss.str();
+ IntervalHistoryProvider *ihp = g_new(IntervalHistoryProvider, 1);
+ new (ihp) IntervalHistoryProvider(history_file);
+ state_system = g_new(StateSystem, 1);
+ new (state_system) StateSystem(ihp);
+
+ /* Create global attributes */
+ state_system->updateCurrentState(
+ state_system->getQuark("nbproc"), 0);
+ state_system->updateCurrentState(
+ state_system->getQuark("nbnewproc"), 0);
+ state_system->updateCurrentState(
+ state_system->getQuark("nbdeadproc"), 0);
+ state_system->updateCurrentState(
+ state_system->getQuark("nbthreads"), 0);
+ state_system->updateCurrentState(
+ state_system->getQuark("nbnewthreads"), 0);
+ state_system->updateCurrentState(
+ state_system->getQuark("nbdeadthreads"), 0);
+ state_system->updateCurrentState(
+ state_system->getQuark("nbfiles"), 0);
+ state_system->updateCurrentState(
+ state_system->getQuark("nbnewfiles"), 0);
+ state_system->updateCurrentState(
+ state_system->getQuark("nbdeadfiles"), 0);
+}
+
+void usage(FILE *fp)
+{
+ fprintf(fp, "LTTngTop %s\n\n", VERSION);
+ fprintf(fp, "Usage : lttngtop /path/to/trace\n");
+}
+
+/*
+ * Return 0 if caller should continue, < 0 if caller should return
+ * error, > 0 if caller should exit without reporting error.
+ */
+static int parse_options(int argc, char **argv)
+{
+ poptContext pc;
+ int opt, ret = 0;
+
+ if (argc == 1) {
+ usage(stdout);
+ return 1; /* exit cleanly */
+ }
+
+ pc = poptGetContext(NULL, argc, (const char **) argv, long_options, 0);
+ poptReadDefaultConfig(pc, 0);
+
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case OPT_HELP:
+ usage(stdout);
+ ret = 1; /* exit cleanly */
+ goto end;
+ default:
+ ret = -EINVAL;
+ goto end;
+ }
+ }
+
+ opt_input_path = poptGetArg(pc);
+ if (!opt_input_path) {
+ ret = -EINVAL;
+ goto end;
+ }
+end:
+ if (pc) {
+ poptFreeContext(pc);
+ }
+ return ret;
+}
+
+void iter_trace(struct bt_context *bt_ctx)
+{
+ struct bt_ctf_iter *iter;
+ struct bt_iter_pos begin_pos;
+ const struct bt_ctf_event *event;
+ int ret = 0;
+
+ begin_pos.type = bt_iter_pos::BT_SEEK_BEGIN;
+ iter = bt_ctf_iter_create(bt_ctx, &begin_pos, NULL);
+
+ /* at each event check if we need to refresh */
+ bt_ctf_iter_add_callback(iter, 0, NULL, 0,
+ check_timestamp,
+ NULL, NULL, NULL);
+ /* at each event, verify the status of the process table */
+ bt_ctf_iter_add_callback(iter, 0, NULL, 0,
+ fix_process_table,
+ NULL, NULL, NULL);
+ /* to handle the scheduling events */
+ bt_ctf_iter_add_callback(iter,
+ g_quark_from_static_string("sched_switch"),
+ NULL, 0, handle_sched_switch, NULL, NULL, NULL);
+ /* to clean up the process table */
+ bt_ctf_iter_add_callback(iter,
+ g_quark_from_static_string("sched_process_free"),
+ NULL, 0, handle_sched_process_free, NULL, NULL, NULL);
+ /* to get all the process from the statedumps */
+ bt_ctf_iter_add_callback(iter,
+ g_quark_from_static_string(
+ "lttng_statedump_process_state"),
+ NULL, 0, handle_statedump_process_state,
+ NULL, NULL, NULL);
+
+ /* for IO top */
+ bt_ctf_iter_add_callback(iter,
+ g_quark_from_static_string("exit_syscall"),
+ NULL, 0, handle_exit_syscall, NULL, NULL, NULL);
+ bt_ctf_iter_add_callback(iter,
+ g_quark_from_static_string("sys_write"),
+ NULL, 0, handle_sys_write, NULL, NULL, NULL);
+ bt_ctf_iter_add_callback(iter,
+ g_quark_from_static_string("sys_read"),
+ NULL, 0, handle_sys_read, NULL, NULL, NULL);
+ bt_ctf_iter_add_callback(iter,
+ g_quark_from_static_string("sys_open"),
+ NULL, 0, handle_sys_open, NULL, NULL, NULL);
+ bt_ctf_iter_add_callback(iter,
+ g_quark_from_static_string("sys_close"),
+ NULL, 0, handle_sys_close, NULL, NULL, NULL);
+ bt_ctf_iter_add_callback(iter,
+ g_quark_from_static_string(
+ "lttng_statedump_file_descriptor"),
+ NULL, 0, handle_statedump_file_descriptor,
+ NULL, NULL, NULL);
+
+ while ((event = bt_ctf_iter_read_event(iter)) != NULL) {
+ ret = bt_iter_next(bt_ctf_get_iter(iter));
+ if (ret < 0)
+ goto end_iter;
+ }
+
+ /* block until quit, we reached the end of the trace */
+ sem_wait(&end_trace_sem);
+
+end_iter:
+ bt_ctf_iter_destroy(iter);
+}
+
+/*
+ * bt_context_add_traces_recursive: Open a trace recursively
+ * (copied from BSD code in converter/babeltrace.c)
+ *
+ * Find each trace present in the subdirectory starting from the given
+ * path, and add them to the context. The packet_seek parameter can be
+ * NULL: this specify to use the default format packet_seek.
+ *
+ * Return: 0 on success, nonzero on failure.
+ * Unable to open toplevel: failure.
+ * Unable to open some subdirectory or file: warn and continue;
+ */
+int bt_context_add_traces_recursive(struct bt_context *ctx, const char *path,
+ const char *format_str,
+ void (*packet_seek)(struct stream_pos *pos,
+ size_t offset, int whence))
+{
+ FTS *tree;
+ FTSENT *node;
+ GArray *trace_ids;
+ char lpath[PATH_MAX];
+ char * const paths[2] = { lpath, NULL };
+ int ret = -1;
+
+ /*
+ * Need to copy path, because fts_open can change it.
+ * It is the pointer array, not the strings, that are constant.
+ */
+ strncpy(lpath, path, PATH_MAX);
+ lpath[PATH_MAX - 1] = '\0';
+
+ tree = fts_open(paths, FTS_NOCHDIR | FTS_LOGICAL, 0);
+ if (tree == NULL) {
+ fprintf(stderr, "[error] [Context] Cannot traverse \"%s\" for reading.\n",
+ path);
+ return -EINVAL;
+ }
+
+ trace_ids = g_array_new(FALSE, TRUE, sizeof(int));
+
+ while ((node = fts_read(tree))) {
+ int dirfd, metafd;
+
+ if (!(node->fts_info & FTS_D))
+ continue;
+
+ dirfd = open(node->fts_accpath, 0);
+ if (dirfd < 0) {
+ fprintf(stderr, "[error] [Context] Unable to open trace "
+ "directory file descriptor.\n");
+ ret = dirfd;
+ goto error;
+ }
+ metafd = openat(dirfd, "metadata", O_RDONLY);
+ if (metafd < 0) {
+ close(dirfd);
+ ret = -1;
+ continue;
+ } else {
+ int trace_id;
+
+ ret = close(metafd);
+ if (ret < 0) {
+ perror("close");
+ goto error;
+ }
+ ret = close(dirfd);
+ if (ret < 0) {
+ perror("close");
+ goto error;
+ }
+
+ trace_id = bt_context_add_trace(ctx,
+ node->fts_accpath, format_str,
+ packet_seek, NULL, NULL);
+ if (trace_id < 0) {
+ fprintf(stderr, "[warning] [Context] opening trace \"%s\" from %s "
+ "for reading.\n", node->fts_accpath, path);
+ /* Allow to skip erroneous traces. */
+ continue;
+ }
+ g_array_append_val(trace_ids, trace_id);
+ }
+ }
+
+ g_array_free(trace_ids, TRUE);
+ return ret;
+
+error:
+ return ret;
+}
+
+static int check_field_requirements(const struct bt_ctf_field_decl *const * field_list,
+ int field_cnt, int *tid_check, int *pid_check,
+ int *procname_check, int *ppid_check)
+{
+ int j;
+ const char *name;
+ Quark perf;
+ Quark perf_root = state_system->getQuark("perf");
+ static bool first_perf_counter = true;
+
+ for (j = 0; j < field_cnt; j++) {
+ name = bt_ctf_get_decl_field_name(field_list[j]);
+ if (*tid_check == 0) {
+ if (strncmp(name, "tid", 3) == 0)
+ (*tid_check)++;
+ }
+ if (*pid_check == 0) {
+ if (strncmp(name, "tid", 3) == 0)
+ (*pid_check)++;
+ }
+ if (*ppid_check == 0) {
+ if (strncmp(name, "ppid", 4) == 0)
+ (*ppid_check)++;
+ }
+ if (*procname_check == 0) {
+ if (strncmp(name, "procname", 8) == 0)
+ (*procname_check)++;
+ }
+ if (strncmp(name, "perf_", 5) == 0) {
+ if (!state_system->attributeExists(
+ perf_root, name+5)) {
+ perf = state_system->getQuark(perf_root, name+5);
+ /* by default, sort on the first perf context */
+ if (first_perf_counter) {
+ modify_attribute(0, &perf, "sort", 1);
+ first_perf_counter = false;
+ }
+ modify_attribute(0, &perf, "visible", 1);
+ add_in_sequence(0, perf, perf_root);
+ }
+ }
+ }
+
+ if (*tid_check == 1 && *pid_check == 1 && *ppid_check == 1 &&
+ *procname_check == 1)
+ return 0;
+
+ return -1;
+}
+
+/*
+ * check_requirements: check if the required context informations are available
+ *
+ * If each mandatory context information is available for at least in one
+ * event, return 0 otherwise return -1.
+ */
+int check_requirements(struct bt_context *ctx)
+{
+ unsigned int i, evt_cnt, field_cnt;
+ struct bt_ctf_event_decl *const * evt_list;
+ const struct bt_ctf_field_decl *const * field_list;
+ int tid_check = 0;
+ int pid_check = 0;
+ int procname_check = 0;
+ int ppid_check = 0;
+ int ret = 0;
+
+ bt_ctf_get_event_decl_list(0, ctx, &evt_list, &evt_cnt);
+ for (i = 0; i < evt_cnt; i++) {
+ bt_ctf_get_decl_fields(evt_list[i], BT_STREAM_EVENT_CONTEXT,
+ &field_list, &field_cnt);
+ ret = check_field_requirements(field_list, field_cnt,
+ &tid_check, &pid_check, &procname_check,
+ &ppid_check);
+
+ bt_ctf_get_decl_fields(evt_list[i], BT_EVENT_CONTEXT,
+ &field_list, &field_cnt);
+ ret = check_field_requirements(field_list, field_cnt,
+ &tid_check, &pid_check, &procname_check,
+ &ppid_check);
+
+ bt_ctf_get_decl_fields(evt_list[i], BT_STREAM_PACKET_CONTEXT,
+ &field_list, &field_cnt);
+ ret = check_field_requirements(field_list, field_cnt,
+ &tid_check, &pid_check, &procname_check,
+ &ppid_check);
+ }
+
+ if (tid_check == 0) {
+ ret = -1;
+ fprintf(stderr, "[error] missing tid context information\n");
+ }
+ if (pid_check == 0) {
+ ret = -1;
+ fprintf(stderr, "[error] missing pid context information\n");
+ }
+ if (ppid_check == 0) {
+ ret = -1;
+ fprintf(stderr, "[error] missing ppid context information\n");
+ }
+ if (procname_check == 0) {
+ ret = -1;
+ fprintf(stderr, "[error] missing procname context information\n");
+ }
+
+ return ret;
+}
+
+int main(int argc, char **argv)
+{
+ int ret;
+ struct bt_context *bt_ctx = NULL;
+
+ ret = parse_options(argc, argv);
+ if (ret < 0) {
+ fprintf(stdout, "Error parsing options.\n\n");
+ usage(stdout);
+ exit(EXIT_FAILURE);
+ } else if (ret > 0) {
+ exit(EXIT_SUCCESS);
+ }
+
+ init_lttngtop();
+
+ bt_ctx = bt_context_create();
+ ret = bt_context_add_traces_recursive(bt_ctx, opt_input_path, "ctf", NULL);
+ if (ret < 0) {
+ fprintf(stderr, "[error] Opening the trace\n");
+ goto end;
+ }
+
+ ret = check_requirements(bt_ctx);
+ if (ret < 0) {
+ fprintf(stderr, "[error] some mandatory contexts were missing, exiting.\n");
+ goto end;
+ }
+
+ pthread_create(&display_thread, NULL, ncurses_display, (void *) NULL);
+ pthread_create(&timer_thread, NULL, refresh_thread, (void *) NULL);
+
+ iter_trace(bt_ctx);
+
+ quit = 1;
+ pthread_join(display_thread, NULL);
+ pthread_join(timer_thread, NULL);
+
+end:
+ bt_context_put(bt_ctx);
+ return 0;
+}
#include <glib.h>
-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;