mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-22 15:02:25 +00:00
init: enable init to report its internal state
This patch equips init with the ability to report its internal state in the form of a "state" report. This feature can be enabled by placing a '<report>' node in init's configuration. The report node accepts the following arguments (with their default values): 'delay_ms="100"': specifies the number of milliseconds to wait before producing a new report. This way, many consecutive state changes - like they occur during the startup - do not result in an overly large number of reports but are merged into one final report. 'buffer="4K"': the maximum size of the report in bytes. The attribute accepts the use of K/M/G as units. 'init_ram="no"': if enabled, the report will contain a '<ram>' node with the memory stats of init. 'ids="no"': supplement the children in the report with unique IDs, which may be used to infer the lifetime of children accross configuration updates in the future; 'requested="no"': if enabled, the report will contain information about all session requests initiated by the children. 'provided="no"': if enabled, the report will contain information about all sessions provided by all servers. 'session_args="no"': level of detail of the session information generated via 'requested' or 'provided'. 'child_ram="no"': if enabled, the report will contain a '<ram>' node for each child based on the information obtained from the child's RAM session. Issue #2246
This commit is contained in:
parent
9d683a56a0
commit
84fddafda7
@ -24,6 +24,7 @@
|
|||||||
#include <init/verbose.h>
|
#include <init/verbose.h>
|
||||||
#include <init/child_config.h>
|
#include <init/child_config.h>
|
||||||
#include <init/child_policy.h>
|
#include <init/child_policy.h>
|
||||||
|
#include <init/report.h>
|
||||||
|
|
||||||
namespace Init {
|
namespace Init {
|
||||||
|
|
||||||
@ -234,6 +235,21 @@ namespace Init {
|
|||||||
service = &s; });
|
service = &s; });
|
||||||
return service;
|
return service;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline void generate_ram_info(Xml_generator &xml, Ram_session const &ram)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* The const cast is needed because the 'Ram_session' accessors are
|
||||||
|
* non-const methods.
|
||||||
|
*/
|
||||||
|
Ram_session &ram_nonconst = const_cast<Ram_session &>(ram);
|
||||||
|
|
||||||
|
typedef String<32> Value;
|
||||||
|
xml.attribute("quota", Value(Number_of_bytes(ram_nonconst.quota())));
|
||||||
|
xml.attribute("used", Value(Number_of_bytes(ram_nonconst.used())));
|
||||||
|
xml.attribute("avail", Value(Number_of_bytes(ram_nonconst.avail())));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -312,6 +328,11 @@ class Init::Child : Child_policy, Child_service::Wakeup
|
|||||||
*/
|
*/
|
||||||
class Child_name_is_not_unique { };
|
class Child_name_is_not_unique { };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unique ID of the child, solely used for diagnostic purposes
|
||||||
|
*/
|
||||||
|
struct Id { unsigned value; };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
friend class Child_registry;
|
friend class Child_registry;
|
||||||
@ -320,6 +341,10 @@ class Init::Child : Child_policy, Child_service::Wakeup
|
|||||||
|
|
||||||
Verbose const &_verbose;
|
Verbose const &_verbose;
|
||||||
|
|
||||||
|
Id const _id;
|
||||||
|
|
||||||
|
Report_update_trigger &_report_update_trigger;
|
||||||
|
|
||||||
List_element<Child> _list_element;
|
List_element<Child> _list_element;
|
||||||
|
|
||||||
Xml_node _start_node;
|
Xml_node _start_node;
|
||||||
@ -488,6 +513,8 @@ class Init::Child : Child_policy, Child_service::Wakeup
|
|||||||
*/
|
*/
|
||||||
Child(Env &env,
|
Child(Env &env,
|
||||||
Verbose const &verbose,
|
Verbose const &verbose,
|
||||||
|
Id id,
|
||||||
|
Report_update_trigger &report_update_trigger,
|
||||||
Xml_node start_node,
|
Xml_node start_node,
|
||||||
Xml_node default_route_node,
|
Xml_node default_route_node,
|
||||||
Name_registry &name_registry,
|
Name_registry &name_registry,
|
||||||
@ -496,7 +523,8 @@ class Init::Child : Child_policy, Child_service::Wakeup
|
|||||||
Registry<Parent_service> &parent_services,
|
Registry<Parent_service> &parent_services,
|
||||||
Registry<Routed_service> &child_services)
|
Registry<Routed_service> &child_services)
|
||||||
:
|
:
|
||||||
_env(env), _verbose(verbose),
|
_env(env), _verbose(verbose), _id(id),
|
||||||
|
_report_update_trigger(report_update_trigger),
|
||||||
_list_element(this),
|
_list_element(this),
|
||||||
_start_node(start_node),
|
_start_node(start_node),
|
||||||
_default_route_node(default_route_node),
|
_default_route_node(default_route_node),
|
||||||
@ -564,6 +592,51 @@ class Init::Child : Child_policy, Child_service::Wakeup
|
|||||||
*/
|
*/
|
||||||
bool has_name(Child_policy::Name const &str) const { return str == name(); }
|
bool has_name(Child_policy::Name const &str) const { return str == name(); }
|
||||||
|
|
||||||
|
void report_state(Xml_generator &xml, Report_detail const &detail) const
|
||||||
|
{
|
||||||
|
xml.node("child", [&] () {
|
||||||
|
|
||||||
|
xml.attribute("name", _name.unique);
|
||||||
|
xml.attribute("binary", _name.file);
|
||||||
|
|
||||||
|
if (detail.ids())
|
||||||
|
xml.attribute("id", _id.value);
|
||||||
|
|
||||||
|
if (detail.child_ram() && _child.ram_session_cap().valid()) {
|
||||||
|
xml.node("ram", [&] () {
|
||||||
|
/*
|
||||||
|
* The const cast is needed because there is no const
|
||||||
|
* accessor for the RAM session of the child.
|
||||||
|
*/
|
||||||
|
auto &nonconst_child = const_cast<Genode::Child &>(_child);
|
||||||
|
generate_ram_info(xml, nonconst_child.ram());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Session_state::Detail const
|
||||||
|
session_detail { detail.session_args() ? Session_state::Detail::ARGS
|
||||||
|
: Session_state::Detail::NO_ARGS};
|
||||||
|
|
||||||
|
if (detail.requested()) {
|
||||||
|
xml.node("requested", [&] () {
|
||||||
|
_child.for_each_session([&] (Session_state const &session) {
|
||||||
|
xml.node("session", [&] () {
|
||||||
|
session.generate_client_side_info(xml, session_detail); }); }); });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (detail.provided()) {
|
||||||
|
xml.node("provided", [&] () {
|
||||||
|
|
||||||
|
auto fn = [&] (Session_state const &session) {
|
||||||
|
xml.node("session", [&] () {
|
||||||
|
session.generate_server_side_info(xml, session_detail); }); };
|
||||||
|
|
||||||
|
server_id_space().for_each<Session_state const>(fn);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/****************************
|
/****************************
|
||||||
** Child-policy interface **
|
** Child-policy interface **
|
||||||
@ -792,6 +865,11 @@ class Init::Child : Child_policy, Child_service::Wakeup
|
|||||||
*/
|
*/
|
||||||
Child_policy::exit(exit_value);
|
Child_policy::exit(exit_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void session_state_changed() override
|
||||||
|
{
|
||||||
|
_report_update_trigger.trigger_report_update();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* _INCLUDE__INIT__CHILD_H_ */
|
#endif /* _INCLUDE__INIT__CHILD_H_ */
|
||||||
|
68
repos/os/include/init/report.h
Normal file
68
repos/os/include/init/report.h
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* \brief Report configuration
|
||||||
|
* \author Norman Feske
|
||||||
|
* \date 2017-01-16
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _INCLUDE__INIT__REPORT_H_
|
||||||
|
#define _INCLUDE__INIT__REPORT_H_
|
||||||
|
|
||||||
|
#include <util/noncopyable.h>
|
||||||
|
#include <util/xml_node.h>
|
||||||
|
|
||||||
|
namespace Init {
|
||||||
|
struct Report_update_trigger;
|
||||||
|
struct Report_detail;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class Init::Report_detail : Genode::Noncopyable
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
bool _children = false;
|
||||||
|
bool _ids = false;
|
||||||
|
bool _requested = false;
|
||||||
|
bool _provided = false;
|
||||||
|
bool _session_args = false;
|
||||||
|
bool _child_ram = false;
|
||||||
|
bool _init_ram = false;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Report_detail() { }
|
||||||
|
|
||||||
|
Report_detail(Genode::Xml_node report)
|
||||||
|
{
|
||||||
|
_children = true;
|
||||||
|
_ids = report.attribute_value("ids", false);
|
||||||
|
_requested = report.attribute_value("requested", false);
|
||||||
|
_provided = report.attribute_value("provided", false);
|
||||||
|
_session_args = report.attribute_value("session_args", false);
|
||||||
|
_child_ram = report.attribute_value("child_ram", false);
|
||||||
|
_init_ram = report.attribute_value("init_ram", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool children() const { return _children; }
|
||||||
|
bool ids() const { return _ids; }
|
||||||
|
bool requested() const { return _requested; }
|
||||||
|
bool provided() const { return _provided; }
|
||||||
|
bool session_args() const { return _session_args; }
|
||||||
|
bool child_ram() const { return _child_ram; }
|
||||||
|
bool init_ram() const { return _init_ram; }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct Init::Report_update_trigger
|
||||||
|
{
|
||||||
|
virtual void trigger_report_update() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _INCLUDE__INIT__REPORT_H_ */
|
@ -11,8 +11,13 @@
|
|||||||
* under the terms of the GNU General Public License version 2.
|
* under the terms of the GNU General Public License version 2.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* Genode includes */
|
||||||
#include <base/component.h>
|
#include <base/component.h>
|
||||||
#include <base/attached_rom_dataspace.h>
|
#include <base/attached_rom_dataspace.h>
|
||||||
|
#include <os/reporter.h>
|
||||||
|
#include <timer_session/connection.h>
|
||||||
|
|
||||||
|
/* init includes */
|
||||||
#include <init/child.h>
|
#include <init/child.h>
|
||||||
|
|
||||||
namespace Init {
|
namespace Init {
|
||||||
@ -208,6 +213,22 @@ class Init::Child_registry : public Name_registry, Child_list
|
|||||||
return _aliases.first() ? _aliases.first() : 0;
|
return _aliases.first() ? _aliases.first() : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void report_state(Xml_generator &xml, Report_detail const &detail) const
|
||||||
|
{
|
||||||
|
Genode::List_element<Child> const *curr = first();
|
||||||
|
for (; curr; curr = curr->next())
|
||||||
|
curr->object()->report_state(xml, detail);
|
||||||
|
|
||||||
|
/* check for name clash with an existing alias */
|
||||||
|
for (Alias const *a = _aliases.first(); a; a = a->next()) {
|
||||||
|
xml.node("alias", [&] () {
|
||||||
|
xml.attribute("name", a->name);
|
||||||
|
xml.attribute("child", a->child);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*****************************
|
/*****************************
|
||||||
** Name-registry interface **
|
** Name-registry interface **
|
||||||
@ -241,10 +262,129 @@ class Init::Child_registry : public Name_registry, Child_list
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
namespace Init { struct Main; }
|
namespace Init {
|
||||||
|
struct State_reporter;
|
||||||
|
struct Main;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
struct Init::Main
|
class Init::State_reporter : public Report_update_trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
struct Producer
|
||||||
|
{
|
||||||
|
virtual void produce_state_report(Xml_generator &xml,
|
||||||
|
Report_detail const &) const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
Env &_env;
|
||||||
|
|
||||||
|
Producer &_producer;
|
||||||
|
|
||||||
|
Constructible<Reporter> _reporter;
|
||||||
|
|
||||||
|
size_t _buffer_size = 0;
|
||||||
|
|
||||||
|
Reconstructible<Report_detail> _report_detail;
|
||||||
|
|
||||||
|
unsigned _report_delay_ms = 0;
|
||||||
|
|
||||||
|
/* version string from config, to be reflected in the report */
|
||||||
|
typedef String<64> Version;
|
||||||
|
Version _version;
|
||||||
|
|
||||||
|
Constructible<Timer::Connection> _timer;
|
||||||
|
|
||||||
|
Signal_handler<State_reporter> _timer_handler {
|
||||||
|
_env.ep(), *this, &State_reporter::_handle_timer };
|
||||||
|
|
||||||
|
bool _scheduled = false;
|
||||||
|
|
||||||
|
void _handle_timer()
|
||||||
|
{
|
||||||
|
_scheduled = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
Reporter::Xml_generator xml(*_reporter, [&] () {
|
||||||
|
|
||||||
|
if (_version.valid())
|
||||||
|
xml.attribute("version", _version);
|
||||||
|
|
||||||
|
_producer.produce_state_report(xml, *_report_detail);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch(Xml_generator::Buffer_exceeded) {
|
||||||
|
|
||||||
|
error("state report exceeds maximum size");
|
||||||
|
|
||||||
|
/* try to reflect the error condition as state report */
|
||||||
|
try {
|
||||||
|
Reporter::Xml_generator xml(*_reporter, [&] () {
|
||||||
|
xml.attribute("error", "report buffer exceeded"); });
|
||||||
|
}
|
||||||
|
catch (...) { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
State_reporter(Env &env, Producer &producer)
|
||||||
|
:
|
||||||
|
_env(env), _producer(producer)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
void apply_config(Xml_node config)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
Xml_node report = config.sub_node("report");
|
||||||
|
|
||||||
|
/* (re-)construct reporter whenever the buffer size is changed */
|
||||||
|
Number_of_bytes const buffer_size =
|
||||||
|
report.attribute_value("buffer", Number_of_bytes(4096));
|
||||||
|
|
||||||
|
if (buffer_size != _buffer_size || !_reporter.constructed()) {
|
||||||
|
_buffer_size = buffer_size;
|
||||||
|
_reporter.construct(_env, "state", "state", _buffer_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
_report_detail.construct(report);
|
||||||
|
_report_delay_ms = report.attribute_value("delay_ms", 100UL);
|
||||||
|
_reporter->enabled(true);
|
||||||
|
}
|
||||||
|
catch (Xml_node::Nonexistent_sub_node) {
|
||||||
|
_report_detail.construct();
|
||||||
|
_report_delay_ms = 0;
|
||||||
|
if (_reporter.constructed())
|
||||||
|
_reporter->enabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
_version = config.attribute_value("version", Version());
|
||||||
|
|
||||||
|
if (_report_delay_ms) {
|
||||||
|
|
||||||
|
if (!_timer.constructed()) {
|
||||||
|
_timer.construct(_env);
|
||||||
|
_timer->sigh(_timer_handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
trigger_report_update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void trigger_report_update() override
|
||||||
|
{
|
||||||
|
if (!_scheduled && _timer.constructed() && _report_delay_ms) {
|
||||||
|
_timer->trigger_once(_report_delay_ms*1000);
|
||||||
|
_scheduled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct Init::Main : State_reporter::Producer
|
||||||
{
|
{
|
||||||
Env &_env;
|
Env &_env;
|
||||||
|
|
||||||
@ -258,8 +398,21 @@ struct Init::Main
|
|||||||
|
|
||||||
Reconstructible<Verbose> _verbose { _config.xml() };
|
Reconstructible<Verbose> _verbose { _config.xml() };
|
||||||
|
|
||||||
|
unsigned _child_cnt = 0;
|
||||||
|
|
||||||
void _handle_resource_avail() { }
|
void _handle_resource_avail() { }
|
||||||
|
|
||||||
|
void produce_state_report(Xml_generator &xml, Report_detail const &detail) const
|
||||||
|
{
|
||||||
|
if (detail.init_ram())
|
||||||
|
xml.node("ram", [&] () { generate_ram_info(xml, _env.ram()); });
|
||||||
|
|
||||||
|
if (detail.children())
|
||||||
|
_children.report_state(xml, detail);
|
||||||
|
}
|
||||||
|
|
||||||
|
State_reporter _state_reporter { _env, *this };
|
||||||
|
|
||||||
Signal_handler<Main> _resource_avail_handler {
|
Signal_handler<Main> _resource_avail_handler {
|
||||||
_env.ep(), *this, &Main::_handle_resource_avail };
|
_env.ep(), *this, &Main::_handle_resource_avail };
|
||||||
|
|
||||||
@ -300,12 +453,10 @@ void Init::Main::_handle_config()
|
|||||||
_parent_services.for_each([&] (Init::Parent_service &service) {
|
_parent_services.for_each([&] (Init::Parent_service &service) {
|
||||||
destroy(_heap, &service); });
|
destroy(_heap, &service); });
|
||||||
|
|
||||||
/* reload config */
|
|
||||||
try { config()->reload(); } catch (...) { }
|
|
||||||
|
|
||||||
_config.update();
|
_config.update();
|
||||||
|
|
||||||
_verbose.construct(_config.xml());
|
_verbose.construct(_config.xml());
|
||||||
|
_state_reporter.apply_config(_config.xml());
|
||||||
|
|
||||||
try { determine_parent_services(_parent_services, _config.xml(),
|
try { determine_parent_services(_parent_services, _config.xml(),
|
||||||
_heap, _verbose->enabled()); }
|
_heap, _verbose->enabled()); }
|
||||||
@ -335,7 +486,10 @@ void Init::Main::_handle_config()
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
_children.insert(new (_heap)
|
_children.insert(new (_heap)
|
||||||
Init::Child(_env, *_verbose, start_node, default_route_node,
|
Init::Child(_env, *_verbose,
|
||||||
|
Init::Child::Id { ++_child_cnt },
|
||||||
|
_state_reporter,
|
||||||
|
start_node, default_route_node,
|
||||||
_children, read_prio_levels(_config.xml()),
|
_children, read_prio_levels(_config.xml()),
|
||||||
read_affinity_space(_config.xml()),
|
read_affinity_space(_config.xml()),
|
||||||
_parent_services, _child_services));
|
_parent_services, _child_services));
|
||||||
|
Loading…
Reference in New Issue
Block a user