From 384a901c64991dc0b87838ef6f31cf539e5eeb3d Mon Sep 17 00:00:00 2001 From: Jonathan Herman Date: Wed, 28 Mar 2012 20:00:09 -0400 Subject: containers: both virtual tasks and cpus now drawn --- Makefile | 2 +- rt-graph.c | 2 +- rt-plot-container.c | 46 +------ rt-plot-container.h | 1 + rt-plot-vcpu.c | 279 ++++++++++++++++++++++------------------- rt-plot-vcpu.h | 30 +++++ rt-plot-vtask.c | 208 +++++++++++++++++++++++++++++++ rt-plot-vtask.h | 4 + rt-plot.c | 352 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 751 insertions(+), 173 deletions(-) create mode 100644 rt-plot-vtask.c create mode 100644 rt-plot-vtask.h create mode 100644 rt-plot.c diff --git a/Makefile b/Makefile index f1254c3..1a8c0a2 100644 --- a/Makefile +++ b/Makefile @@ -300,7 +300,7 @@ TRACE_CMD_OBJS = trace-cmd.o trace-record.o trace-read.o trace-split.o trace-lis trace-stack.o trace-options.o TRACE_VIEW_OBJS = trace-view.o trace-view-store.o RT_GRAPH_OBJS = rt-graph.o rt-plot-task.o rt-plot-cpu.o rt-plot-container.o rt-plot.o \ - rt-plot-vcpu.o + rt-plot-vcpu.o rt-plot-vtask.o TRACE_GRAPH_OBJS = trace-graph.o trace-plot.o \ trace-plot-cpu.o trace-plot-task.o \ $(RT_GRAPH_OBJS) task-list.o diff --git a/rt-graph.c b/rt-graph.c index fbf61bd..239018e 100644 --- a/rt-graph.c +++ b/rt-graph.c @@ -3,7 +3,7 @@ #include "trace-graph.h" #include "trace-hash.h" -#define DEBUG_LEVEL 0 +#define DEBUG_LEVEL 4 #if DEBUG_LEVEL > 0 #define dprintf(l, x...) \ do { \ diff --git a/rt-plot-container.c b/rt-plot-container.c index f36e2f8..97e9fd8 100644 --- a/rt-plot-container.c +++ b/rt-plot-container.c @@ -4,40 +4,6 @@ #include "trace-hash.h" #include "trace-filter.h" -struct vtask_info { - struct rt_task_info task_info; - struct cont_list *cont; -}; - -static void insert_vtask(struct graph_info *ginfo, struct cont_list *cont, - struct vcpu_list *vcpu_info) -{ - struct graph_plot *plot; - struct vtask_info *vtask; - char *label; - int len; - - vtask = malloc_or_die(sizeof(*vtask)); - vtask->task_info.pid = vcpu_info->sid; - vtask->task_info.wcet = vcpu_info->params.wcet; - vtask->task_info.period = vcpu_info->params.period; - vtask->task_info.label = malloc_or_die(LLABEL); - vtask->cont = cont; - - g_assert(cont); - - len = strlen(cont->name) + 100; - label = malloc_or_die(len); - snprintf(label, len, "%s-%d\n(%1.1f, %1.1f)", - cont->name, -vtask->task_info.pid, - nano_as_milli(vtask->task_info.wcet), - nano_as_milli(vtask->task_info.period)); - - plot = trace_graph_plot_append(ginfo, label, PLOT_TYPE_SERVER_TASK, - TIME_TYPE_RT, &rt_task_cb, vtask); - trace_graph_plot_add_all_recs(ginfo, plot); -} - int rt_plot_get_containers(struct graph_info *ginfo, gint **conts, gboolean plotted_only) { @@ -122,7 +88,7 @@ void rt_plot_container(struct graph_info *ginfo, int cid) cont->plotted = TRUE; for (vlist = cont->vcpus; vlist; vlist = vlist->next) { - /* insert_vtask(ginfo, cont, vlist); */ + insert_vtask(ginfo, cont, vlist); insert_vcpu(ginfo, cont, vlist); } } @@ -185,7 +151,6 @@ static void do_container_filter(struct graph_info *ginfo, struct cont_filter_helper *helper, gpointer data) { struct graph_plot *plot; - struct vtask_info *vtask; struct vcpu_info *vcpu; struct cont_list *cont; int i, c, *append; @@ -201,13 +166,8 @@ static void do_container_filter(struct graph_info *ginfo, plot->type != PLOT_TYPE_SERVER_CPU) continue; - if (plot->type == PLOT_TYPE_SERVER_TASK) { - vtask = plot->private; - cont = vtask->cont; - } else { - vcpu = plot->private; - cont = vcpu->cont; - } + vcpu = plot->private; + cont = vcpu->cont; for (c = 0; c < helper->num_conts; c++) { if (helper->conts[c] == cont->cid) diff --git a/rt-plot-container.h b/rt-plot-container.h index e6c57b0..cde5c2f 100644 --- a/rt-plot-container.h +++ b/rt-plot-container.h @@ -2,6 +2,7 @@ #define __RT_PLOT_CONTAINER_H #include "rt-plot-vcpu.h" +#include "rt-plot-vtask.h" typedef void (*cont_dialog_cb_func)(gboolean, gint*, gint*, gpointer); diff --git a/rt-plot-vcpu.c b/rt-plot-vcpu.c index 7951ad4..0ec55da 100644 --- a/rt-plot-vcpu.c +++ b/rt-plot-vcpu.c @@ -2,7 +2,7 @@ #include #include "trace-graph.h" -#define DEBUG_LEVEL 4 +#define DEBUG_LEVEL 0 #if DEBUG_LEVEL > 0 #define dprintf(l, x...) \ do { \ @@ -22,95 +22,6 @@ static void update_tid(struct vcpu_info *info, int tid) } } - -static int try_release(struct graph_info *ginfo, struct vcpu_info *vcpu_info, - struct record *record, struct plot_info *info) -{ - int sid, job, match, ret = 0; - unsigned long long release, deadline; - - match = rt_graph_check_server_release(ginfo, record, &sid, &job, - &release, &deadline); - if (match && sid == vcpu_info->sid) { - info->release = TRUE; - info->rtime = release; - - info->deadline = TRUE; - info->dtime = deadline; - - dprintf(3, "VCPU release for %d:%d on %d, rel: %llu, dead: %llu\n", - sid, job, record->cpu, release, deadline); - - ret = 1; - } - return ret; -} - -static int try_completion(struct graph_info *ginfo, - struct vcpu_info *vcpu_info, - struct record *record, struct plot_info *info) -{ - int sid, job, match, ret = 0; - unsigned long long ts; - - match = rt_graph_check_server_completion(ginfo, record, &sid, &job, &ts); - if (match && sid == vcpu_info->sid) { - - info->completion = TRUE; - info->ctime = ts; - - dprintf(3, "VCPU completion for %d:%d on %d at %llu\n", - sid, job, record->cpu, ts); - ret = 1; - } - return ret; -} - -static int try_block(struct graph_info *ginfo, struct vcpu_info *vcpu_info, - struct record *record, struct plot_info *info) -{ - int sid, match, ret = 0; - unsigned long long ts; - - match = rt_graph_check_server_block(ginfo, record, &sid, &ts); - if (match && sid == vcpu_info->sid) { - vcpu_info->fresh = FALSE; - vcpu_info->block_time = ts; - vcpu_info->block_cpu = NO_CPU; - dprintf(3, "VCPU resume for %d on %d at %llu\n", - sid, record->cpu, ts); - ret = 1; - } - return ret; -} - -static int try_resume(struct graph_info *ginfo, struct vcpu_info *vcpu_info, - struct record *record, struct plot_info *info) -{ - int sid, match, ret = 0; - unsigned long long ts; - - match = rt_graph_check_server_resume(ginfo, record, &sid, &ts); - - if (match && sid == vcpu_info->sid) { - info->box = TRUE; - info->bcolor = 0x0; - info->bfill = TRUE; - info->bthin = TRUE; - info->bstart = vcpu_info->block_time; - info->bend = ts; - vcpu_info->fresh = FALSE; - - vcpu_info->block_time = 0ULL; - vcpu_info->block_cpu = NO_CPU; - dprintf(3, "VCPU resume for %d on %d at %llu\n", - sid, record->cpu, ts); - - ret = 1; - } - return ret; -} - static int try_server_switch_away(struct graph_info *ginfo, struct vcpu_info *vcpu_info, struct record *record, struct plot_info *info) @@ -259,16 +170,55 @@ static int rt_vcpu_plot_event(struct graph_info *ginfo, struct graph_plot *plot, match = try_server_switch_away(ginfo, vcpu_info, record, info) || try_server_switch_to(ginfo, vcpu_info, record, info) || - try_block(ginfo, vcpu_info, record, info) || - try_resume(ginfo, vcpu_info, record, info) || - try_release(ginfo, vcpu_info, record, info) || - try_completion(ginfo, vcpu_info, record, info) || + vcpu_try_block(ginfo, vcpu_info, record, info) || + vcpu_try_resume(ginfo, vcpu_info, record, info) || + vcpu_try_release(ginfo, vcpu_info, record, info) || + vcpu_try_completion(ginfo, vcpu_info, record, info) || try_switch_to(ginfo, vcpu_info, record, info) || try_switch_away(ginfo, vcpu_info, record, info); return match; } -static void rt_vcpu_plot_start(struct graph_info *ginfo, struct graph_plot *plot, + +const struct plot_callbacks rt_vcpu_cb = { + .start = rt_vcpu_plot_start, + .destroy = rt_vcpu_plot_destroy, + .plot_event = rt_vcpu_plot_event, + .display_last_event = rt_plot_display_last_event, + .display_info = rt_plot_display_info, + .match_time = rt_plot_match_time, + .find_record = rt_plot_find_record, +}; + +void insert_vcpu(struct graph_info *ginfo, struct cont_list *cont, + struct vcpu_list *vcpu_info) +{ + struct graph_plot *plot; + struct vcpu_info *vcpu; + char *label; + + vcpu = malloc_or_die(sizeof(*vcpu)); + vcpu->sid = vcpu_info->sid; + vcpu->cont = cont; + vcpu->label = malloc_or_die(LLABEL); + + vcpu->common.record_matches = rt_vcpu_plot_record_matches; + vcpu->common.is_drawn = rt_vcpu_plot_is_drawn; + vcpu->common.write_header = rt_vcpu_plot_write_header; + + g_assert(cont); + + label = malloc_or_die(1); + snprintf(label, 2, " "); + plot = trace_graph_plot_append(ginfo, label, PLOT_TYPE_SERVER_CPU, + TIME_TYPE_RT, &rt_vcpu_cb, vcpu); + trace_graph_plot_add_all_recs(ginfo, plot); +} + +/** + * + */ +void rt_vcpu_plot_start(struct graph_info *ginfo, struct graph_plot *plot, unsigned long long time) { struct vcpu_info *vcpu_info = plot->private; @@ -284,11 +234,15 @@ static void rt_vcpu_plot_start(struct graph_info *ginfo, struct graph_plot *plot vcpu_info->fresh = TRUE; vcpu_info->running = FALSE; + vcpu_info->last_job = -1; vcpu_info->run_tid = 0; } -static void rt_vcpu_plot_destroy(struct graph_info *ginfo, struct graph_plot *plot) +/** + * + */ +void rt_vcpu_plot_destroy(struct graph_info *ginfo, struct graph_plot *plot) { struct vcpu_info *vcpu_info = plot->private; trace_graph_plot_remove_all_recs(ginfo, plot); @@ -296,10 +250,10 @@ static void rt_vcpu_plot_destroy(struct graph_info *ginfo, struct graph_plot *pl free(vcpu_info); } -/* +/** * Return 1 if @record is relevant to @match_sid. */ -static int rt_vcpu_plot_record_matches(struct rt_plot_common *rt, +int rt_vcpu_plot_record_matches(struct rt_plot_common *rt, struct graph_info *ginfo, struct record *record) { @@ -318,11 +272,11 @@ static int rt_vcpu_plot_record_matches(struct rt_plot_common *rt, return (sid == vcpu_info->sid); } -/* +/** * Return true if the given record type is drawn on screen. This does not * include event line markes. */ -static int +int rt_vcpu_plot_is_drawn(struct graph_info *ginfo, int eid) { struct rt_graph_info *rtg_info = &ginfo->rtg_info; @@ -334,7 +288,10 @@ rt_vcpu_plot_is_drawn(struct graph_info *ginfo, int eid) eid == rtg_info->server_resume_id); } -static struct record* +/** + * + */ +struct record* rt_vcpu_plot_write_header(struct rt_plot_common *rt, struct graph_info *ginfo, struct trace_seq *s, @@ -358,38 +315,104 @@ rt_vcpu_plot_write_header(struct rt_plot_common *rt, return record; } +/** + * vcpu_try_release - draw release and deadline arrows if record matches + */ +int vcpu_try_release(struct graph_info *ginfo, struct vcpu_info *vcpu_info, + struct record *record, struct plot_info *info) +{ + int sid, job, match, ret = 0; + unsigned long long release, deadline; -const struct plot_callbacks rt_vcpu_cb = { - .start = rt_vcpu_plot_start, - .destroy = rt_vcpu_plot_destroy, - .plot_event = rt_vcpu_plot_event, - .display_last_event = rt_plot_display_last_event, - .display_info = rt_plot_display_info, - .match_time = rt_plot_match_time, - .find_record = rt_plot_find_record, -}; + match = rt_graph_check_server_release(ginfo, record, &sid, &job, + &release, &deadline); + if (match && sid == vcpu_info->sid) { + info->release = TRUE; + info->rtime = release; -void insert_vcpu(struct graph_info *ginfo, struct cont_list *cont, - struct vcpu_list *vcpu_info) + info->deadline = TRUE; + info->dtime = deadline; + + dprintf(3, "VCPU release for %d:%d on %d, rel: %llu, dead: %llu\n", + sid, job, record->cpu, release, deadline); + + ret = 1; + } + return ret; +} + +/** + * vcpu_try_completion - draw completion if record matches + */ +int vcpu_try_completion(struct graph_info *ginfo, + struct vcpu_info *vcpu_info, + struct record *record, struct plot_info *info) { - struct graph_plot *plot; - struct vcpu_info *vcpu; - char *label; + int sid, job, match, ret = 0; + unsigned long long ts; - vcpu = malloc_or_die(sizeof(*vcpu)); - vcpu->sid = vcpu_info->sid; - vcpu->cont = cont; - vcpu->label = malloc_or_die(LLABEL); + match = rt_graph_check_server_completion(ginfo, record, &sid, &job, &ts); + if (match && sid == vcpu_info->sid) { - vcpu->common.record_matches = rt_vcpu_plot_record_matches; - vcpu->common.is_drawn = rt_vcpu_plot_is_drawn; - vcpu->common.write_header = rt_vcpu_plot_write_header; + info->completion = TRUE; + info->ctime = ts; - g_assert(cont); + dprintf(3, "VCPU completion for %d:%d on %d at %llu\n", + sid, job, record->cpu, ts); + ret = 1; + } + return ret; +} - label = malloc_or_die(1); - snprintf(label, 2, " "); - plot = trace_graph_plot_append(ginfo, label, PLOT_TYPE_SERVER_CPU, - TIME_TYPE_RT, &rt_vcpu_cb, vcpu); - trace_graph_plot_add_all_recs(ginfo, plot); +/** + * vcpu_try_block - start block box if record matches + */ +int vcpu_try_block(struct graph_info *ginfo, struct vcpu_info *vcpu_info, + struct record *record, struct plot_info *info) +{ + int sid, match, ret = 0; + unsigned long long ts; + + match = rt_graph_check_server_block(ginfo, record, &sid, &ts); + if (match && sid == vcpu_info->sid) { + vcpu_info->fresh = FALSE; + vcpu_info->block_time = ts; + vcpu_info->block_cpu = NO_CPU; + dprintf(3, "VCPU resume for %d on %d at %llu\n", + sid, record->cpu, ts); + ret = 1; + } + return ret; } + +/** + * vcpu_try_resume - end block box if record matches + */ +int vcpu_try_resume(struct graph_info *ginfo, struct vcpu_info *vcpu_info, + struct record *record, struct plot_info *info) +{ + int sid, match, ret = 0; + unsigned long long ts; + + match = rt_graph_check_server_resume(ginfo, record, &sid, &ts); + + if (match && sid == vcpu_info->sid) { + info->box = TRUE; + info->bcolor = 0x0; + info->bfill = TRUE; + info->bthin = TRUE; + info->bstart = vcpu_info->block_time; + info->bend = ts; + vcpu_info->fresh = FALSE; + + vcpu_info->block_time = 0ULL; + vcpu_info->block_cpu = NO_CPU; + dprintf(3, "VCPU resume for %d on %d at %llu\n", + sid, record->cpu, ts); + + ret = 1; + } + return ret; +} + + diff --git a/rt-plot-vcpu.h b/rt-plot-vcpu.h index 9c50a63..f88e6ab 100644 --- a/rt-plot-vcpu.h +++ b/rt-plot-vcpu.h @@ -11,6 +11,8 @@ struct vcpu_info { int run_cpu; unsigned long long run_time; + int last_job; + int block_cpu; unsigned long long block_time; @@ -24,3 +26,31 @@ struct vcpu_info { void insert_vcpu(struct graph_info *ginfo, struct cont_list *cont, struct vcpu_list *vcpu_info); + + +/* drawing methods */ +int vcpu_try_release(struct graph_info *ginfo, struct vcpu_info *vcpu_info, + struct record *record, struct plot_info *info); +int vcpu_try_completion(struct graph_info *ginfo, + struct vcpu_info *vcpu_info, + struct record *record, struct plot_info *info); +int vcpu_try_block(struct graph_info *ginfo, struct vcpu_info *vcpu_info, + struct record *record, struct plot_info *info); +int vcpu_try_resume(struct graph_info *ginfo, struct vcpu_info *vcpu_info, + struct record *record, struct plot_info *info); + +/* trace-plot methods */ +void rt_vcpu_plot_start(struct graph_info *ginfo, struct graph_plot *plot, + unsigned long long time); +void rt_vcpu_plot_destroy(struct graph_info *ginfo, struct graph_plot *plot); + +/* rt-plot-common methods */ +int rt_vcpu_plot_record_matches(struct rt_plot_common *rt, + struct graph_info *ginfo, + struct record *record); +int rt_vcpu_plot_is_drawn(struct graph_info *ginfo, int eid); +struct record* +rt_vcpu_plot_write_header(struct rt_plot_common *rt, + struct graph_info *ginfo, + struct trace_seq *s, + unsigned long long time); diff --git a/rt-plot-vtask.c b/rt-plot-vtask.c new file mode 100644 index 0000000..5b54343 --- /dev/null +++ b/rt-plot-vtask.c @@ -0,0 +1,208 @@ +#include +#include +#include "trace-graph.h" + +#define DEBUG_LEVEL 0 +#if DEBUG_LEVEL > 0 +#define dprintf(l, x...) \ + do { \ + if (l <= DEBUG_LEVEL) \ + printf(x); \ + } while (0) +#else +#define dprintf(l, x...) do { if (0) printf(x); } while (0) +#endif + +static void update_job(struct vcpu_info *info, int job) +{ + info->fresh = FALSE; + if (job < info->last_job) { + dprintf(1, "Inconsistent job state for server %d:%d -> %d\n", + info->sid, info->last_job, job); + } + + if (job > info->last_job) { + info->last_job = job; + snprintf(info->label, LLABEL, "%d:%d", + info->sid, info->last_job); + } +} + +static int +try_server_switch_away(struct graph_info *ginfo, struct vcpu_info *vcpu_info, + struct record *record, struct plot_info *info) +{ + int job, sid, tid, match, ret = 0; + unsigned long long ts; + + match = rt_graph_check_server_switch_away(ginfo, record, + &sid, &job, + &tid, &ts); + if (match && sid == vcpu_info->sid) { + /* update_job(vcpu_info, job); */ + + if (vcpu_info->run_time < ts) { + info->box = TRUE; + info->bcolor = hash_pid(sid); + info->bfill = TRUE; + info->bstart = vcpu_info->run_time; + info->bend = ts; + info->blabel = vcpu_info->label; + } + + dprintf(3, "VCPU switch away from %d on %d:%d at %llu\n", + tid, sid, job, ts); + vcpu_info->run_time = 0ULL; + vcpu_info->run_cpu = NO_CPU; + vcpu_info->run_tid = 0; + + ret = 1; + } + + return ret; +} + +static int try_server_switch_to(struct graph_info *ginfo, struct vcpu_info *vcpu_info, + struct record *record, struct plot_info *info) +{ + int job, sid, tid, match, ret = 0; + unsigned long long ts; + + match = rt_graph_check_server_switch_to(ginfo, record, + &sid, &job, &tid, &ts); + if (match && sid == vcpu_info->sid) { + update_job(vcpu_info, job); + vcpu_info->run_time = ts; + vcpu_info->run_cpu = record->cpu; + vcpu_info->run_tid = tid; + dprintf(3, "Switch to %d for %d:%d at %llu\n", + tid, sid, job, ts); + ret = 1; + } + return ret; +} + +static int try_switch_to(struct graph_info *ginfo, struct vcpu_info *vcpu_info, + struct record *record, struct plot_info *info) +{ + int job, pid, match, ret = 0; + unsigned long long ts; + + match = rt_graph_check_switch_to(ginfo, record, &pid, &job, &ts); + if (match && pid && pid == vcpu_info->run_tid && vcpu_info->run_time) { + info->line = TRUE; + info->lcolor = hash_pid(pid); + ret = 1; + } + return ret; +} + +static int try_switch_away(struct graph_info *ginfo, struct vcpu_info *vcpu_info, + struct record *record, struct plot_info *info) +{ + int job, pid, match, ret = 0; + unsigned long long ts; + + match = rt_graph_check_switch_away(ginfo, record, &pid, &job, &ts); + if (match && pid && pid == vcpu_info->run_tid) { + info->line = TRUE; + info->lcolor = hash_pid(pid); + ret = 1; + } + return ret; +} + +static void do_plot_end(struct graph_info *ginfo, struct vcpu_info *vcpu_info, + struct plot_info *info) +{ + int tid, job, is_running; + unsigned long long deadline, release; + struct record *record; + + if (vcpu_info->run_time && vcpu_info->run_cpu != NO_CPU) { + info->box = TRUE; + info->bcolor = hash_pid(vcpu_info->sid); + info->bfill = TRUE; + info->bstart = vcpu_info->run_time; + info->bend = ginfo->view_end_time; + info->blabel = vcpu_info->label; + } else if (vcpu_info->fresh) { + is_running = get_server_info(ginfo, + (struct rt_plot_common*)vcpu_info, + vcpu_info->sid, + ginfo->view_end_time, + &release, &deadline, + &job, &tid, &record); + if (is_running) { + update_job(vcpu_info, job); + info->box = TRUE; + info->bcolor = hash_pid(vcpu_info->sid); + info->bfill = TRUE; + info->bstart = vcpu_info->run_time; + info->bend = ginfo->view_end_time; + info->blabel = vcpu_info->label; + } + } +} + +static int rt_vtask_plot_event(struct graph_info *ginfo, struct graph_plot *plot, + struct record *record, struct plot_info *info) +{ + int match; + struct vcpu_info *vcpu_info = plot->private; + + if (!record) { + do_plot_end(ginfo, vcpu_info, info); + return 0; + } + + match = try_server_switch_away(ginfo, vcpu_info, record, info) || + try_server_switch_to(ginfo, vcpu_info, record, info) || + vcpu_try_block(ginfo, vcpu_info, record, info) || + vcpu_try_resume(ginfo, vcpu_info, record, info) || + vcpu_try_release(ginfo, vcpu_info, record, info) || + vcpu_try_completion(ginfo, vcpu_info, record, info) || + try_switch_to(ginfo, vcpu_info, record, info) || + try_switch_away(ginfo, vcpu_info, record, info); + return match; +} + +const struct plot_callbacks rt_vtask_cb = { + .start = rt_vcpu_plot_start, + .destroy = rt_vcpu_plot_destroy, + .plot_event = rt_vtask_plot_event, + .display_last_event = rt_plot_display_last_event, + .display_info = rt_plot_display_info, + .match_time = rt_plot_match_time, + .find_record = rt_plot_find_record, +}; + +void insert_vtask(struct graph_info *ginfo, struct cont_list *cont, + struct vcpu_list *vcpu_info) +{ + struct graph_plot *plot; + struct vcpu_info *vtask; + char *label; + int len; + + vtask = malloc_or_die(sizeof(*vtask)); + vtask->sid = vcpu_info->sid; + vtask->label = malloc_or_die(LLABEL); + vtask->cont = cont; + + vtask->common.record_matches = rt_vcpu_plot_record_matches; + vtask->common.is_drawn = rt_vcpu_plot_is_drawn; + vtask->common.write_header = rt_vcpu_plot_write_header; + + + len = strlen(cont->name) + 100; + label = malloc_or_die(len); + snprintf(label, len, "%s - %d\n(%1.1f, %1.1f)", + cont->name, -vtask->sid, + nano_as_milli(vcpu_info->params.wcet), + nano_as_milli(vcpu_info->params.period)); + + plot = trace_graph_plot_append(ginfo, label, PLOT_TYPE_SERVER_TASK, + TIME_TYPE_RT, &rt_vtask_cb, vtask); + trace_graph_plot_add_all_recs(ginfo, plot); +} diff --git a/rt-plot-vtask.h b/rt-plot-vtask.h new file mode 100644 index 0000000..05a026c --- /dev/null +++ b/rt-plot-vtask.h @@ -0,0 +1,4 @@ +#include "trace-graph.h" + +void insert_vtask(struct graph_info *ginfo, struct cont_list *cont, + struct vcpu_list *vcpu_info); diff --git a/rt-plot.c b/rt-plot.c new file mode 100644 index 0000000..b04caf1 --- /dev/null +++ b/rt-plot.c @@ -0,0 +1,352 @@ +#include +#include "trace-graph.h" + +/** + * Return first relevant record after @time. + * @display: If set, only considers records which aren't plotted + */ +struct record* +__find_rt_record(struct graph_info *ginfo, struct rt_plot_common *rt_info, + guint64 time, int display) +{ + int next_cpu, match, eid, ignored; + struct record *record; + + set_cpus_to_rts(ginfo, time); + while ((record = tracecmd_read_next_data(ginfo->handle, &next_cpu))) { + eid = pevent_data_type(ginfo->pevent, record); + ignored = (eid == ginfo->event_sched_switch_id); + if (!ignored && display) { + ignored = rt_info->is_drawn(ginfo, eid); + } + match = !ignored && + rt_info->record_matches(rt_info, ginfo, record); + + if (get_rts(ginfo, record) >= time && match) + break; + free_record(record); + }; + + return record; +} + + +/** + * rt_plot_display_last_event - write event name at @time onto plot. + */ +int +rt_plot_display_last_event(struct graph_info *ginfo, struct graph_plot *plot, + struct trace_seq *s, unsigned long long time) +{ + int eid; + struct event_format *event; + struct record *record; + struct offset_cache *offsets; + struct rt_plot_common *rt_info = plot->private; + + offsets = save_offsets(ginfo); + + record = find_rt_display_record(ginfo, rt_info, time); + + restore_offsets(ginfo, offsets); + if (!record) + return 0; + + eid = pevent_data_type(ginfo->pevent, record); + event = pevent_data_event_from_type(ginfo->pevent, eid); + if (event) + trace_seq_puts(s, event->name); + else + trace_seq_printf(s, "UNKNOWN EVENT %d\n", eid); + trace_seq_putc(s, '\n'); + trace_seq_printf(s, "CPU %d\n", record->cpu); + free_record(record); + + return 1; +} + +/** + * rt_plot_display_info - write information about @time into @s + */ +int +rt_plot_display_info(struct graph_info *ginfo, struct graph_plot *plot, + struct trace_seq *s, unsigned long long time) +{ + struct rt_plot_common *rt_info = plot->private; + struct event_format *event; + struct record *record; + unsigned long long msec, nsec, rts; + int eid; + + record = rt_info->write_header(rt_info, ginfo, s, time); + + if (record) { + rts = get_rts(ginfo, record); + eid = pevent_data_type(ginfo->pevent, record); + + if (in_res(ginfo, rts, time)) { + event = pevent_data_event_from_type(ginfo->pevent, eid); + if (event) { + trace_seq_putc(s, '\n'); + trace_seq_puts(s, event->name); + trace_seq_putc(s, '\n'); + pevent_event_info(s, event, record); + } else + trace_seq_printf(s, "\nUNKNOWN EVENT %d\n", eid); + } + trace_seq_putc(s, '\n'); + nano_to_milli(time, &msec, &nsec); + trace_seq_printf(s, "%llu.%06llu ms CPU: %03d", + msec, nsec, record->cpu); + free_record(record); + } + + return 1; +} + +/** + * rt_plot_find_rt_record - return matching record around @time. + */ + struct record* +rt_plot_find_record(struct graph_info *ginfo, struct graph_plot *plot, + unsigned long long time) +{ + return find_rt_record(ginfo, plot->private, time); +} + +/** + * rt_plot_match_time - return 1 if there is an exact match at @time. + */ +int +rt_plot_match_time(struct graph_info *ginfo, struct graph_plot *plot, + unsigned long long time) +{ + struct record *record = NULL; + struct rt_plot_common *rt_info = plot->private; + int next_cpu, match, ret; + + set_cpus_to_rts(ginfo, time); + + do { + free_record(record); + record = tracecmd_read_next_data(ginfo->handle, &next_cpu); + if (!record) + return 0; + match = rt_info->record_matches(rt_info, ginfo, record); + } while ((!match && get_rts(ginfo, record) < time + 1) || + (match && get_rts(ginfo, record) < time)); + + if (record && get_rts(ginfo, record) == time) + ret = 1; + free_record(record); + + return ret; +} + + + +/** + * next_rts - find a real-time timestamp AROUND an FTRACE time + * @ginfo: Current state of the graph + * @cpu: CPU to search + * @ft_target: FTRACE time to seek towards + * + * Returns the RT time of a record CLOSELY BEFORE @ft_time. + */ +unsigned long long +next_rts(struct graph_info *ginfo, int cpu, unsigned long long ft_target) +{ + struct record *record; + unsigned long long ts = 0ULL; + tracecmd_set_cpu_to_timestamp(ginfo->handle, cpu, ft_target); + record = tracecmd_read_data(ginfo->handle, cpu); + if (record) { + ts = get_rts(ginfo, record); + free_record(record); + return ts; + } else + return 0; +} + +/** + * set_cpu_to_rts - seek CPU to a time closely preceding a real-time timestamp + * @ginfo: Current state o the graph + * @cpu: The CPU to seek + * @rt_target: RT time to seek towards + * + * This seeks to a real-time timestamp, not the default ftrace timestamps. + * The @cpu seek location will be placed before the given time, but will + * not necessarily be placed _right_ before the time. + */ +void +set_cpu_to_rts(struct graph_info *ginfo, unsigned long long rt_target, int cpu) +{ + struct record *record; + unsigned long long last_rts, rts, seek_time, last_seek; + long long diff; + + rts = next_rts(ginfo, cpu, rt_target); + diff = rt_target - rts; + + /* "Guess" a new target based on difference */ + seek_time = rt_target + diff; + rts = next_rts(ginfo, cpu, seek_time); + diff = rt_target - rts; + + /* Zero in in 1.5x the difference increments */ + if (rts && diff > 0) { + /* rts rt_target | real-time time + * seek ? | trace-cmd time + * ---|---->>----|-------- + */ + do { + last_seek = seek_time; + last_rts = rts; + seek_time = seek_time + 1.5 * (rt_target - rts); + rts = next_rts(ginfo, cpu, seek_time); + } while (rts < rt_target && last_rts != rts); + tracecmd_set_cpu_to_timestamp(ginfo->handle, cpu, last_seek); + seek_time = last_seek; + } else if (rts && diff < 0) { + /* rt_target rts | real-time time + * ? seek | trace-cmd time + * ---|----<<----|-------- + */ + do { + seek_time = seek_time - 1.5 * (rts - rt_target); + rts = next_rts(ginfo, cpu, seek_time); + } while (rts > rt_target); + } + + /* Get to first record at or after time */ + while ((record = tracecmd_read_data(ginfo->handle, cpu))) { + if (get_rts(ginfo, record) >= rt_target) + break; + free_record(record); + } + if (record) { + tracecmd_set_cursor(ginfo->handle, cpu, record->offset); + free_record(record); + } else + tracecmd_set_cpu_to_timestamp(ginfo->handle, cpu, seek_time); +} + +/** + * set_cpus_to_time - seek all cpus to real-time @rt_target + */ +void set_cpus_to_rts(struct graph_info *ginfo, unsigned long long rt_target) +{ + int cpu; + for (cpu = 0; cpu < ginfo->cpus; cpu++) + set_cpu_to_rts(ginfo, rt_target, cpu); +} + + +/** + * is_task_running - return 1 if @match_pid is running at @time. + */ +int is_task_running(struct graph_info *ginfo, + unsigned long long time, + int match_pid) +{ + int pid, job, cpu, running = 0; + unsigned long long ts, min_ts; + struct record *rec; + + set_cpus_to_rts(ginfo, time); + + min_ts = time - max_rt_search(ginfo); + + for (cpu = 0; cpu < ginfo->cpus; cpu++) { + rec = tracecmd_peek_data(ginfo->handle, cpu); + if (!rec) + continue; + + while ((rec = tracecmd_read_prev(ginfo->handle, rec))) { + if (get_rts(ginfo, rec) < min_ts) + goto out; + +#define ARG ginfo, rec, &pid, &job, &ts + if (rt_graph_check_switch_away(ARG)) { + if (pid == match_pid) + goto out; + } else if (rt_graph_check_switch_to(ARG)) { + if (pid == match_pid) { + running = 1; + goto out; + } + } +#undef ARG + } + free_record(rec); + + } + out: + free_record(rec); + return running; +} + +/** + * Find the information for the last release of @match_tid on @cpu before @time. + * + * This method will NOT re-seek the CPUs near time. The caller must have placed + * the CPUs near the the CPUs themselves. + * + * Returns release record and @out_job, @out_release, and @out_deadline if a + * release was found for @tid before @time. + */ +struct record* get_previous_release(struct graph_info *ginfo, int match_tid, + unsigned long long time, + int *out_job, + unsigned long long *out_release, + unsigned long long *out_deadline) +{ + int tid, cpu, match, job; + unsigned long long release, deadline, min_ts; + struct record *last_rec = NULL, *rec, *ret = NULL; + + min_ts = time - max_rt_search(ginfo); + + /* The release record could have occurred on any CPU. Search all */ + for (cpu = 0; cpu < ginfo->cpus; cpu++) { + last_rec = tracecmd_peek_data(ginfo->handle, cpu); + + /* Require a record to start with */ + if (!last_rec) + goto loop_end; + last_rec->ref_count++; + + while ((rec = tracecmd_read_prev(ginfo->handle, last_rec))) { + if (rec->ts < min_ts) { + free_record(rec); + goto loop_end; + } + +#define ARG ginfo, rec, &tid, &job, &release, &deadline + match = rt_graph_check_task_release(ARG) || + rt_graph_check_server_release(ARG); +#undef ARG + + free_record(last_rec); + last_rec = rec; + + /* Only consider releases before the current time */ + if (match && tid == match_tid && release <= time) { + /* Return the lastest release */ + if (!ret || *out_job < job) { + free_record(ret); + ret = rec; + *out_job = job; + *out_release = release; + *out_deadline = deadline; + } + + last_rec = NULL; + goto loop_end; + } + } + loop_end: + free_record(last_rec); + } + return ret; +} -- cgit v1.2.2