From b22f3c67f00b7620eb88968ad3550e2918ddcf25 Mon Sep 17 00:00:00 2001 From: Norman Feske Date: Mon, 15 Jun 2015 20:41:59 +0200 Subject: [PATCH] Trace-subject reporter Issue #813 --- .../os/src/app/trace_subject_reporter/README | 26 ++ .../os/src/app/trace_subject_reporter/main.cc | 239 ++++++++++++++++++ .../src/app/trace_subject_reporter/target.mk | 3 + 3 files changed, 268 insertions(+) create mode 100644 repos/os/src/app/trace_subject_reporter/README create mode 100644 repos/os/src/app/trace_subject_reporter/main.cc create mode 100644 repos/os/src/app/trace_subject_reporter/target.mk diff --git a/repos/os/src/app/trace_subject_reporter/README b/repos/os/src/app/trace_subject_reporter/README new file mode 100644 index 0000000000..0044e11a74 --- /dev/null +++ b/repos/os/src/app/trace_subject_reporter/README @@ -0,0 +1,26 @@ +This component obtains the information about the existing trace subjects from +core's "TRACE" service and delivers a report to a "Report" server. The report, +in turn, can be used by top-like applications to present the consumed execution +time per thread. + +Configuration +------------- + +The interval of report generation can be defined via the 'period_ms' attribute +of the '' node. The value is specified in milliseconds. + +The level of detail of the generated reports can be influenced by the +'' sub node of the configuration. The following example shows the +default values. + +! +! +! + +When setting 'activity' to "yes", the report contains an '' sub node +for each subject. The sub node has the two attributes 'total' and 'recent'. The +'recent' value represents the execution time consumed in the last period. + +When setting 'affinity' to "yes", the report contains an '' sub node +for each subject. The sub node shows the thread's physical CPU affinity, +expressed via the 'xpos' and 'ypos' attributes. diff --git a/repos/os/src/app/trace_subject_reporter/main.cc b/repos/os/src/app/trace_subject_reporter/main.cc new file mode 100644 index 0000000000..17c407b53d --- /dev/null +++ b/repos/os/src/app/trace_subject_reporter/main.cc @@ -0,0 +1,239 @@ +/* + * \brief Report information about present trace subjects + * \author Norman Feske + * \date 2015-06-15 + */ + +/* + * Copyright (C) 2015 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include +#include +#include +#include + + +namespace Server { struct Main; } + + +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) + { + unsigned long long const last_execution_time = info.execution_time().value; + info = new_info; + recent_execution_time = info.execution_time().value - last_execution_time; + } + }; + + 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]; + + void _sort_by_recent_execution_time() + { + Genode::List sorted; + + while (_entries.first()) { + + /* find entry with lowest recent execution time */ + Entry *lowest = _entries.first(); + for (Entry *e = _entries.first(); e; e = e->next()) { + if (e->recent_execution_time < lowest->recent_execution_time) + lowest = e; + } + + _entries.remove(lowest); + sorted.insert(lowest); + } + + _entries = sorted; + } + + + 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); + } + } + + _sort_by_recent_execution_time(); + } + + void report(Genode::Xml_generator &xml, + bool report_affinity, bool report_activity) + { + for (Entry const *e = _entries.first(); e; e = e->next()) { + xml.node("subject", [&] () { + xml.attribute("label", e->info.session_label().string()); + xml.attribute("thread", e->info.thread_name().string()); + xml.attribute("id", e->id.id); + + typedef Genode::Trace::Subject_info Subject_info; + Subject_info::State const state = e->info.state(); + xml.attribute("state", Subject_info::state_name(state)); + + if (report_activity) + xml.node("activity", [&] () { + xml.attribute("total", e->info.execution_time().value); + xml.attribute("recent", e->recent_execution_time); + }); + + if (report_affinity) + xml.node("affinity", [&] () { + xml.attribute("xpos", e->info.affinity().xpos()); + xml.attribute("ypos", e->info.affinity().ypos()); + }); + }); + } + } +}; + + +struct Server::Main +{ + Entrypoint &ep; + + Genode::Trace::Connection trace { 256*1024, 32*1024, 0 }; + + Genode::Reporter reporter { "trace_subjects", 64*1024 }; + + static unsigned long default_period_ms() { return 5000; } + + unsigned long period_ms = default_period_ms(); + + bool report_affinity = false; + bool report_activity = false; + + bool config_report_attribute_enabled(char const *attr) const + { + try { + return Genode::config()->xml_node().sub_node("report") + .attribute(attr).has_value("yes"); + } catch (...) { return false; } + } + + Timer::Connection timer; + + Trace_subject_registry trace_subject_registry; + + void handle_config(unsigned); + + Signal_rpc_member
config_dispatcher = { + ep, *this, &Main::handle_config}; + + void handle_period(unsigned); + + Signal_rpc_member
periodic_dispatcher = { + ep, *this, &Main::handle_period}; + + Main(Entrypoint &ep) : ep(ep) + { + Genode::config()->sigh(config_dispatcher); + handle_config(0); + + timer.sigh(periodic_dispatcher); + + reporter.enabled(true); + } +}; + + +void Server::Main::handle_config(unsigned) +{ + Genode::config()->reload(); + + try { + period_ms = default_period_ms(); + Genode::config()->xml_node().attribute("period_ms").value(&period_ms); + } catch (...) { } + + report_affinity = config_report_attribute_enabled("affinity"); + report_activity = config_report_attribute_enabled("activity"); + + PINF("period_ms=%ld, report_activity=%d, report_affinity=%d", + period_ms, report_activity, report_affinity); + + timer.trigger_periodic(1000*period_ms); +} + + +void Server::Main::handle_period(unsigned) +{ + /* update subject information */ + trace_subject_registry.update(trace, *Genode::env()->heap()); + + /* generate report */ + reporter.clear(); + Genode::Reporter::Xml_generator xml(reporter, [&] () + { + trace_subject_registry.report(xml, report_affinity, report_activity); + }); +} + + +namespace Server { + + char const *name() { return "trace_subject_reporter"; } + + size_t stack_size() { return 4*1024*sizeof(long); } + + void construct(Entrypoint &ep) + { + static Main main(ep); + } +} + diff --git a/repos/os/src/app/trace_subject_reporter/target.mk b/repos/os/src/app/trace_subject_reporter/target.mk new file mode 100644 index 0000000000..01d7ca3ded --- /dev/null +++ b/repos/os/src/app/trace_subject_reporter/target.mk @@ -0,0 +1,3 @@ +TARGET = trace_subject_reporter +SRC_CC = main.cc +LIBS += base server config