genode/repos/os/src/app/dummy/main.cc

361 lines
7.6 KiB
C++

/*
* \brief Dummy component used for automated component-composition tests
* \author Norman Feske
* \date 2017-02-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 Affero General Public License version 3.
*/
/* Genode includes */
#include <base/heap.h>
#include <base/registry.h>
#include <base/component.h>
#include <base/attached_rom_dataspace.h>
#include <base/sleep.h>
#include <root/component.h>
#include <timer_session/connection.h>
#include <log_session/connection.h>
namespace Dummy {
struct Log_service;
struct Log_connections;
struct Ram_consumer;
struct Cap_consumer;
struct Resource_yield_handler;
struct Main;
using namespace Genode;
}
struct Dummy::Log_service
{
Env &_env;
Sliced_heap _heap { _env.ram(), _env.rm() };
bool const _verbose;
struct Session_component : Rpc_object<Log_session>
{
Session_label const _label;
bool const _verbose;
Session_component(Session_label const &label, bool verbose)
:
_label(label), _verbose(verbose)
{
if (_verbose)
log("opening session with label ", _label);
}
~Session_component()
{
if (_verbose)
log("closing session with label ", _label);
}
size_t write(String const &string) override
{
/* strip known line delimiter from incoming message */
unsigned n = 0;
Genode::String<16> const pattern("\033[0m\n");
for (char const *s = string.string(); s[n] && pattern != s + n; n++);
typedef Genode::String<100> Message;
Message const message("[", _label, "] ", Cstring(string.string(), n));
log(message);
return strlen(string.string());
}
};
struct Root : Root_component<Session_component>
{
Pd_session &_pd;
bool const _verbose;
Root(Entrypoint &ep, Allocator &alloc, Pd_session &pd, bool verbose)
:
Root_component(ep, alloc), _pd(pd), _verbose(verbose)
{ }
Session_component *_create_session(const char *args, Affinity const &) override
{
return new (md_alloc()) Session_component(label_from_args(args), _verbose);
}
void _upgrade_session(Session_component *, const char *args) override
{
size_t const ram_quota =
Arg_string::find_arg(args, "ram_quota").ulong_value(0);
if (_pd.avail_ram().value >= ram_quota)
log("received session quota upgrade");
}
};
Root _root { _env.ep(), _heap, _env.pd(), _verbose };
Log_service(Env &env, bool verbose) : _env(env), _verbose(verbose)
{
_env.parent().announce(_env.ep().manage(_root));
log("created LOG service");
}
~Log_service() { _env.ep().dissolve(_root); }
};
struct Dummy::Log_connections
{
Env &_env;
Sliced_heap _heap { _env.ram(), _env.rm() };
typedef Registered<Log_connection> Connection;
Registry<Connection> _connections { };
Log_connections(Env &env, Xml_node node) : _env(env)
{
unsigned const count = node.attribute_value("count", 0UL);
Number_of_bytes const ram_upgrade =
node.attribute_value("ram_upgrade", Number_of_bytes());
log("going to create ", count, " LOG connections");
for (unsigned i = 0; i < count; i++) {
Connection *connection =
new (_heap) Connection(_connections, _env, Session_label { i });
if (ram_upgrade > 0) {
log("upgrade connection ", i);
connection->upgrade_ram(ram_upgrade);
}
}
log("created all LOG connections");
}
~Log_connections()
{
_connections.for_each([&] (Connection &c) { destroy(_heap, &c); });
log("destroyed all LOG connections");
}
};
struct Dummy::Ram_consumer
{
size_t _amount = 0;
Ram_dataspace_capability _ds_cap { };
Ram_allocator &_ram;
Ram_consumer(Ram_allocator &ram) : _ram(ram) { }
void release()
{
if (!_amount)
return;
log("release ", Number_of_bytes(_amount), " bytes of memory");
_ram.free(_ds_cap);
_ds_cap = Ram_dataspace_capability();
_amount = 0;
}
void consume(size_t amount)
{
if (_amount)
release();
log("consume ", Number_of_bytes(amount), " bytes of memory");
_ds_cap = _ram.alloc(amount);
_amount = amount;
}
};
struct Dummy::Cap_consumer
{
Entrypoint &_ep;
size_t _amount = 0;
struct Interface : Genode::Interface { GENODE_RPC_INTERFACE(); };
struct Object : Genode::Rpc_object<Interface>
{
Entrypoint &_ep;
Object(Entrypoint &ep) : _ep(ep) { _ep.manage(*this); }
~Object() { _ep.dissolve(*this); }
};
/*
* Statically allocate an array of RPC objects to avoid RAM allocations
* as side effect during the cap-consume step.
*/
static constexpr size_t MAX = 100;
Constructible<Object> _objects[MAX];
Cap_consumer(Entrypoint &ep) : _ep(ep) { }
void release()
{
if (!_amount)
return;
log("release ", _amount, " caps");
for (unsigned i = 0; i < MAX; i++)
_objects[i].destruct();
_amount = 0;
}
void consume(size_t amount)
{
if (_amount)
release();
log("consume ", amount, " caps");
for (unsigned i = 0; i < min(amount, MAX); i++)
_objects[i].construct(_ep);
_amount = amount;
}
};
struct Dummy::Resource_yield_handler
{
Env &_env;
Ram_consumer &_ram_consumer;
Cap_consumer &_cap_consumer;
void _handle_yield()
{
log("got yield request");
_ram_consumer.release();
_cap_consumer.release();
_env.parent().yield_response();
}
Signal_handler<Resource_yield_handler> _yield_handler {
_env.ep(), *this, &Resource_yield_handler::_handle_yield };
Resource_yield_handler(Env &env,
Ram_consumer &ram_consumer, Cap_consumer &cap_consumer)
:
_env(env), _ram_consumer(ram_consumer), _cap_consumer(cap_consumer)
{
_env.parent().yield_sigh(_yield_handler);
}
};
struct Dummy::Main
{
Env &_env;
Constructible<Timer::Connection> _timer { };
Attached_rom_dataspace _config { _env, "config" };
unsigned _config_cnt = 0;
typedef String<50> Version;
Version _config_version { };
Signal_handler<Main> _config_handler { _env.ep(), *this, &Main::_handle_config };
Ram_consumer _ram_consumer { _env.ram() };
Cap_consumer _cap_consumer { _env.ep() };
Constructible<Resource_yield_handler> _resource_yield_handler { };
void _handle_config()
{
_config.update();
Version const version = _config.xml().attribute_value("version", Version());
if (_config_cnt > 0 && version == _config_version)
return;
_config_cnt++;
_config_version = version;
if (_config_version.valid())
log("config ", _config_cnt, ": ", _config_version);
_config.xml().for_each_sub_node([&] (Xml_node node) {
if (node.type() == "create_log_connections")
_log_connections.construct(_env, node);
if (node.type() == "destroy_log_connections")
_log_connections.destruct();
if (node.type() == "log_service")
_log_service.construct(_env, node.attribute_value("verbose", false));
if (node.type() == "consume_ram")
_ram_consumer.consume(node.attribute_value("amount", Number_of_bytes()));
if (node.type() == "consume_caps")
_cap_consumer.consume(node.attribute_value("amount", 0UL));
if (node.type() == "handle_yield_requests")
_resource_yield_handler.construct(_env, _ram_consumer, _cap_consumer);
if (node.type() == "sleep") {
if (!_timer.constructed())
_timer.construct(_env);
_timer->msleep(node.attribute_value("ms", (uint64_t)100));
}
if (node.type() == "sleep_forever")
sleep_forever();
if (node.type() == "log")
log(node.attribute_value("string", String<50>()));
if (node.type() == "exit") {
_env.parent().exit(0);
sleep_forever();
}
});
}
Constructible<Log_connections> _log_connections { };
Constructible<Log_service> _log_service { };
Main(Env &env) : _env(env)
{
_config.sigh(_config_handler);
_handle_config();
}
};
void Component::construct(Genode::Env &env) { static Dummy::Main main(env); }