diff --git a/repos/os/run/trace.run b/repos/os/run/trace.run
index f3ccb15560..274bb62f13 100644
--- a/repos/os/run/trace.run
+++ b/repos/os/run/trace.run
@@ -7,6 +7,7 @@ set build_components {
drivers/timer
test/trace
lib/trace/policy/null
+ app/top
}
build $build_components
@@ -44,6 +45,10 @@ append config {
+
+
+
+
}
install_config $config
@@ -56,12 +61,13 @@ install_config $config
set boot_modules {
core ld.lib.so init
timer
+ top
test-trace
null
}
build_boot_image $boot_modules
-append qemu_args " -nographic -serial mon:stdio -m 256 "
+append qemu_args " -nographic -serial mon:stdio -m 256 -smp 2 "
run_genode_until {.*child "test-trace" exited with exit value 0.*} 30
diff --git a/repos/os/src/app/top/README b/repos/os/src/app/top/README
new file mode 100644
index 0000000000..a99ad2140a
--- /dev/null
+++ b/repos/os/src/app/top/README
@@ -0,0 +1,13 @@
+This component obtains the information about the existing trace subjects from
+core's "TRACE" service and shows via the LOG session the highest CPU consumer
+per CPU in percentage.
+
+Configuration
+-------------
+
+The interval of output generation can be defined via the 'period_ms' attribute
+of the '' node. The value is specified in milliseconds.
+
+The following example shows the default values.
+
+!
diff --git a/repos/os/src/app/top/main.cc b/repos/os/src/app/top/main.cc
new file mode 100644
index 0000000000..a37e12a791
--- /dev/null
+++ b/repos/os/src/app/top/main.cc
@@ -0,0 +1,281 @@
+/*
+ * \brief Application to show highest CPU consumer per CPU via LOG session
+ * \author Norman Feske
+ * Alexander Boettcher
+ * \date 2015-06-15
+ */
+
+/*
+ * Copyright (C) 2015-2017 Genode Labs GmbH
+ *
+ * This file is part of the Genode OS framework, which is distributed
+ * under the terms of the GNU Affero General Public License version 3.
+ */
+
+/* Genode includes */
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+struct Trace_subject_registry
+{
+ private:
+
+ struct Entry : Genode::List::Element
+ {
+ Genode::Trace::Subject_id const id;
+
+ Genode::Trace::Subject_info info;
+
+ /**
+ * Execution time during the last period
+ */
+ unsigned long long recent_execution_time = 0;
+
+ Entry(Genode::Trace::Subject_id id) : id(id) { }
+
+ void update(Genode::Trace::Subject_info const &new_info)
+ {
+ if (new_info.execution_time().value < info.execution_time().value)
+ recent_execution_time = 0;
+ else
+ recent_execution_time = new_info.execution_time().value -
+ info.execution_time().value;
+
+ info = new_info;
+ }
+ };
+
+ Genode::List _entries;
+
+ Entry *_lookup(Genode::Trace::Subject_id const id)
+ {
+ for (Entry *e = _entries.first(); e; e = e->next())
+ if (e->id == id)
+ return e;
+
+ return nullptr;
+ }
+
+ enum { MAX_SUBJECTS = 512 };
+ Genode::Trace::Subject_id _subjects[MAX_SUBJECTS];
+
+ enum { MAX_CPUS_X = 16, MAX_CPUS_Y = 1, MAX_ELEMENTS_PER_CPU = 6};
+
+ /* accumulated execution time on all CPUs */
+ unsigned long long total [MAX_CPUS_X][MAX_CPUS_Y];
+
+ /* most significant consumer per CPU */
+ Entry const * load[MAX_CPUS_X][MAX_CPUS_Y][MAX_ELEMENTS_PER_CPU];
+
+ public:
+
+ void update(Genode::Trace::Connection &trace, Genode::Allocator &alloc)
+ {
+ unsigned const num_subjects = trace.subjects(_subjects, MAX_SUBJECTS);
+
+ /* add and update existing entries */
+ for (unsigned i = 0; i < num_subjects; i++) {
+
+ Genode::Trace::Subject_id const id = _subjects[i];
+
+ Entry *e = _lookup(id);
+ if (!e) {
+ e = new (alloc) Entry(id);
+ _entries.insert(e);
+ }
+
+ e->update(trace.subject_info(id));
+
+ /* purge dead threads */
+ if (e->info.state() == Genode::Trace::Subject_info::DEAD) {
+ trace.free(e->id);
+ _entries.remove(e);
+ Genode::destroy(alloc, e);
+ }
+ }
+ }
+
+ void top()
+ {
+ /* clear old calculations */
+ Genode::memset(total, 0, sizeof(total));
+ Genode::memset(load, 0, sizeof(load));
+
+ for (Entry const *e = _entries.first(); e; e = e->next()) {
+
+ /* collect highest execution time per CPU */
+ unsigned const x = e->info.affinity().xpos();
+ unsigned const y = e->info.affinity().ypos();
+ if (x >= MAX_CPUS_X || y >= MAX_CPUS_Y) {
+ Genode::error("cpu ", e->info.affinity().xpos(), ".",
+ e->info.affinity().ypos(), " is outside "
+ "supported range ",
+ (int)MAX_CPUS_X, ".", (int)MAX_CPUS_Y);
+ continue;
+ }
+
+ total[x][y] += e->recent_execution_time;
+
+ enum { NONE = ~0U };
+ unsigned replace = NONE;
+
+ for (unsigned i = 0; i < MAX_ELEMENTS_PER_CPU; i++) {
+ if (load[x][y][i])
+ continue;
+
+ replace = i;
+ break;
+ }
+
+ if (replace != NONE) {
+ load[x][y][replace] = e;
+ continue;
+ }
+
+ for (unsigned i = 0; i < MAX_ELEMENTS_PER_CPU; i++) {
+ if (e->recent_execution_time
+ <= load[x][y][i]->recent_execution_time)
+ continue;
+
+ if (replace == NONE) {
+ replace = i;
+ continue;
+ }
+ if (load[x][y][replace]->recent_execution_time
+ <= load[x][y][i]->recent_execution_time)
+ replace = i;
+ }
+
+ if (replace != NONE)
+ load[x][y][replace] = e;
+ }
+
+ /* sort */
+ for (unsigned x = 0; x < MAX_CPUS_X; x++) {
+ for (unsigned y = 0; y < MAX_CPUS_Y; y++) {
+ for (unsigned k = 0; k < MAX_ELEMENTS_PER_CPU;) {
+ if (!load[x][y][k])
+ break;
+
+ unsigned i = k;
+ for (unsigned j = i; j < MAX_ELEMENTS_PER_CPU; j++) {
+ if (!load[x][y][j])
+ break;
+
+ if (load[x][y][i]->recent_execution_time
+ < load[x][y][j]->recent_execution_time) {
+
+ Entry const * tmp = load[x][y][j];
+ load[x][y][j] = load[x][y][i];
+ load[x][y][i] = tmp;
+
+ i++;
+ if (i >= MAX_ELEMENTS_PER_CPU || !load[x][y][i])
+ break;
+ }
+ }
+ if (i == k)
+ k++;
+ }
+ }
+ }
+
+ for (unsigned x = 0; x < MAX_CPUS_X; x++) {
+ for (unsigned y = 0; y < MAX_CPUS_Y; y++) {
+ for (unsigned i = 0; i < MAX_ELEMENTS_PER_CPU; i++) {
+ if (!load[x][y][i] || !total[x][y])
+ continue;
+
+ unsigned percent = load[x][y][i]->recent_execution_time * 100 / total[x][y];
+ unsigned rest = load[x][y][i]->recent_execution_time * 10000 / total[x][y] - (percent * 100);
+
+ using Genode::log;
+ log("cpu=", load[x][y][i]->info.affinity().xpos(), ".",
+ load[x][y][i]->info.affinity().ypos(), " ",
+ percent < 10 ? " " : (percent < 100 ? " " : ""),
+ percent, ".", rest < 10 ? "0" : "", rest, "% "
+ "thread='", load[x][y][i]->info.thread_name(), "' "
+ "label='", load[x][y][i]->info.session_label(), "'");
+ }
+ }
+ }
+ if (load[0][0][0] && load[0][0][0]->recent_execution_time)
+ Genode::log("");
+ }
+};
+
+
+namespace App {
+
+ struct Main;
+ using namespace Genode;
+}
+
+
+struct App::Main
+{
+ Env &_env;
+
+ Trace::Connection _trace { _env, 512*1024, 32*1024, 0 };
+
+ static unsigned long _default_period_ms() { return 5000; }
+
+ unsigned long _period_ms = _default_period_ms();
+
+ Attached_rom_dataspace _config { _env, "config" };
+
+ Timer::Connection _timer { _env };
+
+ Heap _heap { _env.ram(), _env.rm() };
+
+ Trace_subject_registry _trace_subject_registry;
+
+ void _handle_config();
+
+ Signal_handler _config_handler = {
+ _env.ep(), *this, &Main::_handle_config};
+
+ void _handle_period();
+
+ Signal_handler _periodic_handler = {
+ _env.ep(), *this, &Main::_handle_period};
+
+ Main(Env &env) : _env(env)
+ {
+ _config.sigh(_config_handler);
+ _handle_config();
+
+ _timer.sigh(_periodic_handler);
+ }
+};
+
+
+void App::Main::_handle_config()
+{
+ _config.update();
+
+ _period_ms = _config.xml().attribute_value("period_ms", _default_period_ms());
+
+ log("period_ms=", _period_ms);
+
+ _timer.trigger_periodic(1000*_period_ms);
+}
+
+
+void App::Main::_handle_period()
+{
+ /* update subject information */
+ _trace_subject_registry.update(_trace, _heap);
+
+ /* show most significant consumers */
+ _trace_subject_registry.top();
+}
+
+
+void Component::construct(Genode::Env &env) { static App::Main main(env); }
+
diff --git a/repos/os/src/app/top/target.mk b/repos/os/src/app/top/target.mk
new file mode 100644
index 0000000000..105f2b06fa
--- /dev/null
+++ b/repos/os/src/app/top/target.mk
@@ -0,0 +1,3 @@
+TARGET = top
+SRC_CC = main.cc
+LIBS += base