From 451c08ac016f18933fbe87b38c0486bc6d92f55d Mon Sep 17 00:00:00 2001 From: Alexander Boettcher Date: Mon, 6 Mar 2017 10:36:05 +0100 Subject: [PATCH] os: app showing top style CPU utilization via LOG Fixes #2307 --- repos/os/run/trace.run | 8 +- repos/os/src/app/top/README | 13 ++ repos/os/src/app/top/main.cc | 281 +++++++++++++++++++++++++++++++++ repos/os/src/app/top/target.mk | 3 + 4 files changed, 304 insertions(+), 1 deletion(-) create mode 100644 repos/os/src/app/top/README create mode 100644 repos/os/src/app/top/main.cc create mode 100644 repos/os/src/app/top/target.mk 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