From 7088e4faaa40244f2c82cf2901a945e05fd75a31 Mon Sep 17 00:00:00 2001 From: Norman Feske Date: Thu, 7 Jun 2018 12:23:21 +0200 Subject: [PATCH] os: add init_loop test This test monitors the RAM quota of a dynamic init and a server hosted within the dynamic init in the presence of a repeatedly created and destructed client. --- repos/os/run/init_loop.run | 68 +++++++ repos/os/src/test/init_loop/main.cc | 257 ++++++++++++++++++++++++++ repos/os/src/test/init_loop/target.mk | 3 + tool/autopilot.list | 1 + 4 files changed, 329 insertions(+) create mode 100644 repos/os/run/init_loop.run create mode 100644 repos/os/src/test/init_loop/main.cc create mode 100644 repos/os/src/test/init_loop/target.mk diff --git a/repos/os/run/init_loop.run b/repos/os/run/init_loop.run new file mode 100644 index 000000000..711765561 --- /dev/null +++ b/repos/os/run/init_loop.run @@ -0,0 +1,68 @@ +create_boot_directory + +import_from_depot genodelabs/src/[base_src] \ + genodelabs/src/report_rom + +install_config { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +} + +build { lib/ld init app/dummy test/init_loop } + +build_boot_image { ld.lib.so init dummy test-init_loop } + +append qemu_args " -nographic " + +run_genode_until {.*child "test-init_loop" exited with exit value 0.*} 100 + diff --git a/repos/os/src/test/init_loop/main.cc b/repos/os/src/test/init_loop/main.cc new file mode 100644 index 000000000..2b0788a4c --- /dev/null +++ b/repos/os/src/test/init_loop/main.cc @@ -0,0 +1,257 @@ +/* + * \brief Test for the repeated child creation in a dynamic init + * \author Norman Feske + * \date 2018-06-07 + */ + +/* + * Copyright (C) 2018 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. + */ + +#include +#include +#include +#include +#include +#include + +namespace Test { + struct Main; + using namespace Genode; +} + + +struct Test::Main +{ + Env &_env; + + bool _client_starting = false; + + Expanding_reporter _init_config_reporter { _env, "config", "init.config" }; + + void _gen_log_server_start_content(Xml_generator &xml) const + { + xml.attribute("name", "server"); + xml.attribute("caps", "100"); + xml.node("resource", [&] () { + xml.attribute("name", "RAM"); + xml.attribute("quantum", "1M"); }); + + xml.node("binary", [&] () { + xml.attribute("name", "dummy"); }); + + xml.node("config", [&] () { + xml.node("log_service", [&] () {}); }); + + xml.node("provides", [&] () { + xml.node("service", [&] () { + xml.attribute("name", Log_session::service_name()); }); }); + + xml.node("route", [&] () { + xml.node("any-service", [&] () { + xml.node("parent", [&] () {}); }); }); + } + + void _gen_log_client_start_content(Xml_generator &xml) const + { + xml.attribute("name", "client"); + xml.attribute("caps", "200"); + xml.node("resource", [&] () { + xml.attribute("name", "RAM"); + xml.attribute("quantum", "1M"); }); + + xml.node("binary", [&] () { + xml.attribute("name", "dummy"); }); + + xml.node("config", [&] () { + xml.node("create_log_connections", [&] () { + xml.attribute("count", 1); + xml.attribute("ram_upgrade", "64K"); + }); + xml.node("log", [&] () { + xml.attribute("string", "client started"); }); }); + + xml.node("route", [&] () { + + xml.node("service", [&] () { + xml.attribute("name", Log_session::service_name()); + xml.node("child", [&] () { + xml.attribute("name", "server"); }); + }); + + xml.node("any-service", [&] () { + xml.node("parent", [&] () {}); }); + }); + } + + void _gen_init_config(Xml_generator &xml) const + { + xml.node("report", [&] () { + xml.attribute("requested", "yes"); + xml.attribute("init_ram", "yes"); + xml.attribute("init_caps", "yes"); + xml.attribute("child_ram", "yes"); + xml.attribute("child_caps", "yes"); + xml.attribute("delay_ms", 100); + }); + + auto gen_service = [&] (char const *name) { + xml.node("service", [&] () { xml.attribute("name", name); }); }; + + xml.node("parent-provides", [&] () { + gen_service("ROM"); + gen_service("CPU"); + gen_service("PD"); + gen_service("RM"); + gen_service("LOG"); + }); + + xml.node("start", [&] () { + _gen_log_server_start_content(xml); }); + + if (_client_starting) + xml.node("start", [&] () { + _gen_log_client_start_content(xml); }); + } + + void generate_init_config() + { + _init_config_reporter.generate([&] (Xml_generator &xml) { + _gen_init_config(xml); }); + } + + /* + * Handling of state reports generated by init + */ + Attached_rom_dataspace _init_state { _env, "state" }; + + Signal_handler
_init_state_handler { + _env.ep(), *this, &Main::_handle_init_state }; + + /* counter for iterations */ + unsigned _cnt = 0; + + struct Ram_tracker + { + typedef String<32> Name; + + Name const _name; + + /* RAM quota of previous iteration */ + size_t _previous = 0; + + size_t total_loss = 0; + + void update(Xml_node ram) + { + size_t const current = ram.attribute_value("quota", Number_of_bytes()); + + log(_name, " RAM: ", Number_of_bytes(current)); + if (_previous) { + + if (current < _previous) { + total_loss += _previous - current; + log(_name, " lost ", _previous - current, ", bytes", " " + "(total ", total_loss, " bytes)"); + } + + if (current > _previous) + log(_name, " gained ", current - _previous, ", bytes"); + } + _previous = current; + } + + Ram_tracker(Name const &name) : _name(name) { } + }; + + Ram_tracker _init_ram_tracker { "init" }; + Ram_tracker _server_ram_tracker { "server" }; + + static Number_of_bytes _init_ram(Xml_node state) + { + /* \throw Nonexistent_sub_node */ + return state.sub_node("ram").attribute_value("quota", Number_of_bytes()); + } + + typedef String<32> Name; + + template + void _apply_child(Xml_node state, Name const &name, FN const &fn) + { + state.for_each_sub_node("child", [&] (Xml_node child) { + if (child.attribute_value("name", Name()) == name) + fn(child); }); + } + + void _handle_init_state() + { + _init_state.update(); + + Xml_node const state = _init_state.xml(); + + /* + * Detect state where the client is running and has established a + * session to the LOG server. + */ + bool client_present = false; + bool client_complete = false; + + _apply_child(state, "client", [&] (Xml_node child) { + client_present = true; + child.for_each_sub_node("requested", [&] (Xml_node requested) { + requested.for_each_sub_node("session", [&] (Xml_node session) { + if (session.attribute_value("service", String<16>()) == "LOG" + && session.attribute_value("state", String<16>()) == "CAP_HANDED_OUT") + client_complete = true; }); }); }); + + if (_client_starting) { + + /* kill client as soon as it started up completely */ + if (client_complete) + _client_starting = false; + + } else { + + /* restart client as soon as it vanished */ + if (!client_present) { + _cnt++; + log("iteration ", _cnt); + + if (state.has_sub_node("ram")) + _init_ram_tracker.update(state.sub_node("ram")); + + _apply_child(state, "server", [&] (Xml_node child) { + _server_ram_tracker.update(child.sub_node("ram")); }); + + _client_starting = true; + } + } + + if (_init_ram_tracker.total_loss > 16*1024 + || _server_ram_tracker.total_loss > 16*1024) { + + error("unexpected quota distribution"); + _env.parent().exit(1); + } + + /* success after 50 iterations without any accounting issues */ + if (_cnt == 50) + _env.parent().exit(0); + + generate_init_config(); + } + + Main(Env &env) : _env(env) + { + _init_state.sigh(_init_state_handler); + + generate_init_config(); + } +}; + + +void Component::construct(Genode::Env &env) { static Test::Main main(env); } + diff --git a/repos/os/src/test/init_loop/target.mk b/repos/os/src/test/init_loop/target.mk new file mode 100644 index 000000000..aa6369dbc --- /dev/null +++ b/repos/os/src/test/init_loop/target.mk @@ -0,0 +1,3 @@ +TARGET = test-init_loop +SRC_CC = main.cc +LIBS += base diff --git a/tool/autopilot.list b/tool/autopilot.list index e86af7c73..003929950 100644 --- a/tool/autopilot.list +++ b/tool/autopilot.list @@ -19,6 +19,7 @@ fs_log fs_report gdb_monitor init +init_loop init_smp input_filter ldso