top: show execution times per SC and per EC

Issue #3192
This commit is contained in:
Alexander Boettcher 2017-09-11 14:29:26 +02:00 committed by Christian Helmuth
parent 36fac8e22b
commit 93ba870b2d
2 changed files with 76 additions and 24 deletions

View File

@ -7,7 +7,10 @@ Configuration
The interval of output generation can be defined via the 'period_ms' attribute The interval of output generation can be defined via the 'period_ms' attribute
of the '<config>' node. The value is specified in milliseconds. of the '<config>' node. The value is specified in milliseconds.
The threads with the top most execution time are shown per CPU. The sorting
of the execution time can be shown per thread ('ec' - execution context) or,
if supported by the kernel, per scheduling context of a thread ('sc').
The following example shows the default values. The following example shows the default values.
! <config period_ms="5000"/> ! <config period_ms="5000" sort_time="ec"/>

View File

@ -20,6 +20,7 @@
#include <base/heap.h> #include <base/heap.h>
#include <os/reporter.h> #include <os/reporter.h>
enum SORT_TIME { EC_TIME = 0, SC_TIME = 1};
struct Trace_subject_registry struct Trace_subject_registry
{ {
@ -34,17 +35,23 @@ struct Trace_subject_registry
/** /**
* Execution time during the last period * Execution time during the last period
*/ */
unsigned long long recent_execution_time = 0; Genode::uint64_t recent_time[2] = { 0, 0 };
Entry(Genode::Trace::Subject_id id) : id(id) { } Entry(Genode::Trace::Subject_id id) : id(id) { }
void update(Genode::Trace::Subject_info const &new_info) void update(Genode::Trace::Subject_info const &new_info)
{ {
if (new_info.execution_time().thread_context < info.execution_time().thread_context) if (new_info.execution_time().thread_context < info.execution_time().thread_context)
recent_execution_time = 0; recent_time[EC_TIME] = 0;
else else
recent_execution_time = new_info.execution_time().thread_context - recent_time[EC_TIME] = new_info.execution_time().thread_context -
info.execution_time().thread_context; info.execution_time().thread_context;
if (new_info.execution_time().scheduling_context < info.execution_time().scheduling_context)
recent_time[SC_TIME] = 0;
else
recent_time[SC_TIME] = new_info.execution_time().scheduling_context -
info.execution_time().scheduling_context;
info = new_info; info = new_info;
} }
@ -67,7 +74,8 @@ struct Trace_subject_registry
enum { MAX_CPUS_X = 16, MAX_CPUS_Y = 1, MAX_ELEMENTS_PER_CPU = 6}; enum { MAX_CPUS_X = 16, MAX_CPUS_Y = 1, MAX_ELEMENTS_PER_CPU = 6};
/* accumulated execution time on all CPUs */ /* accumulated execution time on all CPUs */
unsigned long long total [MAX_CPUS_X][MAX_CPUS_Y]; unsigned long long total_first [MAX_CPUS_X][MAX_CPUS_Y];
unsigned long long total_second [MAX_CPUS_X][MAX_CPUS_Y];
/* most significant consumer per CPU */ /* most significant consumer per CPU */
Entry const * load[MAX_CPUS_X][MAX_CPUS_Y][MAX_ELEMENTS_PER_CPU]; Entry const * load[MAX_CPUS_X][MAX_CPUS_Y][MAX_ELEMENTS_PER_CPU];
@ -121,7 +129,7 @@ struct Trace_subject_registry
/* remove dead threads which did not run in the last period */ /* remove dead threads which did not run in the last period */
if (e->info.state() == Genode::Trace::Subject_info::DEAD && if (e->info.state() == Genode::Trace::Subject_info::DEAD &&
!e->recent_execution_time) { !e->recent_time[EC_TIME] && !e->recent_time[SC_TIME]) {
trace.free(e->id); trace.free(e->id);
_entries.remove(e); _entries.remove(e);
@ -144,12 +152,16 @@ struct Trace_subject_registry
} }
} }
void top() void top(enum SORT_TIME sorting)
{ {
/* clear old calculations */ /* clear old calculations */
Genode::memset(total, 0, sizeof(total)); Genode::memset(total_first, 0, sizeof(total_first));
Genode::memset(total_second, 0, sizeof(total_second));
Genode::memset(load, 0, sizeof(load)); Genode::memset(load, 0, sizeof(load));
unsigned const first = sorting == EC_TIME ? EC_TIME : SC_TIME;
unsigned const second = sorting == EC_TIME ? SC_TIME : EC_TIME;
for (Entry const *e = _entries.first(); e; e = e->next()) { for (Entry const *e = _entries.first(); e; e = e->next()) {
/* collect highest execution time per CPU */ /* collect highest execution time per CPU */
@ -163,7 +175,8 @@ struct Trace_subject_registry
continue; continue;
} }
total[x][y] += e->recent_execution_time; total_first[x][y] += e->recent_time[first];
total_second[x][y] += e->recent_time[second];
enum { NONE = ~0U }; enum { NONE = ~0U };
unsigned replace = NONE; unsigned replace = NONE;
@ -182,16 +195,16 @@ struct Trace_subject_registry
} }
for (unsigned i = 0; i < MAX_ELEMENTS_PER_CPU; i++) { for (unsigned i = 0; i < MAX_ELEMENTS_PER_CPU; i++) {
if (e->recent_execution_time if (e->recent_time[first]
<= load[x][y][i]->recent_execution_time) <= load[x][y][i]->recent_time[first])
continue; continue;
if (replace == NONE) { if (replace == NONE) {
replace = i; replace = i;
continue; continue;
} }
if (load[x][y][replace]->recent_execution_time if (load[x][y][replace]->recent_time[first]
> load[x][y][i]->recent_execution_time) > load[x][y][i]->recent_time[first])
replace = i; replace = i;
} }
@ -211,8 +224,8 @@ struct Trace_subject_registry
if (!load[x][y][j]) if (!load[x][y][j])
break; break;
if (load[x][y][i]->recent_execution_time if (load[x][y][i]->recent_time[first]
< load[x][y][j]->recent_execution_time) { < load[x][y][j]->recent_time[first]) {
Entry const * tmp = load[x][y][j]; Entry const * tmp = load[x][y][j];
load[x][y][j] = load[x][y][i]; load[x][y][j] = load[x][y][i];
@ -232,23 +245,47 @@ struct Trace_subject_registry
for (unsigned x = 0; x < MAX_CPUS_X; x++) { for (unsigned x = 0; x < MAX_CPUS_X; x++) {
for (unsigned y = 0; y < MAX_CPUS_Y; y++) { for (unsigned y = 0; y < MAX_CPUS_Y; y++) {
for (unsigned i = 0; i < MAX_ELEMENTS_PER_CPU; i++) { for (unsigned i = 0; i < MAX_ELEMENTS_PER_CPU; i++) {
if (!load[x][y][i] || !total[x][y]) if (!load[x][y][i] || !total_first[x][y])
continue; continue;
unsigned percent = load[x][y][i]->recent_execution_time * 100 / total[x][y]; unsigned ec_percent = load[x][y][i]->recent_time[first] * 100 / total_first[x][y];
unsigned rest = load[x][y][i]->recent_execution_time * 10000 / total[x][y] - (percent * 100); unsigned ec_rest = load[x][y][i]->recent_time[first] * 10000 / total_first[x][y] - (ec_percent * 100);
unsigned sc_percent = 0;
unsigned sc_rest = 0;
if (total_second[x][y]) {
sc_percent = load[x][y][i]->recent_time[second] * 100 / total_second[x][y];
sc_rest = load[x][y][i]->recent_time[second] * 10000 / total_second[x][y] - (sc_percent * 100);
}
enum { NAME_SPACE = 24 };
static char space[NAME_SPACE];
Genode::memset(space, ' ', NAME_SPACE - 1);
unsigned thread_name_len = load[x][y][i]->info.thread_name().length();
if (!thread_name_len)
space[NAME_SPACE - 1] = 0;
else
if (thread_name_len >= NAME_SPACE)
space[0] = 0;
else
space[NAME_SPACE - thread_name_len] = 0;
Genode::String<NAME_SPACE> space_string(space);
using Genode::log; using Genode::log;
log("cpu=", load[x][y][i]->info.affinity().xpos(), ".", log("cpu=", load[x][y][i]->info.affinity().xpos(), ".",
load[x][y][i]->info.affinity().ypos(), " ", load[x][y][i]->info.affinity().ypos(), " ",
percent < 10 ? " " : (percent < 100 ? " " : ""), ec_percent < 10 ? " " : (ec_percent < 100 ? " " : ""),
percent, ".", rest < 10 ? "0" : "", rest, "% " ec_percent, ".", ec_rest < 10 ? "0" : "", ec_rest, "% ",
"thread='", load[x][y][i]->info.thread_name(), "' " sc_percent < 10 ? " " : (sc_percent < 100 ? " " : ""),
sc_percent, ".", sc_rest < 10 ? "0" : "", sc_rest, "% "
"thread='", load[x][y][i]->info.thread_name(), "' ", space_string,
"label='", load[x][y][i]->info.session_label(), "'"); "label='", load[x][y][i]->info.session_label(), "'");
} }
} }
} }
if (load[0][0][0] && load[0][0][0]->recent_execution_time) if (load[0][0][0] && load[0][0][0]->recent_time[first])
Genode::log(""); Genode::log("");
} }
}; };
@ -278,6 +315,8 @@ struct App::Main
unsigned long _period_ms = _default_period_ms(); unsigned long _period_ms = _default_period_ms();
SORT_TIME _sort { EC_TIME };
Attached_rom_dataspace _config { _env, "config" }; Attached_rom_dataspace _config { _env, "config" };
Timer::Connection _timer { _env }; Timer::Connection _timer { _env };
@ -312,6 +351,16 @@ void App::Main::_handle_config()
_period_ms = _config.xml().attribute_value("period_ms", _default_period_ms()); _period_ms = _config.xml().attribute_value("period_ms", _default_period_ms());
String<8> ec_sc(_config.xml().attribute_value("sort_time", String<8>("ec")));
if (ec_sc == "ec")
_sort = EC_TIME;
else
_sort = SC_TIME;
log("sorting based on ",
_sort == EC_TIME ? "execution context (ec) [other option is scheduling context (sc)]"
: "scheduling context (sc) [other option is execution context (ec)]");
_timer.trigger_periodic(1000*_period_ms); _timer.trigger_periodic(1000*_period_ms);
} }
@ -328,7 +377,7 @@ void App::Main::_handle_period()
} }
/* show most significant consumers */ /* show most significant consumers */
_trace_subject_registry.top(); _trace_subject_registry.top(_sort);
/* by destructing the session we free up the allocated memory in core */ /* by destructing the session we free up the allocated memory in core */
if (reconstruct) { if (reconstruct) {