Trace-subject reporter

Issue #813
This commit is contained in:
Norman Feske 2015-06-15 20:41:59 +02:00 committed by Christian Helmuth
parent 959572968d
commit b22f3c67f0
3 changed files with 268 additions and 0 deletions

View File

@ -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 '<config>' node. The value is specified in milliseconds.
The level of detail of the generated reports can be influenced by the
'<report>' sub node of the configuration. The following example shows the
default values.
! <config period_ms="5000" >
! <report activity="no" affinity="no"/>
! </config>
When setting 'activity' to "yes", the report contains an '<activity>' 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 '<affinity>' sub node
for each subject. The sub node shows the thread's physical CPU affinity,
expressed via the 'xpos' and 'ypos' attributes.

View File

@ -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 <trace_session/connection.h>
#include <timer_session/connection.h>
#include <os/reporter.h>
#include <os/server.h>
#include <os/config.h>
#include <base/env.h>
namespace Server { struct Main; }
struct Trace_subject_registry
{
private:
struct Entry : Genode::List<Entry>::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<Entry> _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<Entry> 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<Main> config_dispatcher = {
ep, *this, &Main::handle_config};
void handle_period(unsigned);
Signal_rpc_member<Main> 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);
}
}

View File

@ -0,0 +1,3 @@
TARGET = trace_subject_reporter
SRC_CC = main.cc
LIBS += base server config