/* * \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 enum SORT_TIME { EC_TIME = 0, SC_TIME = 1}; 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 */ Genode::uint64_t recent_time[2] = { 0, 0 }; Entry(Genode::Trace::Subject_id id) : id(id) { } void update(Genode::Trace::Subject_info const &new_info) { if (new_info.execution_time().thread_context < info.execution_time().thread_context) recent_time[EC_TIME] = 0; else recent_time[EC_TIME] = new_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; } }; 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 = 1024 }; Genode::Trace::Subject_id _subjects[MAX_SUBJECTS]; enum { MAX_CPUS_X = 16, MAX_CPUS_Y = 4, MAX_ELEMENTS_PER_CPU = 6}; /* accumulated execution time on all CPUs */ 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 */ Entry const * load[MAX_CPUS_X][MAX_CPUS_Y][MAX_ELEMENTS_PER_CPU]; bool _reconstruct_trace_connection = false; template unsigned update_subjects(Genode::Pd_session &pd, Genode::Trace::Connection &trace, FN const &fn) { Genode::Ram_quota ram_quota; do { try { return trace.for_each_subject_info(fn); } catch (Genode::Out_of_ram) { trace.upgrade_ram(4096); } ram_quota = pd.avail_ram(); _reconstruct_trace_connection = (ram_quota.value < 4 * 4096); } while (ram_quota.value >= 2 * 4096); return 0; } public: void update(Genode::Pd_session &pd, Genode::Trace::Connection &trace, Genode::Allocator &alloc) { update_subjects(pd, trace, [&](Genode::Trace::Subject_id const &id, Genode::Trace::Subject_info const &info) { Entry *e = _lookup(id); if (!e) { e = new (alloc) Entry(id); _entries.insert(e); } e->update(info); /* remove dead threads which did not run in the last period */ if (e->info.state() == Genode::Trace::Subject_info::DEAD && !e->recent_time[EC_TIME] && !e->recent_time[SC_TIME]) { trace.free(e->id); _entries.remove(e); Genode::destroy(alloc, e); } }); if (_reconstruct_trace_connection) throw Genode::Out_of_ram(); } void flush(Genode::Trace::Connection &trace, Genode::Allocator &alloc) { _reconstruct_trace_connection = false; while (Entry * const e = _entries.first()) { trace.free(e->id); _entries.remove(e); Genode::destroy(alloc, e); } } void top(enum SORT_TIME sorting) { /* clear old calculations */ Genode::memset(total_first, 0, sizeof(total_first)); Genode::memset(total_second, 0, sizeof(total_second)); 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()) { /* 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_first[x][y] += e->recent_time[first]; total_second[x][y] += e->recent_time[second]; 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_time[first] <= load[x][y][i]->recent_time[first]) continue; if (replace == NONE) { replace = i; continue; } if (load[x][y][replace]->recent_time[first] > load[x][y][i]->recent_time[first]) 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_time[first] < load[x][y][j]->recent_time[first]) { 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_first[x][y]) continue; Entry const &entry = *load[x][y][i]; unsigned ec_percent = entry.recent_time[first] * 100 / total_first[x][y]; unsigned ec_rest = entry.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 = entry.recent_time[second] * 100 / total_second[x][y]; sc_rest = entry.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 = entry.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 space_string(space); using Genode::log; log("cpu=", entry.info.affinity().xpos(), ".", entry.info.affinity().ypos(), " ", _align_right<4>(entry.info.execution_time().priority), " ", _align_right<6>(entry.info.execution_time().quantum), " ", _align_right<4>(ec_percent), ".", _align_right<3>(ec_rest, true), "%" " ", _align_right<4>(sc_percent), ".", _align_right<3>(sc_rest, true), "% " "thread='", entry.info.thread_name(), "' ", space_string, "label='", entry.info.session_label(), "'"); } } } if (load[0][0][0] && load[0][0][0]->recent_time[first]) Genode::log(""); } template Genode::String _align_right(Genode::uint64_t value, bool zero = false) { Genode::String result(value); for (Genode::uint64_t i = 1, pow = 10; i < (T - 1); i++, pow *= 10) { if (value < pow) result = Genode::String(zero ? "0" : " ", result); } return result; } }; namespace App { struct Main; using namespace Genode; } struct App::Main { Env &_env; enum { TRACE_RAM_QUOTA = 10 * 4096, ARG_BUFFER_RAM = 32 * 1024, PARENT_LEVELS = 0 }; Reconstructible _trace { _env, TRACE_RAM_QUOTA, ARG_BUFFER_RAM, PARENT_LEVELS }; static uint64_t _default_period_ms() { return 5000; } uint64_t _period_ms = _default_period_ms(); SORT_TIME _sort { EC_TIME }; 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()); 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); } void App::Main::_handle_period() { bool reconstruct = false; /* update subject information */ try { _trace_subject_registry.update(_env.pd(), *_trace, _heap); } catch (Genode::Out_of_ram) { reconstruct = true; } /* show most significant consumers */ _trace_subject_registry.top(_sort); /* by destructing the session we free up the allocated memory in core */ if (reconstruct) { Genode::warning("re-construct trace session because of out of memory"); _trace_subject_registry.flush(*_trace, _heap); _trace.destruct(); _trace.construct(_env, TRACE_RAM_QUOTA, ARG_BUFFER_RAM, PARENT_LEVELS); } } void Component::construct(Genode::Env &env) { static App::Main main(env); }