/* * \brief Fork bomb to stress Genode * \author Christian Helmuth * \author Norman Feske * \author Alexander Böttcher * \date 2007-08-16 */ /* * Copyright (C) 2007-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. */ #include #include #include #include #include #include #include #include using namespace Genode; class Bomb_child : public Child_policy { private: Env &_env; Binary_name const _binary_name; Name const _label; Cap_quota const _cap_quota; Ram_quota const _ram_quota; Registry > &_parent_services; Child_policy_dynamic_rom_file _config_policy { _env.rm(), "config", _env.ep().rpc_ep(), &_env.ram() }; Child _child { _env.rm(), _env.ep().rpc_ep(), *this }; public: Bomb_child(Env &env, Name const &binary_name, Name const &label, Cap_quota const cap_quota, Ram_quota const ram_quota, Registry > &parent_services, unsigned generation) : _env(env), _binary_name(binary_name), _label(label), _cap_quota(Child::effective_quota(cap_quota)), _ram_quota(Child::effective_quota(ram_quota)), _parent_services(parent_services) { String<64> config(""); _config_policy.load(config.string(), config.length()); } ~Bomb_child() { log(__PRETTY_FUNCTION__); } /**************************** ** Child-policy interface ** ****************************/ Name name() const override { return _label; } Binary_name binary_name() const override { return _binary_name; } void init(Pd_session &pd, Pd_session_capability pd_cap) override { pd.ref_account(_env.pd_session_cap()); _env.pd().transfer_quota(pd_cap, _cap_quota); _env.pd().transfer_quota(pd_cap, _ram_quota); } Pd_session &ref_pd() override { return _env.pd(); } Pd_session_capability ref_pd_cap() const override { return _env.pd_session_cap(); } Service &_matching_service(Service::Name const &service_name, Session_label const &label) { Service *service = nullptr; /* check for config file request */ if ((service = _config_policy.resolve_session_request(service_name, label))) return *service; _parent_services.for_each([&] (Service &s) { if (!service && service_name == s.name()) service = &s; }); if (!service) throw Service_denied(); return *service; } Route resolve_session_request(Service::Name const &service_name, Session_label const &label) override { return Route { .service = _matching_service(service_name, label), .label = label, .diag = Session::Diag() }; } }; typedef Registry > Children; /** * Check if a program with the specified name already exists */ static bool child_name_exists(Children const &children, Bomb_child::Name const &name) { bool found = false; children.for_each([&] (Bomb_child const &child) { if (!found && child.name() == name) found = true; }); return found; } /** * Create a unique name based on the filename * * If a program with the filename as name already exists, we * add a counting number as suffix. */ static Bomb_child::Name unique_child_name(Children const &children, Bomb_child::Name const &binary_name, unsigned const generation) { /* serialize calls to this function */ static Mutex mutex; Mutex::Guard guard(mutex); for (unsigned cnt = 1; ; cnt++) { /* if such a program name does not exist yet, we are happy */ Bomb_child::Name const unique(binary_name, "_g", generation, ".", cnt); if (!child_name_exists(children, unique.string())) return unique; } } struct Bomb { Genode::Env &env; Constructible timer { }; Genode::Signal_handler signal_timeout { env.ep(), *this, &Bomb::destruct_children }; Genode::Signal_handler signal_resource { env.ep(), *this, &Bomb::resource_request }; Attached_rom_dataspace config { env, "config" }; unsigned round = 0; unsigned const rounds = config.xml().attribute_value("rounds", 1U); unsigned const generation = config.xml().attribute_value("generations", 1U); unsigned const children = config.xml().attribute_value("children", 2U); uint64_t const sleeptime = config.xml().attribute_value("sleep", (uint64_t)2000); size_t const ram_demand = config.xml().attribute_value("demand", 1024UL * 1024); bool const master = config.xml().attribute_value("master", true); Heap heap { env.ram(), env.rm() }; Children child_registry { }; Static_parent_services parent_services { env }; void construct_children() { size_t const preserved_ram = Pd_connection::RAM_QUOTA; size_t const avail_ram = env.pd().avail_ram().value; if (avail_ram < preserved_ram + ram_demand) { error("RAM demand exceeds available RAM"); return; } Ram_quota const ram_amount { (avail_ram - preserved_ram - ram_demand) / children }; if (ram_amount.value < (ram_demand * children)) { log("I'm a leaf node - generation ", generation, " - not enough memory."); return; } size_t const avail_caps = env.pd().avail_caps().value; size_t const preserved_caps = children*30; if (avail_caps < preserved_caps) { log("I ran out of capabilities."); return; } Cap_quota const cap_quota { (avail_caps - preserved_caps) / children }; if (generation == 0) { log("I'm a leaf node - generation 0"); return; } log("[", round, "] It's time to start all my children..."); Bomb_child::Name const binary_name("bomb"); for (unsigned i = children; i; --i) { try { new (heap) Registered(child_registry, env, binary_name, unique_child_name(child_registry, binary_name, generation - 1), cap_quota, ram_amount, parent_services, generation - 1); } catch (...) { Genode::error("creation of child ", i, " failed"); } } /* master if we have a timer connection */ if (master) timer->trigger_once(sleeptime * 1000); } void destruct_children() { log("[", round, "] It's time to kill all my children..."); child_registry.for_each([&] (Registered &child) { destroy(heap, &child); }); log("[", round, "] Done."); ++round; /* master if we have a timer connection */ if (round == rounds && master) { log("Done. Going to sleep"); return; } construct_children(); } void resource_request() { Genode::error("resource request"); } Bomb(Genode::Env &env) : env(env) { /* * Don't ask parent for further resources if we ran out of memory. * Prevent us to block for resource upgrades caused by clients */ env.parent().resource_avail_sigh(signal_resource); log("--- bomb started ---"); if (master) { timer.construct(env); timer->sigh(signal_timeout); log("rounds=", rounds, " generations=", generation, " children=", children, " sleep=", sleeptime, " demand=", ram_demand/1024, "K"); } construct_children(); } }; void Component::construct(Genode::Env &env) { static Bomb bomb(env); }