From: Mathieu Bain Date: Thu, 23 Feb 2012 00:54:10 +0000 (-0500) Subject: Bandwidth per process per file in detailled view X-Git-Tag: v0.2~64 X-Git-Url: https://git.lttng.org/?p=lttngtop.git;a=commitdiff_plain;h=b093de8aa1ac7555d20b3aa4227d4675b39ef008 Bandwidth per process per file in detailled view display information for each files Display all the currently opened files clean the data structure no more struct iostream everything is now in struct processtop [ Updated by Julien Desfossez to fit with the new babeltrace API ] Signed-off-by: Mathieu Bain Signed-off-by: Julien Desfossez --- diff --git a/src/common.c b/src/common.c index 0d39f30..f0b7af0 100644 --- a/src/common.c +++ b/src/common.c @@ -46,13 +46,15 @@ struct processtop* add_proc(struct lttngtop *ctx, int tid, char *comm, newproc->tid = tid; newproc->birth = timestamp; newproc->process_files_table = g_ptr_array_new(); + newproc->files_history = g_new0(struct file_history, 1); + newproc->files_history->next = NULL; + newproc->totalfileread = 0; + newproc->totalfilewrite = 0; + newproc->fileread = 0; + newproc->filewrite = 0; + newproc->syscall_info = NULL; newproc->threads = g_ptr_array_new(); newproc->perf = g_hash_table_new(g_str_hash, g_str_equal); - newproc->iostream = g_new0(struct iostream, 1); - newproc->iostream->ret_read = 0; - newproc->iostream->ret_write = 0; - newproc->iostream->ret_total = 0; - newproc->iostream->syscall_info = NULL; g_ptr_array_add(ctx->process_table, newproc); } newproc->comm = strdup(comm); @@ -190,15 +192,24 @@ void rotate_perfcounter() { void cleanup_processtop() { - gint i; + 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->iostream->ret_read = 0; - tmp->iostream->ret_write = 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; + } + } } } @@ -233,27 +244,29 @@ struct lttngtop* get_copy_lttngtop(unsigned long start, unsigned long end) new->perf = g_hash_table_new(g_str_hash, g_str_equal); g_hash_table_foreach(tmp->perf, copy_perf_counter, new->perf); - new->iostream = g_new0(struct iostream, 1); - memcpy(new->iostream, tmp->iostream, sizeof(struct iostream)); /* compute the stream speed */ if (end - start != 0) { time = (end - start) / NSEC_PER_SEC; - new->iostream->ret_read = new->iostream->ret_read / time; - new->iostream->ret_write = new->iostream->ret_write / time; + 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 = g_new0(struct files, 1); - - 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); + 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 diff --git a/src/cursesdisplay.c b/src/cursesdisplay.c index 7a9f99d..eb0caa0 100644 --- a/src/cursesdisplay.c +++ b/src/cursesdisplay.c @@ -27,6 +27,7 @@ #include "cursesdisplay.h" #include "lttngtoptypes.h" +#include "iostreamtop.h" #include "common.h" #define DEFAULT_DELAY 15 @@ -210,7 +211,8 @@ void print_log(char *str) wmove(status, ++current_line, 1); current_char = 1; } else { - mvwprintw(status, current_line, current_char++, "%c", log_lines[i]); + mvwprintw(status, current_line, current_char++, "%c", + log_lines[i]); } } wrefresh(status); @@ -420,6 +422,8 @@ void update_process_details() unsigned long elapsed; double maxcputime; struct processtop *tmp = find_process_tid(data, selected_tid, selected_comm); + struct files *file_tmp; + int i, j = 0; set_window_title(center, "Process details"); @@ -442,6 +446,23 @@ void update_process_details() wprintw(center, "%d", tmp->ppid); print_key_title("CPU", 5); wprintw(center, "%1.2f %%", tmp->totalcpunsec/maxcputime); + + print_key_title("READ B/s", 6); + wprintw(center, "%d", tmp->fileread); + + print_key_title("WRITE B/s", 7); + wprintw(center, "%d", tmp->filewrite); + + for (i = 0; i < tmp->process_files_table->len; i++) { + file_tmp = get_file(tmp, i); + if (file_tmp != NULL) { + print_key_title("file", 8+j); + wprintw(center, "%s fd = %d", file_tmp->name, i); + wprintw(center, " read = %d", file_tmp->read); + wprintw(center, " write = %d", file_tmp->write); + j++; + } + } } void update_perf() @@ -506,7 +527,8 @@ void update_perf() value = perfn2->count; else value = 0; - mvwprintw(center, current_line + header_offset, perf_row, "%d", value); + mvwprintw(center, current_line + header_offset, + perf_row, "%d", value); perf_row += 20; } } @@ -562,8 +584,9 @@ gint sort_by_ret_desc(gconstpointer p1, gconstpointer p2) { struct processtop *n1 = *(struct processtop **)p1; struct processtop *n2 = *(struct processtop **)p2; - unsigned long totaln1 = n1->iostream->ret_total; - unsigned long totaln2 = n2->iostream->ret_total; + + unsigned long totaln1 = n1->totalfileread + n1->totalfilewrite; + unsigned long totaln2 = n2->totalfileread + n2->totalfilewrite; if (totaln1 < totaln2) return 1; @@ -579,6 +602,7 @@ void update_iostream() struct processtop *tmp; int nblinedisplayed = 0; int current_line = 0; + int total = 0; set_window_title(center, "IO Top"); wattron(center, A_BOLD); @@ -606,24 +630,28 @@ void update_iostream() wattron(center, COLOR_PAIR(5)); mvwhline(center, current_line + header_offset, 1, ' ', COLS-3); } + /* READ (bytes/sec) */ mvwprintw(center, current_line + header_offset, 1, "%lu", - tmp->iostream->ret_read); + tmp->fileread); /* WRITE (bytes/sec) */ mvwprintw(center, current_line + header_offset, 20, "%lu", - tmp->iostream->ret_write); + tmp->filewrite); /* TOTAL STREAM */ - if(tmp->iostream->ret_total >= 1000000) + total = tmp->totalfileread + tmp->totalfilewrite; + + if (total >= 1000000) mvwprintw(center, current_line + header_offset, 40, "%lu MB", - tmp->iostream->ret_total/1000000); - else if(tmp->iostream->ret_total >=1000) + total/1000000); + else if (total >= 1000) mvwprintw(center, current_line + header_offset, 40, "%lu KB", - tmp->iostream->ret_total/1000); + total/1000); else mvwprintw(center, current_line + header_offset, 40, "%lu B", - tmp->iostream->ret_total); + total); + /* TGID */ mvwprintw(center, current_line + header_offset, 60, "%d", tmp->pid); /* PID */ @@ -701,7 +729,8 @@ void update_perf_panel(int line_selected, int toggle_view, int toggle_sort) box(perf_panel_window, 0 , 0); set_window_title(perf_panel_window, "Perf Preferences "); wattron(perf_panel_window, A_BOLD); - mvwprintw(perf_panel_window, g_hash_table_size(data->perf_list) + 1, 1, " 's' to sort"); + mvwprintw(perf_panel_window, g_hash_table_size(data->perf_list) + 1, 1, + " 's' to sort"); wattroff(perf_panel_window, A_BOLD); if (toggle_sort == 1) { @@ -952,12 +981,8 @@ void *handle_keyboard(void *p) toggle_perf_panel(); break; default: - /* - * commented because it makes the list refresh in different order - * if we sort and there are equal values - if (data) - update_current_view(); - */ + if (data) + update_current_view(); break; } update_footer(); diff --git a/src/iostreamtop.c b/src/iostreamtop.c index 1594df7..a1c78a9 100644 --- a/src/iostreamtop.c +++ b/src/iostreamtop.c @@ -16,38 +16,154 @@ * MA 02111-1307, USA. */ +#include +#include +#include +#include +#include #include #include "lttngtoptypes.h" #include "common.h" #include "iostreamtop.h" -#include +void add_file(struct processtop *proc, struct files *file, int fd) +{ + if (proc->process_files_table->len <= fd) { + g_ptr_array_set_size(proc->process_files_table, fd); + g_ptr_array_add(proc->process_files_table, file); + } else { + g_ptr_array_index(proc->process_files_table, fd) = file; + } + file->fd = fd; +} + + +void insert_file(struct processtop *proc, int fd) +{ + struct files *tmp; + + if (fd >= proc->process_files_table->len) { + tmp = g_new0(struct files, 1); + tmp->name = "Unknown"; + 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; + add_file(proc, tmp, fd); + } + } +} + +void close_file(struct processtop *proc, int fd) +{ + int len; + + 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) { + g_ptr_array_remove_index_fast(proc->process_files_table, fd); + g_ptr_array_set_size(proc->process_files_table, len + 1); + } +} + +struct files *get_file(struct processtop *proc, int fd) +{ + struct files *tmp; + 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"); +} int update_iostream_ret(struct lttngtop *ctx, int tid, char *comm, unsigned long timestamp, int cpu_id, int ret) { struct processtop *tmp; + struct files *tmpfile; int err = 0; tmp = get_proc(ctx, tid, comm, timestamp); - if ((tmp->iostream->syscall_info != NULL) && (tmp->iostream->syscall_info->cpu_id == cpu_id)) { - if (tmp->iostream->syscall_info->type == __NR_read && ret > 0) { - tmp->iostream->ret_read += ret; - tmp->iostream->ret_total += ret; - } else if(tmp->iostream->syscall_info->type == __NR_write && ret > 0) { - tmp->iostream->ret_write += ret; - tmp->iostream->ret_total += ret; - } else{ + + 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); + 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); + tmpfile->write += ret; + } else if (tmp->syscall_info->type == __NR_open + && ret > 0) { + add_file(tmp, tmp->files_history->file, ret); + } else { err = -1; } - free(tmp->iostream->syscall_info); - tmp->iostream->syscall_info = NULL; - } - + g_free(tmp->syscall_info); + tmp->syscall_info = NULL; + } return err; } +struct syscalls *create_syscall_info(unsigned int type, unsigned int 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_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) { @@ -96,7 +212,8 @@ enum bt_cb_ret handle_exit_syscall(struct bt_ctf_event *call_data, } /* - * if we encounter an exit_syscall and it is not for a syscall read or write + * 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) @@ -114,11 +231,11 @@ enum bt_cb_ret handle_sys_write(struct bt_ctf_event *call_data, { struct definition *scope; struct processtop *tmp; - struct syscalls *syscall_info; unsigned long timestamp; uint64_t cpu_id; char *comm; int64_t tid; + int fd; timestamp = bt_ctf_get_timestamp(call_data); if (timestamp == -1ULL) @@ -149,12 +266,19 @@ enum bt_cb_ret handle_sys_write(struct bt_ctf_event *call_data, goto error; } - syscall_info = malloc(sizeof(struct syscalls)); - syscall_info->cpu_id = cpu_id; - syscall_info->type = __NR_write; - syscall_info->tid = tid; + 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, comm, timestamp); - tmp->iostream->syscall_info = syscall_info; + tmp->syscall_info = create_syscall_info(__NR_write, cpu_id, tid, fd); + + insert_file(tmp, fd); return BT_CB_OK; @@ -167,11 +291,11 @@ enum bt_cb_ret handle_sys_read(struct bt_ctf_event *call_data, { struct processtop *tmp; struct definition *scope; - struct syscalls * syscall_info; unsigned long timestamp; uint64_t cpu_id; char *comm; int64_t tid; + int fd; timestamp = bt_ctf_get_timestamp(call_data); if (timestamp == -1ULL) @@ -202,12 +326,81 @@ enum bt_cb_ret handle_sys_read(struct bt_ctf_event *call_data, goto error; } - syscall_info = malloc(sizeof(struct syscalls)); - syscall_info->cpu_id = cpu_id; - syscall_info->type = __NR_read; - syscall_info->tid = tid; + 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, comm, 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; + struct definition *scope; + unsigned long timestamp; + uint64_t cpu_id; + char *comm; + int64_t tid; + char *file; + + timestamp = bt_ctf_get_timestamp(call_data); + if (timestamp == -1ULL) + goto error; + + scope = bt_ctf_get_top_level_scope(call_data, + BT_STREAM_EVENT_CONTEXT); + comm = bt_ctf_get_char_array(bt_ctf_get_field(call_data, + scope, "_procname")); + if (bt_ctf_field_get_error()) { + fprintf(stderr, "Missing procname context info\n"); + goto error; + } + + tid = bt_ctf_get_int64(bt_ctf_get_field(call_data, + scope, "_tid")); + if (bt_ctf_field_get_error()) { + fprintf(stderr, "Missing tid context info\n"); + goto error; + } + + scope = bt_ctf_get_top_level_scope(call_data, + BT_STREAM_PACKET_CONTEXT); + cpu_id = bt_ctf_get_uint64(bt_ctf_get_field(call_data, + scope, "cpu_id")); + if (bt_ctf_field_get_error()) { + fprintf(stderr, "Missing cpu_id context info\n"); + goto error; + } + + 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 fd context info\n"); + goto error; + } + tmp = get_proc(<tngtop, tid, comm, timestamp); - tmp->iostream->syscall_info = syscall_info; + 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; @@ -215,3 +408,51 @@ error: return BT_CB_ERROR_STOP; } + +enum bt_cb_ret handle_sys_close(struct bt_ctf_event *call_data, + void *private_data) +{ + struct definition *scope; + unsigned long timestamp; + int64_t tid; + struct processtop *tmp; + char *comm; + int fd; + + timestamp = bt_ctf_get_timestamp(call_data); + if (timestamp == -1ULL) + goto error; + + scope = bt_ctf_get_top_level_scope(call_data, + BT_STREAM_EVENT_CONTEXT); + comm = bt_ctf_get_char_array(bt_ctf_get_field(call_data, + scope, "_procname")); + if (bt_ctf_field_get_error()) { + fprintf(stderr, "Missing procname context info\n"); + goto error; + } + + tid = bt_ctf_get_int64(bt_ctf_get_field(call_data, + scope, "_tid")); + if (bt_ctf_field_get_error()) { + fprintf(stderr, "Missing tid context info\n"); + goto error; + } + + scope = bt_ctf_get_top_level_scope(call_data, + BT_EVENT_FIELDS); + 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, comm, timestamp); + close_file(tmp, fd); + + return BT_CB_OK; + +error: + return BT_CB_ERROR_STOP; +} diff --git a/src/iostreamtop.h b/src/iostreamtop.h index bde4dbf..4176c69 100644 --- a/src/iostreamtop.h +++ b/src/iostreamtop.h @@ -20,22 +20,23 @@ #define _IOSTREAMTOP_H #include +#include #include #include #include -/* -#define SYS_READ 1 -#define SYS_WRITE 2 -*/ +struct files *get_file(struct processtop *proc, int fd); +void show_table(GPtrArray *tab); enum bt_cb_ret handle_exit_syscall(struct bt_ctf_event *call_data, void *private_data); - enum bt_cb_ret handle_sys_write(struct bt_ctf_event *call_data, void *private_data); - enum bt_cb_ret handle_sys_read(struct bt_ctf_event *call_data, void *private_data); +enum bt_cb_ret handle_sys_open(struct bt_ctf_event *call_data, + void *private_data); +enum bt_cb_ret handle_sys_close(struct bt_ctf_event *call_data, + void *private_data); #endif /* _IOSTREAMTOP_H */ diff --git a/src/lttngtop.c b/src/lttngtop.c index 52bf6e9..8dc96ae 100644 --- a/src/lttngtop.c +++ b/src/lttngtop.c @@ -431,6 +431,13 @@ void iter_trace(struct bt_context *bt_ctx) 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); while ((event = bt_ctf_iter_read_event(iter)) != NULL) { ret = bt_iter_next(bt_ctf_get_iter(iter)); if (ret < 0) diff --git a/src/lttngtoptypes.h b/src/lttngtoptypes.h index 7030b32..7e105f9 100644 --- a/src/lttngtoptypes.h +++ b/src/lttngtoptypes.h @@ -51,16 +51,20 @@ struct processtop { 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; - /* IO speed for this process */ - struct iostream *iostream; }; struct perfcounter @@ -114,6 +118,11 @@ struct files { /* 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 */ @@ -145,9 +154,10 @@ struct vmas { struct syscalls { unsigned int id; unsigned long count; - unsigned int cpu_id; - unsigned int type; - unsigned int tid; + unsigned int cpu_id; + unsigned int type; + unsigned int tid; + unsigned int fd; }; struct signals { @@ -156,11 +166,11 @@ struct signals { unsigned long count; }; -struct iostream { - struct syscalls *syscall_info; /* NULL if there is no waiting for an exit_syscall */ - unsigned long ret_read; /* value returned by an I/O syscall_exit for a sys_read*/ - unsigned long ret_write; /* value returned by an I/O syscall_exit for a sys_write*/ - unsigned long ret_total; +struct file_info { + struct file_info *next; + char *name; + int fd; + int status; }; #endif /* LTTNGTOPTYPES_H */