mirror of
https://github.com/genodelabs/genode.git
synced 2025-01-19 03:06:39 +00:00
cli_monitor: Resource-balancing support
This patch introduces new commands for dynamically balancing RAM between subsystems. The 'status' command prints a table with the RAM status of each subsystem. The 'ram' command changes the quota or a quota limit of a given subsystem. The quota limit can be defined to allow the on-demand expansion of the quota. Finally, the 'yield' command can be used to instruct a subsystem to yield a specified amount of resources. For trying out the new commands, a so-called 'ram_eater' example has been added to the 'terminal_mux.run' scenario. This program simulates a subsystem with a growing demand for resources, yet with the capability to yield resources when instructed by the parent (i.e., cli_monitor). Besides implementing the new features, the patch splits the implementation of 'cli_monitor' into multiple files.
This commit is contained in:
parent
cbdad73dd7
commit
a903049a1a
@ -11,8 +11,8 @@ if {[have_spec linux]} { puts "Run script does not support Linux"; exit 0 }
|
||||
|
||||
set build_components {
|
||||
core init noux/minimal lib/libc_noux app/cli_monitor test/bomb test/signal
|
||||
drivers/timer drivers/uart server/terminal_mux server/terminal_log
|
||||
noux-pkg/vim
|
||||
test/resource_yield drivers/timer drivers/uart server/terminal_mux
|
||||
server/terminal_log noux-pkg/vim
|
||||
}
|
||||
|
||||
build $build_components
|
||||
@ -125,6 +125,11 @@ append config {
|
||||
<resource name="RAM" quantum="5M" />
|
||||
<binary name="test-signal" />
|
||||
</subsystem>
|
||||
<subsystem name="ram_eater" help="resource-yield test program">
|
||||
<resource name="RAM" quantum="25M" />
|
||||
<binary name="test-resource_yield" />
|
||||
<config child="yes" expand="yes" />
|
||||
</subsystem>
|
||||
</config>
|
||||
<route>
|
||||
<any-service> <child name="terminal_mux" /> <any-child/> <parent/> </any-service>
|
||||
@ -143,7 +148,7 @@ install_config $config
|
||||
# generic modules
|
||||
set boot_modules {
|
||||
core init timer ld.lib.so noux terminal_mux terminal_log
|
||||
test-signal cli_monitor
|
||||
test-signal cli_monitor test-resource_yield
|
||||
libc.lib.so libm.lib.so libc_noux.lib.so libc_terminal.lib.so ncurses.lib.so
|
||||
vim.tar
|
||||
}
|
||||
|
303
os/src/app/cli_monitor/child.h
Normal file
303
os/src/app/cli_monitor/child.h
Normal file
@ -0,0 +1,303 @@
|
||||
/*
|
||||
* \brief Child handling
|
||||
* \author Norman Feske
|
||||
* \date 2013-10-05
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2013 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 _CHILD_H_
|
||||
#define _CHILD_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <util/list.h>
|
||||
#include <base/child.h>
|
||||
#include <ram_session/connection.h>
|
||||
#include <cpu_session/connection.h>
|
||||
|
||||
class Child : public List<Child>::Element, Genode::Child_policy
|
||||
{
|
||||
public:
|
||||
|
||||
/*
|
||||
* XXX derive donated quota from information to be provided by
|
||||
* the used 'Connection' interfaces
|
||||
*/
|
||||
enum { DONATED_RAM_QUOTA = 128*1024 };
|
||||
|
||||
class Quota_exceeded : public Genode::Exception { };
|
||||
|
||||
Argument const argument;
|
||||
|
||||
private:
|
||||
|
||||
Ram &_ram;
|
||||
|
||||
struct Label
|
||||
{
|
||||
enum { LABEL_MAX_LEN = 128 };
|
||||
char buf[LABEL_MAX_LEN];
|
||||
Label(char const *label) { strncpy(buf, label, sizeof(buf)); }
|
||||
};
|
||||
|
||||
Label const _label;
|
||||
|
||||
struct Resources
|
||||
{
|
||||
Genode::Ram_connection ram;
|
||||
Genode::Cpu_connection cpu;
|
||||
Genode::Rm_connection rm;
|
||||
|
||||
Resources(const char *label, Genode::size_t ram_quota)
|
||||
: ram(label), cpu(label)
|
||||
{
|
||||
if (ram_quota > DONATED_RAM_QUOTA)
|
||||
ram_quota -= DONATED_RAM_QUOTA;
|
||||
else
|
||||
throw Quota_exceeded();
|
||||
ram.ref_account(Genode::env()->ram_session_cap());
|
||||
if (Genode::env()->ram_session()->transfer_quota(ram.cap(), ram_quota) != 0)
|
||||
throw Quota_exceeded();
|
||||
}
|
||||
};
|
||||
|
||||
size_t _ram_quota;
|
||||
size_t _ram_limit;
|
||||
Resources _resources;
|
||||
Genode::Service_registry _parent_services;
|
||||
Genode::Rom_connection _binary_rom;
|
||||
|
||||
enum { ENTRYPOINT_STACK_SIZE = 12*1024 };
|
||||
Genode::Rpc_entrypoint _entrypoint;
|
||||
|
||||
Init::Child_policy_enforce_labeling _labeling_policy;
|
||||
Init::Child_policy_provide_rom_file _binary_policy;
|
||||
Genode::Child_policy_dynamic_rom_file _config_policy;
|
||||
Genode::Child _child;
|
||||
|
||||
/**
|
||||
* If set to true, immediately withdraw resources yielded by the child
|
||||
*/
|
||||
bool _withdraw_on_yield_response = false;
|
||||
|
||||
/**
|
||||
* Arguments of current resource request from the child
|
||||
*/
|
||||
Genode::Parent::Resource_args _resource_args;
|
||||
|
||||
Genode::Signal_context_capability _yield_response_sigh_cap;
|
||||
|
||||
public:
|
||||
|
||||
Child(Ram &ram,
|
||||
char const *label,
|
||||
char const *binary,
|
||||
Genode::Cap_session &cap_session,
|
||||
Genode::size_t ram_quota,
|
||||
Genode::size_t ram_limit,
|
||||
Genode::Signal_context_capability yield_response_sig_cap)
|
||||
:
|
||||
argument(label, "subsystem"),
|
||||
_ram(ram),
|
||||
_label(label),
|
||||
_ram_quota(ram_quota),
|
||||
_ram_limit(ram_limit),
|
||||
_resources(_label.buf, _ram_quota),
|
||||
_binary_rom(binary, _label.buf),
|
||||
_entrypoint(&cap_session, ENTRYPOINT_STACK_SIZE, _label.buf, false),
|
||||
_labeling_policy(_label.buf),
|
||||
_binary_policy("binary", _binary_rom.dataspace(), &_entrypoint),
|
||||
_config_policy("config", _entrypoint, &_resources.ram),
|
||||
_child(_binary_rom.dataspace(),
|
||||
_resources.ram.cap(), _resources.cpu.cap(),
|
||||
_resources.rm.cap(), &_entrypoint, this),
|
||||
_yield_response_sigh_cap(yield_response_sig_cap)
|
||||
{ }
|
||||
|
||||
void configure(char const *config, size_t config_len)
|
||||
{
|
||||
_config_policy.load(config, config_len);
|
||||
}
|
||||
|
||||
void start()
|
||||
{
|
||||
_entrypoint.activate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Issue yield request to the child
|
||||
*/
|
||||
void yield(size_t amount, bool greedy)
|
||||
{
|
||||
char buf[128];
|
||||
snprintf(buf, sizeof(buf), "ram_quota=%zd", amount);
|
||||
_withdraw_on_yield_response = greedy;
|
||||
_child.yield(buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return amount of RAM currently requested by the child
|
||||
*/
|
||||
size_t requested_ram_quota() const
|
||||
{
|
||||
return Genode::Arg_string::find_arg(_resource_args.string(), "ram_quota").ulong_value(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Withdraw quota from the child
|
||||
*
|
||||
* \throw Ram::Transfer_quota_failed
|
||||
*/
|
||||
void withdraw_ram_quota(size_t amount)
|
||||
{
|
||||
_ram.withdraw_from(_resources.ram.cap(), amount);
|
||||
_ram_quota -= amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Upgrade quota of child
|
||||
*
|
||||
* \throw Ram::Transfer_quota_failed
|
||||
*/
|
||||
void upgrade_ram_quota(size_t amount)
|
||||
{
|
||||
_ram.transfer_to(_resources.ram.cap(), amount);
|
||||
_ram_quota += amount;
|
||||
|
||||
/* wake up child if resource request is in flight */
|
||||
size_t const req = requested_ram_quota();
|
||||
if (req && _resources.ram.avail() >= req) {
|
||||
_child.notify_resource_avail();
|
||||
|
||||
/* clear request state */
|
||||
_resource_args = Genode::Parent::Resource_args("");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to respond to a current resource request issued by the child
|
||||
*
|
||||
* This function evaluates the conditions, under which a resource
|
||||
* request can be answered: There must be enough room between the
|
||||
* current quota and the configured limit, and there must be enough
|
||||
* slack memory available. If both conditions are met, the quota
|
||||
* of the child gets upgraded.
|
||||
*/
|
||||
void try_response_to_resource_request()
|
||||
{
|
||||
size_t const req = requested_ram_quota();
|
||||
|
||||
if (!req)
|
||||
return; /* no resource request in flight */
|
||||
|
||||
/*
|
||||
* Respond to the current request if the requested quota fits
|
||||
* within the limit and if there is enough free quota available.
|
||||
*/
|
||||
if (req <= _ram.status().avail && req + _ram_quota <= _ram_limit) {
|
||||
try { upgrade_ram_quota(req); }
|
||||
catch (Ram::Transfer_quota_failed) { }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set limit for on-demand RAM quota expansion
|
||||
*/
|
||||
void ram_limit(size_t limit)
|
||||
{
|
||||
_ram_limit = limit;
|
||||
try_response_to_resource_request();
|
||||
}
|
||||
|
||||
struct Ram_status
|
||||
{
|
||||
size_t quota = 0, limit = 0, xfer = 0, used = 0, avail = 0, req = 0;
|
||||
|
||||
Ram_status() { }
|
||||
Ram_status(size_t quota, size_t limit, size_t xfer, size_t used,
|
||||
size_t avail, size_t req)
|
||||
:
|
||||
quota(quota), limit(limit), xfer(xfer), used(used),
|
||||
avail(avail), req(req)
|
||||
{ }
|
||||
};
|
||||
|
||||
/**
|
||||
* Return RAM quota status of the child
|
||||
*
|
||||
* XXX should be a const function, but the 'Ram_session' accessors
|
||||
* are not const
|
||||
*/
|
||||
Ram_status ram_status()
|
||||
{
|
||||
return Ram_status(_ram_quota,
|
||||
_ram_limit,
|
||||
_ram_quota - _resources.ram.quota(),
|
||||
_resources.ram.used(),
|
||||
_resources.ram.avail(),
|
||||
requested_ram_quota());
|
||||
}
|
||||
|
||||
|
||||
/****************************
|
||||
** Child_policy interface **
|
||||
****************************/
|
||||
|
||||
const char *name() const { return _label.buf; }
|
||||
|
||||
Genode::Service *resolve_session_request(const char *service_name,
|
||||
const char *args)
|
||||
{
|
||||
Genode::Service *service = 0;
|
||||
|
||||
/* check for binary file request */
|
||||
if ((service = _binary_policy.resolve_session_request(service_name, args)))
|
||||
return service;
|
||||
|
||||
/* check for config file request */
|
||||
if ((service = _config_policy.resolve_session_request(service_name, args)))
|
||||
return service;
|
||||
|
||||
/* fill parent service registry on demand */
|
||||
if (!(service = _parent_services.find(service_name))) {
|
||||
service = new (Genode::env()->heap())
|
||||
Genode::Parent_service(service_name);
|
||||
_parent_services.insert(service);
|
||||
}
|
||||
|
||||
/* return parent service */
|
||||
return service;
|
||||
}
|
||||
|
||||
void filter_session_args(const char *service,
|
||||
char *args, Genode::size_t args_len)
|
||||
{
|
||||
_labeling_policy.filter_session_args(service, args, args_len);
|
||||
}
|
||||
|
||||
void yield_response()
|
||||
{
|
||||
if (_withdraw_on_yield_response) {
|
||||
|
||||
/* try to immediately withdraw freed-up resources */
|
||||
try { withdraw_ram_quota(_resources.ram.avail()); }
|
||||
catch (Ram::Transfer_quota_failed) { }
|
||||
}
|
||||
|
||||
/* propagate yield-response signal */
|
||||
Genode::Signal_transmitter(_yield_response_sigh_cap).submit();
|
||||
}
|
||||
|
||||
void resource_request(Genode::Parent::Resource_args const &args)
|
||||
{
|
||||
_resource_args = args;
|
||||
try_response_to_resource_request();
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _CHILD_H_ */
|
68
os/src/app/cli_monitor/child_registry.h
Normal file
68
os/src/app/cli_monitor/child_registry.h
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* \brief Registry of running children
|
||||
* \author Norman Feske
|
||||
* \date 2013-10-05
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2013 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 _CHILD_REGISTRY_H_
|
||||
#define _CHILD_REGISTRY_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <util/list.h>
|
||||
|
||||
/* local includes */
|
||||
#include <child.h>
|
||||
|
||||
class Child_registry : public List<Child>
|
||||
{
|
||||
private:
|
||||
|
||||
/**
|
||||
* Return true if a child with the specified name already exists
|
||||
*/
|
||||
bool _child_name_exists(const char *label)
|
||||
{
|
||||
for (Child *child = first() ; child; child = child->next())
|
||||
if (strcmp(child->name(), label) == 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
enum { CHILD_NAME_MAX_LEN = 64 };
|
||||
|
||||
/**
|
||||
* Produce new unique child name
|
||||
*/
|
||||
void unique_child_name(const char *prefix, char *dst, int dst_len)
|
||||
{
|
||||
char buf[CHILD_NAME_MAX_LEN];
|
||||
char suffix[8];
|
||||
suffix[0] = 0;
|
||||
|
||||
for (int cnt = 1; true; cnt++) {
|
||||
|
||||
/* build program name composed of prefix and numeric suffix */
|
||||
snprintf(buf, sizeof(buf), "%s%s", prefix, suffix);
|
||||
|
||||
/* if such a program name does not exist yet, we are happy */
|
||||
if (!_child_name_exists(buf)) {
|
||||
strncpy(dst, buf, dst_len);
|
||||
return;
|
||||
}
|
||||
|
||||
/* increase number of suffix */
|
||||
snprintf(suffix, sizeof(suffix), ".%d", cnt + 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _CHILD_REGISTRY_H_ */
|
112
os/src/app/cli_monitor/format_util.h
Normal file
112
os/src/app/cli_monitor/format_util.h
Normal file
@ -0,0 +1,112 @@
|
||||
/*
|
||||
* \brief Utilities for formatting output to terminal
|
||||
* \author Norman Feske
|
||||
* \date 2013-10-05
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2013 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 _FORMAT_UTIL_H_
|
||||
#define _FORMAT_UTIL_H_
|
||||
|
||||
/* local includes */
|
||||
#include <terminal_util.h>
|
||||
|
||||
|
||||
/**
|
||||
* Print rational number with two fractional decimals
|
||||
*/
|
||||
static inline size_t format_number(char *dst, size_t len, size_t const value,
|
||||
size_t const quotient, char const *unit)
|
||||
{
|
||||
size_t const integer = value / quotient;
|
||||
size_t const n = snprintf(dst, len, "%zd.", integer);
|
||||
size_t const remainder = ((value - (integer * quotient))*100) / quotient;
|
||||
|
||||
if (len == n) return n;
|
||||
|
||||
return n + snprintf(dst + n, len - n, "%s%zd%s",
|
||||
remainder < 10 ? "0" : "", remainder, unit);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Print number of bytes using the best suitable unit
|
||||
*/
|
||||
static inline size_t format_bytes(char *dst, size_t len, size_t bytes)
|
||||
{
|
||||
enum { KB = 1024, MB = 1024*KB };
|
||||
|
||||
if (bytes > MB)
|
||||
return format_number(dst, len, bytes, MB, " MiB");
|
||||
|
||||
if (bytes > KB)
|
||||
return format_number(dst, len, bytes, KB, " KiB");
|
||||
|
||||
return snprintf(dst, len, "%zd bytes", bytes);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Print number in MiB, without unit
|
||||
*/
|
||||
static inline size_t format_mib(char *dst, size_t len, size_t bytes)
|
||||
{
|
||||
enum { KB = 1024, MB = 1024*KB };
|
||||
|
||||
return format_number(dst, len, bytes, MB , "");
|
||||
}
|
||||
|
||||
|
||||
static inline size_t format_bytes(size_t bytes)
|
||||
{
|
||||
char buf[128];
|
||||
return format_bytes(buf, sizeof(buf), bytes);
|
||||
}
|
||||
|
||||
|
||||
static inline size_t format_mib(size_t bytes)
|
||||
{
|
||||
char buf[128];
|
||||
return format_mib(buf, sizeof(buf), bytes);
|
||||
}
|
||||
|
||||
|
||||
static inline void tprint_bytes(Terminal::Session &terminal, size_t bytes)
|
||||
{
|
||||
char buf[128];
|
||||
format_bytes(buf, sizeof(buf), bytes);
|
||||
Terminal::tprintf(terminal, "%s", buf);
|
||||
}
|
||||
|
||||
|
||||
static inline void tprint_mib(Terminal::Session &terminal, size_t bytes)
|
||||
{
|
||||
char buf[128];
|
||||
format_mib(buf, sizeof(buf), bytes);
|
||||
Terminal::tprintf(terminal, "%s", buf);
|
||||
}
|
||||
|
||||
|
||||
static inline void tprint_status_bytes(Terminal::Session &terminal,
|
||||
char const *label, size_t bytes)
|
||||
{
|
||||
Terminal::tprintf(terminal, label);
|
||||
tprint_bytes(terminal, bytes);
|
||||
Terminal::tprintf(terminal, "\n");
|
||||
}
|
||||
|
||||
|
||||
static void tprint_padding(Terminal::Session &terminal, size_t pad, char c = ' ')
|
||||
{
|
||||
char const buf[2] = { c, 0 };
|
||||
for (unsigned i = 0; i < pad; i++)
|
||||
Terminal::tprintf(terminal, buf);
|
||||
}
|
||||
|
||||
#endif /* _FORMAT_UTIL_H_ */
|
28
os/src/app/cli_monitor/help_command.h
Normal file
28
os/src/app/cli_monitor/help_command.h
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* \brief Help command
|
||||
* \author Norman Feske
|
||||
* \date 2013-03-18
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2013 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 _HELP_COMMAND_H_
|
||||
#define _HELP_COMMAND_H_
|
||||
|
||||
struct Help_command : Command
|
||||
{
|
||||
Help_command() : Command("help", "brief help information") { }
|
||||
|
||||
void execute(Command_line &, Terminal::Session &terminal)
|
||||
{
|
||||
tprintf(terminal, " Press [tab] for a list of commands.\n");
|
||||
tprintf(terminal, " When given a command, press [tab] for a list of arguments.\n");
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _HELP_COMMAND_H_ */
|
75
os/src/app/cli_monitor/kill_command.h
Normal file
75
os/src/app/cli_monitor/kill_command.h
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* \brief Kill command
|
||||
* \author Norman Feske
|
||||
* \date 2013-03-18
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2013 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 _KILL_COMMAND_H_
|
||||
#define _KILL_COMMAND_H_
|
||||
|
||||
/* local includes */
|
||||
#include <child_registry.h>
|
||||
#include <process_arg_registry.h>
|
||||
|
||||
struct Kill_command : Command
|
||||
{
|
||||
Child_registry &_children;
|
||||
|
||||
Process_arg_registry &_process_args;
|
||||
|
||||
void _destroy_child(Child *child, Terminal::Session &terminal)
|
||||
{
|
||||
tprintf(terminal, "destroying subsystem '%s'\n", child->name());
|
||||
_process_args.list.remove(&child->argument);
|
||||
_children.remove(child);
|
||||
Genode::destroy(Genode::env()->heap(), child);
|
||||
}
|
||||
|
||||
Kill_command(Child_registry &children, Process_arg_registry &process_args)
|
||||
:
|
||||
Command("kill", "destroy subsystem"),
|
||||
_children(children),
|
||||
_process_args(process_args)
|
||||
{
|
||||
add_parameter(new Parameter("--all", Parameter::VOID, "kill all subsystems"));
|
||||
}
|
||||
|
||||
void execute(Command_line &cmd, Terminal::Session &terminal)
|
||||
{
|
||||
bool const kill_all = cmd.parameter_exists("--all");
|
||||
|
||||
if (kill_all) {
|
||||
for (Child *child = _children.first(); child; child = _children.first())
|
||||
_destroy_child(child, terminal);
|
||||
return;
|
||||
}
|
||||
|
||||
char label[128];
|
||||
label[0] = 0;
|
||||
if (cmd.argument(0, label, sizeof(label)) == false) {
|
||||
tprintf(terminal, "Error: no subsystem name specified\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* lookup child by its unique name */
|
||||
for (Child *child = _children.first(); child; child = child->next()) {
|
||||
if (strcmp(child->name(), label) == 0) {
|
||||
_destroy_child(child, terminal);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
tprintf(terminal, "Error: subsystem '%s' does not exist\n", label);
|
||||
}
|
||||
|
||||
List<Argument> &arguments() { return _process_args.list; }
|
||||
};
|
||||
|
||||
#endif /* _KILL_COMMAND_H_ */
|
@ -101,21 +101,25 @@ struct Command_line;
|
||||
*/
|
||||
struct Command : List<Command>::Element, Completable
|
||||
{
|
||||
List<Argument> _arguments;
|
||||
List<Parameter> _parameters;
|
||||
|
||||
Command(char const *name, char const *short_help)
|
||||
: Completable(name, short_help) { }
|
||||
|
||||
void add_parameter(Parameter *par) { _parameters.insert(par); }
|
||||
void add_argument (Argument *arg) { _arguments. insert(arg); }
|
||||
|
||||
void remove_argument(Argument *arg) { _arguments.remove(arg); }
|
||||
|
||||
char const *name_suffix() const { return ""; }
|
||||
|
||||
List<Parameter> ¶meters() { return _parameters; }
|
||||
List<Argument> &arguments() { return _arguments; }
|
||||
|
||||
/**
|
||||
* To be overridden by commands that accept auto-completion of arguments
|
||||
*/
|
||||
virtual List<Argument> &arguments()
|
||||
{
|
||||
static List<Argument> empty;
|
||||
return empty;
|
||||
}
|
||||
|
||||
virtual void execute(Command_line &, Terminal::Session &terminal) = 0;
|
||||
};
|
||||
@ -713,11 +717,21 @@ class Line_editor
|
||||
Terminal::Session &terminal, Command_registry &commands)
|
||||
:
|
||||
_prompt(prompt), _prompt_len(strlen(prompt)),
|
||||
_buf(buf), _buf_size(buf_size), _cursor_pos(0),
|
||||
_terminal(terminal), _commands(commands),
|
||||
_complete(false)
|
||||
_buf(buf), _buf_size(buf_size),
|
||||
_terminal(terminal), _commands(commands)
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset prompt to initial state after construction
|
||||
*/
|
||||
void reset()
|
||||
{
|
||||
_buf[0] = 0;
|
||||
_complete = false;
|
||||
_cursor_pos = 0;
|
||||
_seq_tracker = Seq_tracker();
|
||||
_fresh_prompt();
|
||||
}
|
||||
|
||||
|
@ -12,499 +12,33 @@
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/child.h>
|
||||
#include <init/child_policy.h>
|
||||
#include <os/config.h>
|
||||
#include <os/child_policy_dynamic_rom.h>
|
||||
#include <ram_session/connection.h>
|
||||
#include <cpu_session/connection.h>
|
||||
#include <rm_session/connection.h>
|
||||
#include <cap_session/connection.h>
|
||||
#include <rom_session/connection.h>
|
||||
|
||||
/* local includes */
|
||||
#include <line_editor.h>
|
||||
#include <command_line.h>
|
||||
#include <terminal_util.h>
|
||||
#include <format_util.h>
|
||||
#include <extension.h>
|
||||
#include <ram.h>
|
||||
#include <status_command.h>
|
||||
#include <kill_command.h>
|
||||
#include <start_command.h>
|
||||
#include <help_command.h>
|
||||
#include <yield_command.h>
|
||||
#include <ram_command.h>
|
||||
|
||||
using Terminal::tprintf;
|
||||
using Genode::Xml_node;
|
||||
|
||||
|
||||
/***************
|
||||
** Utilities **
|
||||
***************/
|
||||
|
||||
static inline void tprint_bytes(Terminal::Session &terminal, size_t bytes)
|
||||
{
|
||||
enum { KB = 1024, MB = 1024*KB };
|
||||
if (bytes > MB) {
|
||||
size_t const mb = bytes / MB;
|
||||
|
||||
tprintf(terminal, "%zd.", mb);
|
||||
size_t const remainder = bytes - (mb * MB);
|
||||
|
||||
tprintf(terminal, "%zd MiB", (remainder*100)/(MB));
|
||||
return;
|
||||
}
|
||||
|
||||
if (bytes > KB) {
|
||||
size_t const kb = bytes / KB;
|
||||
|
||||
tprintf(terminal, "%zd.", kb);
|
||||
size_t const remainder = bytes - (kb * KB);
|
||||
|
||||
tprintf(terminal, "%zd KiB", (remainder*100)/(KB));
|
||||
return;
|
||||
}
|
||||
|
||||
tprintf(terminal, "%zd bytes", bytes);
|
||||
}
|
||||
|
||||
|
||||
static inline void tprint_status_bytes(Terminal::Session &terminal,
|
||||
char const *label, size_t bytes)
|
||||
{
|
||||
tprintf(terminal, label);
|
||||
tprint_bytes(terminal, bytes);
|
||||
tprintf(terminal, "\n");
|
||||
}
|
||||
|
||||
|
||||
inline void *operator new (size_t size)
|
||||
{
|
||||
return Genode::env()->heap()->alloc(size);
|
||||
}
|
||||
|
||||
|
||||
/********************
|
||||
** Child handling **
|
||||
********************/
|
||||
|
||||
class Child : public List<Child>::Element, Genode::Child_policy
|
||||
{
|
||||
public:
|
||||
|
||||
/*
|
||||
* XXX derive donated quota from information to be provided by
|
||||
* the used 'Connection' interfaces
|
||||
*/
|
||||
enum { DONATED_RAM_QUOTA = 128*1024 };
|
||||
|
||||
class Quota_exceeded : public Genode::Exception { };
|
||||
|
||||
private:
|
||||
|
||||
struct Label
|
||||
{
|
||||
enum { LABEL_MAX_LEN = 128 };
|
||||
char buf[LABEL_MAX_LEN];
|
||||
Label(char const *label) { strncpy(buf, label, sizeof(buf)); }
|
||||
};
|
||||
|
||||
Label const _label;
|
||||
|
||||
Argument _kill_argument;
|
||||
|
||||
struct Resources
|
||||
{
|
||||
Genode::Ram_connection ram;
|
||||
Genode::Cpu_connection cpu;
|
||||
Genode::Rm_connection rm;
|
||||
|
||||
Resources(const char *label, Genode::size_t ram_quota)
|
||||
: ram(label), cpu(label)
|
||||
{
|
||||
if (ram_quota > DONATED_RAM_QUOTA)
|
||||
ram_quota -= DONATED_RAM_QUOTA;
|
||||
else
|
||||
throw Quota_exceeded();
|
||||
ram.ref_account(Genode::env()->ram_session_cap());
|
||||
if (Genode::env()->ram_session()->transfer_quota(ram.cap(), ram_quota) != 0)
|
||||
throw Quota_exceeded();
|
||||
}
|
||||
};
|
||||
|
||||
Resources _resources;
|
||||
Genode::Service_registry _parent_services;
|
||||
Genode::Rom_connection _binary_rom;
|
||||
|
||||
enum { ENTRYPOINT_STACK_SIZE = 12*1024 };
|
||||
Genode::Rpc_entrypoint _entrypoint;
|
||||
|
||||
Init::Child_policy_enforce_labeling _labeling_policy;
|
||||
Init::Child_policy_provide_rom_file _binary_policy;
|
||||
Genode::Child_policy_dynamic_rom_file _config_policy;
|
||||
Genode::Child _child;
|
||||
|
||||
public:
|
||||
|
||||
Child(char const *label,
|
||||
char const *binary,
|
||||
Genode::Cap_session &cap_session,
|
||||
Genode::size_t ram_quota)
|
||||
:
|
||||
_label(label),
|
||||
_kill_argument(label, "subsystem"),
|
||||
_resources(_label.buf, ram_quota),
|
||||
_binary_rom(binary, _label.buf),
|
||||
_entrypoint(&cap_session, ENTRYPOINT_STACK_SIZE, _label.buf, false),
|
||||
_labeling_policy(_label.buf),
|
||||
_binary_policy("binary", _binary_rom.dataspace(), &_entrypoint),
|
||||
_config_policy("config", _entrypoint, &_resources.ram),
|
||||
_child(_binary_rom.dataspace(),
|
||||
_resources.ram.cap(), _resources.cpu.cap(),
|
||||
_resources.rm.cap(), &_entrypoint, this)
|
||||
{ }
|
||||
|
||||
void configure(char const *config, size_t config_len)
|
||||
{
|
||||
_config_policy.load(config, config_len);
|
||||
}
|
||||
|
||||
void start()
|
||||
{
|
||||
_entrypoint.activate();
|
||||
}
|
||||
|
||||
Argument *kill_argument() { return &_kill_argument; }
|
||||
|
||||
|
||||
/****************************
|
||||
** Child_policy interface **
|
||||
****************************/
|
||||
|
||||
const char *name() const { return _label.buf; }
|
||||
|
||||
Genode::Service *resolve_session_request(const char *service_name,
|
||||
const char *args)
|
||||
{
|
||||
Genode::Service *service = 0;
|
||||
|
||||
/* check for binary file request */
|
||||
if ((service = _binary_policy.resolve_session_request(service_name, args)))
|
||||
return service;
|
||||
|
||||
/* check for config file request */
|
||||
if ((service = _config_policy.resolve_session_request(service_name, args)))
|
||||
return service;
|
||||
|
||||
/* fill parent service registry on demand */
|
||||
if (!(service = _parent_services.find(service_name))) {
|
||||
service = new (Genode::env()->heap())
|
||||
Genode::Parent_service(service_name);
|
||||
_parent_services.insert(service);
|
||||
}
|
||||
|
||||
/* return parent service */
|
||||
return service;
|
||||
}
|
||||
|
||||
void filter_session_args(const char *service,
|
||||
char *args, Genode::size_t args_len)
|
||||
{
|
||||
_labeling_policy.filter_session_args(service, args, args_len);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class Child_registry : public List<Child>
|
||||
{
|
||||
private:
|
||||
|
||||
/**
|
||||
* Return true if a child with the specified name already exists
|
||||
*/
|
||||
bool _child_name_exists(const char *label)
|
||||
{
|
||||
for (Child *child = first() ; child; child = child->next())
|
||||
if (strcmp(child->name(), label) == 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
enum { CHILD_NAME_MAX_LEN = 64 };
|
||||
|
||||
/**
|
||||
* Produce new unique child name
|
||||
*/
|
||||
void unique_child_name(const char *prefix, char *dst, int dst_len)
|
||||
{
|
||||
char buf[CHILD_NAME_MAX_LEN];
|
||||
char suffix[8];
|
||||
suffix[0] = 0;
|
||||
|
||||
for (int cnt = 1; true; cnt++) {
|
||||
|
||||
/* build program name composed of prefix and numeric suffix */
|
||||
snprintf(buf, sizeof(buf), "%s%s", prefix, suffix);
|
||||
|
||||
/* if such a program name does not exist yet, we are happy */
|
||||
if (!_child_name_exists(buf)) {
|
||||
strncpy(dst, buf, dst_len);
|
||||
return;
|
||||
}
|
||||
|
||||
/* increase number of suffix */
|
||||
snprintf(suffix, sizeof(suffix), ".%d", cnt + 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**************
|
||||
** Commands **
|
||||
**************/
|
||||
|
||||
struct Help_command : Command
|
||||
{
|
||||
Help_command() : Command("help", "brief help information") { }
|
||||
|
||||
void execute(Command_line &, Terminal::Session &terminal)
|
||||
{
|
||||
tprintf(terminal, " Press [tab] for a list of commands.\n");
|
||||
tprintf(terminal, " When given a command, press [tab] for a list of arguments.\n");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Kill_command : Command
|
||||
{
|
||||
Child_registry &_children;
|
||||
|
||||
void _destroy_child(Child *child, Terminal::Session &terminal)
|
||||
{
|
||||
tprintf(terminal, "destroying subsystem '%s'\n", child->name());
|
||||
remove_argument(child->kill_argument());
|
||||
_children.remove(child);
|
||||
Genode::destroy(Genode::env()->heap(), child);
|
||||
}
|
||||
|
||||
Kill_command(Child_registry &children)
|
||||
:
|
||||
Command("kill", "destroy subsystem"),
|
||||
_children(children)
|
||||
{
|
||||
add_parameter(new Parameter("--all", Parameter::VOID, "kill all subsystems"));
|
||||
}
|
||||
|
||||
void execute(Command_line &cmd, Terminal::Session &terminal)
|
||||
{
|
||||
bool const kill_all = cmd.parameter_exists("--all");
|
||||
|
||||
if (kill_all) {
|
||||
for (Child *child = _children.first(); child; child = _children.first())
|
||||
_destroy_child(child, terminal);
|
||||
return;
|
||||
}
|
||||
|
||||
char label[128];
|
||||
label[0] = 0;
|
||||
if (cmd.argument(0, label, sizeof(label)) == false) {
|
||||
tprintf(terminal, "Error: no configuration name specified\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* lookup child by its unique name */
|
||||
for (Child *child = _children.first(); child; child = child->next()) {
|
||||
if (strcmp(child->name(), label) == 0) {
|
||||
_destroy_child(child, terminal);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
tprintf(terminal, "Error: subsystem '%s' does not exist\n", label);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Start_command : Command
|
||||
{
|
||||
Child_registry &_children;
|
||||
Genode::Cap_session &_cap;
|
||||
Xml_node _config;
|
||||
Kill_command &_kill_command;
|
||||
|
||||
Start_command(Genode::Cap_session &cap, Child_registry &children,
|
||||
Xml_node config, Kill_command &kill_command)
|
||||
:
|
||||
Command("start", "create new subsystem"),
|
||||
_children(children), _cap(cap), _config(config),
|
||||
_kill_command(kill_command)
|
||||
{
|
||||
/* scan config for possible subsystem arguments */
|
||||
try {
|
||||
Xml_node node = _config.sub_node("subsystem");
|
||||
for (;; node = node.next("subsystem")) {
|
||||
|
||||
char name[Parameter::NAME_MAX_LEN];
|
||||
try { node.attribute("name").value(name, sizeof(name)); }
|
||||
catch (Xml_node::Nonexistent_attribute) {
|
||||
PWRN("Missing name in '<subsystem>' configuration");
|
||||
continue;
|
||||
}
|
||||
|
||||
char const *prefix = "config: ";
|
||||
size_t const prefix_len = strlen(prefix);
|
||||
|
||||
char help[Parameter::SHORT_HELP_MAX_LEN + prefix_len];
|
||||
strncpy(help, prefix, ~0);
|
||||
try { node.attribute("help").value(help + prefix_len,
|
||||
sizeof(help) - prefix_len); }
|
||||
catch (Xml_node::Nonexistent_attribute) {
|
||||
PWRN("Missing help in '<subsystem>' configuration");
|
||||
continue;
|
||||
}
|
||||
|
||||
add_argument(new Argument(name, help));
|
||||
}
|
||||
} catch (Xml_node::Nonexistent_sub_node) { /* end of list */ }
|
||||
|
||||
add_parameter(new Parameter("--count", Parameter::NUMBER, "number of instances"));
|
||||
add_parameter(new Parameter("--ram", Parameter::NUMBER, "RAM quota"));
|
||||
add_parameter(new Parameter("--verbose", Parameter::VOID, "show diagnostics"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup subsystem in config
|
||||
*/
|
||||
Xml_node _subsystem_node(char const *name)
|
||||
{
|
||||
Xml_node node = _config.sub_node("subsystem");
|
||||
for (;; node = node.next("subsystem")) {
|
||||
if (node.attribute("name").has_value(name))
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
void execute(Command_line &cmd, Terminal::Session &terminal)
|
||||
{
|
||||
size_t count = 1;
|
||||
Genode::Number_of_bytes ram = 0;
|
||||
|
||||
char name[128];
|
||||
name[0] = 0;
|
||||
if (cmd.argument(0, name, sizeof(name)) == false) {
|
||||
tprintf(terminal, "Error: no configuration name specified\n");
|
||||
return;
|
||||
}
|
||||
|
||||
char buf[128];
|
||||
if (cmd.argument(1, buf, sizeof(buf))) {
|
||||
tprintf(terminal, "Error: unexpected argument \"%s\"\n", buf);
|
||||
return;
|
||||
}
|
||||
|
||||
/* check if a configuration for the subsystem exists */
|
||||
try { _subsystem_node(name); }
|
||||
catch (Xml_node::Nonexistent_sub_node) {
|
||||
tprintf(terminal, "Error: no configuration for \"%s\"\n", name);
|
||||
return;
|
||||
}
|
||||
|
||||
/* read default RAM quota from config */
|
||||
try {
|
||||
Xml_node rsc = _subsystem_node(name).sub_node("resource");
|
||||
for (;; rsc = rsc.next("resource")) {
|
||||
if (rsc.attribute("name").has_value("RAM")) {
|
||||
rsc.attribute("quantum").value(&ram);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (...) { }
|
||||
|
||||
cmd.parameter("--count", count);
|
||||
cmd.parameter("--ram", ram);
|
||||
|
||||
bool const verbose = cmd.parameter_exists("--verbose");
|
||||
|
||||
/*
|
||||
* Determine binary name
|
||||
*
|
||||
* Use subsystem name by default, override with '<binary>' declaration.
|
||||
*/
|
||||
char binary_name[128];
|
||||
strncpy(binary_name, name, sizeof(binary_name));
|
||||
try {
|
||||
Xml_node bin = _subsystem_node(name).sub_node("binary");
|
||||
bin.attribute("name").value(binary_name, sizeof(binary_name));
|
||||
} catch (...) { }
|
||||
|
||||
for (unsigned i = 0; i < count; i++) {
|
||||
|
||||
/* generate unique child name */
|
||||
char label[Child_registry::CHILD_NAME_MAX_LEN];
|
||||
_children.unique_child_name(name, label, sizeof(label));
|
||||
|
||||
tprintf(terminal, "starting new subsystem '%s'\n", label);
|
||||
|
||||
if (verbose) {
|
||||
tprintf(terminal, " RAM quota: ");
|
||||
tprint_bytes(terminal, ram);
|
||||
tprintf(terminal,"\n");
|
||||
tprintf(terminal, " binary: %s\n", binary_name);
|
||||
}
|
||||
|
||||
Child *child = 0;
|
||||
try {
|
||||
child = new (Genode::env()->heap())
|
||||
Child(label, binary_name, _cap, ram);
|
||||
}
|
||||
catch (Genode::Rom_connection::Rom_connection_failed) {
|
||||
tprintf(terminal, "Error: could not obtain ROM module \"%s\"\n",
|
||||
binary_name);
|
||||
return;
|
||||
}
|
||||
catch (Child::Quota_exceeded) {
|
||||
tprintf(terminal, "Error: insufficient memory, need ");
|
||||
tprint_bytes(terminal, ram + Child::DONATED_RAM_QUOTA);
|
||||
tprintf(terminal, ", have ");
|
||||
tprint_bytes(terminal, Genode::env()->ram_session()->avail());
|
||||
tprintf(terminal, "\n");
|
||||
return;
|
||||
}
|
||||
catch (Genode::Allocator::Out_of_memory) {
|
||||
tprintf(terminal, "Error: could not allocate meta data, out of memory\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* configure child */
|
||||
try {
|
||||
Xml_node config_node = _subsystem_node(name).sub_node("config");
|
||||
child->configure(config_node.addr(), config_node.size());
|
||||
if (verbose)
|
||||
tprintf(terminal, " config: inline\n");
|
||||
} catch (...) {
|
||||
if (verbose)
|
||||
tprintf(terminal, " config: none\n");
|
||||
}
|
||||
|
||||
_kill_command.add_argument(child->kill_argument());
|
||||
_children.insert(child);
|
||||
child->start();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Status_command : Command
|
||||
{
|
||||
Status_command() : Command("status", "show runtime status") { }
|
||||
|
||||
void execute(Command_line &, Terminal::Session &terminal)
|
||||
{
|
||||
Genode::Ram_session *ram = Genode::env()->ram_session();
|
||||
|
||||
tprint_status_bytes(terminal, " RAM quota: ", ram->quota());
|
||||
tprint_status_bytes(terminal, " used: ", ram->used());
|
||||
tprint_status_bytes(terminal, " avail: ", ram->avail());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/******************
|
||||
** Main program **
|
||||
******************/
|
||||
@ -520,6 +54,21 @@ static inline Command *lookup_command(char const *buf, Command_registry ®istr
|
||||
}
|
||||
|
||||
|
||||
static size_t ram_preservation_from_config()
|
||||
{
|
||||
Genode::Number_of_bytes ram_preservation = 0;
|
||||
try {
|
||||
Genode::Xml_node node =
|
||||
Genode::config()->xml_node().sub_node("preservation");
|
||||
|
||||
if (node.attribute("name").has_value("RAM"))
|
||||
node.attribute("quantum").value(&ram_preservation);
|
||||
} catch (...) { }
|
||||
|
||||
return ram_preservation;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
/* look for dynamic linker */
|
||||
@ -529,46 +78,55 @@ int main(int argc, char **argv)
|
||||
} catch (...) { }
|
||||
|
||||
using Genode::Signal_context;
|
||||
using Genode::Signal_context_capability;
|
||||
using Genode::Signal_receiver;
|
||||
|
||||
try { Genode::config()->xml_node(); }
|
||||
catch (...) {
|
||||
PERR("Error: could not obtain configuration");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static Genode::Cap_connection cap;
|
||||
static Terminal::Connection terminal;
|
||||
static Command_registry commands;
|
||||
static Child_registry children;
|
||||
static Process_arg_registry process_args;
|
||||
|
||||
/* initialize platform-specific commands */
|
||||
init_extension(commands);
|
||||
|
||||
/* initialize generic commands */
|
||||
commands.insert(new Help_command);
|
||||
Kill_command kill_command(children);
|
||||
commands.insert(&kill_command);
|
||||
commands.insert(new Start_command(cap, children,
|
||||
Genode::config()->xml_node(),
|
||||
kill_command));
|
||||
commands.insert(new Status_command);
|
||||
|
||||
static Signal_receiver sig_rec;
|
||||
static Signal_context read_avail_sig_ctx;
|
||||
terminal.read_avail_sigh(sig_rec.manage(&read_avail_sig_ctx));
|
||||
|
||||
static Signal_context yield_response_sig_ctx;
|
||||
static Signal_context_capability yield_response_sig_cap =
|
||||
sig_rec.manage(&yield_response_sig_ctx);
|
||||
|
||||
static Signal_context yield_broadcast_sig_ctx;
|
||||
static Signal_context resource_avail_sig_ctx;
|
||||
|
||||
static Ram ram(ram_preservation_from_config(),
|
||||
sig_rec.manage(&yield_broadcast_sig_ctx),
|
||||
sig_rec.manage(&resource_avail_sig_ctx));
|
||||
|
||||
/* initialize generic commands */
|
||||
commands.insert(new Help_command);
|
||||
Kill_command kill_command(children, process_args);
|
||||
commands.insert(&kill_command);
|
||||
commands.insert(new Start_command(ram, cap, children,
|
||||
Genode::config()->xml_node(),
|
||||
process_args,
|
||||
yield_response_sig_cap));
|
||||
commands.insert(new Status_command(ram, children));
|
||||
commands.insert(new Yield_command(children, process_args));
|
||||
commands.insert(new Ram_command(children, process_args));
|
||||
|
||||
enum { COMMAND_MAX_LEN = 1000 };
|
||||
static char buf[COMMAND_MAX_LEN];
|
||||
static Line_editor line_editor("genode> ", buf, sizeof(buf), terminal, commands);
|
||||
|
||||
for (;;) {
|
||||
|
||||
enum { COMMAND_MAX_LEN = 1000 };
|
||||
static char buf[COMMAND_MAX_LEN];
|
||||
/* block for event, e.g., the arrival of new user input */
|
||||
Genode::Signal signal = sig_rec.wait_for_signal();
|
||||
|
||||
Line_editor line_editor("genode> ", buf, sizeof(buf), terminal, commands);
|
||||
|
||||
while (!line_editor.is_complete()) {
|
||||
|
||||
/* block for event, e.g., the arrival of new user input */
|
||||
sig_rec.wait_for_signal();
|
||||
if (signal.context() == &read_avail_sig_ctx) {
|
||||
|
||||
/* supply pending terminal input to line editor */
|
||||
while (terminal.avail() && !line_editor.is_complete()) {
|
||||
@ -578,12 +136,44 @@ int main(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
if (signal.context() == &yield_response_sig_ctx
|
||||
|| signal.context() == &resource_avail_sig_ctx) {
|
||||
|
||||
for (Child *child = children.first(); child; child = child->next())
|
||||
child->try_response_to_resource_request();
|
||||
}
|
||||
|
||||
if (signal.context() == &yield_broadcast_sig_ctx) {
|
||||
|
||||
/*
|
||||
* Compute argument of yield request to be broadcasted to all
|
||||
* processes.
|
||||
*/
|
||||
size_t amount = 0;
|
||||
|
||||
/* amount needed to reach preservation limit */
|
||||
Ram::Status ram_status = ram.status();
|
||||
if (ram_status.avail < ram_status.preserve)
|
||||
amount += ram_status.preserve - ram_status.avail;
|
||||
|
||||
/* sum of pending resource requests */
|
||||
for (Child *child = children.first(); child; child = child->next())
|
||||
amount += child->requested_ram_quota();
|
||||
|
||||
for (Child *child = children.first(); child; child = child->next())
|
||||
child->yield(amount, true);
|
||||
}
|
||||
|
||||
if (!line_editor.is_complete())
|
||||
continue;
|
||||
|
||||
Command *command = lookup_command(buf, commands);
|
||||
if (!command) {
|
||||
Token cmd_name(buf);
|
||||
tprintf(terminal, "Error: unknown command \"");
|
||||
terminal.write(cmd_name.start(), cmd_name.len());
|
||||
tprintf(terminal, "\"\n");
|
||||
line_editor.reset();
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -594,9 +184,17 @@ int main(int argc, char **argv)
|
||||
tprintf(terminal, "Error: unexpected parameter \"");
|
||||
terminal.write(unexpected.start(), unexpected.len());
|
||||
tprintf(terminal, "\"\n");
|
||||
line_editor.reset();
|
||||
continue;
|
||||
}
|
||||
command->execute(cmd_line, terminal);
|
||||
|
||||
/*
|
||||
* The command might result in a change of the RAM usage. Validate
|
||||
* that the preservation is satisfied.
|
||||
*/
|
||||
ram.validate_preservation();
|
||||
line_editor.reset();
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
25
os/src/app/cli_monitor/process_arg_registry.h
Normal file
25
os/src/app/cli_monitor/process_arg_registry.h
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* \brief Registry of process names used as arguments
|
||||
* \author Norman Feske
|
||||
* \date 2013-03-18
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2013 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 _PROCESS_ARG_REGISTRY_H_
|
||||
#define _PROCESS_ARG_REGISTRY_H_
|
||||
|
||||
/**
|
||||
* Registry of arguments referring to the currently running processes
|
||||
*/
|
||||
struct Process_arg_registry
|
||||
{
|
||||
Genode::List<Argument> list;
|
||||
};
|
||||
|
||||
#endif /* _PROCESS_ARG_REGISTRY_H_ */
|
123
os/src/app/cli_monitor/ram.h
Normal file
123
os/src/app/cli_monitor/ram.h
Normal file
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* \brief RAM management
|
||||
* \author Norman Feske
|
||||
* \date 2013-10-14
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2013 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 _RAM_H_
|
||||
#define _RAM_H_
|
||||
|
||||
class Ram
|
||||
{
|
||||
private:
|
||||
|
||||
Genode::Lock mutable _lock;
|
||||
Genode::Ram_session &_ram = *Genode::env()->ram_session();
|
||||
Genode::Ram_session_capability _ram_cap = Genode::env()->ram_session_cap();
|
||||
Genode::Signal_context_capability _yield_sigh;
|
||||
Genode::Signal_context_capability _resource_avail_sigh;
|
||||
|
||||
size_t _preserve;
|
||||
|
||||
void _validate_preservation()
|
||||
{
|
||||
if (_ram.avail() < _preserve)
|
||||
Genode::Signal_transmitter(_yield_sigh).submit();
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
struct Status
|
||||
{
|
||||
size_t quota, used, avail, preserve;
|
||||
Status(size_t quota, size_t used, size_t avail, size_t preserve)
|
||||
: quota(quota), used(used), avail(avail), preserve(preserve) { }
|
||||
};
|
||||
|
||||
Ram(size_t preserve,
|
||||
Genode::Signal_context_capability yield_sigh,
|
||||
Genode::Signal_context_capability resource_avail_sigh)
|
||||
:
|
||||
_yield_sigh(yield_sigh), _preserve(preserve)
|
||||
{ }
|
||||
|
||||
size_t preserve() const
|
||||
{
|
||||
Genode::Lock::Guard guard(_lock);
|
||||
|
||||
return _preserve;
|
||||
}
|
||||
|
||||
void preserve(size_t preserve)
|
||||
{
|
||||
Genode::Lock::Guard guard(_lock);
|
||||
|
||||
_preserve = preserve;
|
||||
|
||||
_validate_preservation();
|
||||
}
|
||||
|
||||
Status status() const
|
||||
{
|
||||
Genode::Lock::Guard guard(_lock);
|
||||
|
||||
return Status(_ram.quota(), _ram.used(), _ram.avail(), _preserve);
|
||||
}
|
||||
|
||||
void validate_preservation()
|
||||
{
|
||||
Genode::Lock::Guard guard(_lock);
|
||||
|
||||
_validate_preservation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Exception type
|
||||
*/
|
||||
class Transfer_quota_failed { };
|
||||
|
||||
/**
|
||||
* \throw Transfer_quota_failed
|
||||
*/
|
||||
void withdraw_from(Genode::Ram_session_capability from, size_t amount)
|
||||
{
|
||||
Genode::Lock::Guard guard(_lock);
|
||||
|
||||
int const ret =
|
||||
Genode::Ram_session_client(from).transfer_quota(_ram_cap, amount);
|
||||
|
||||
if (ret != 0)
|
||||
throw Transfer_quota_failed();
|
||||
|
||||
Genode::Signal_transmitter(_resource_avail_sigh).submit();
|
||||
}
|
||||
|
||||
/**
|
||||
* \throw Transfer_quota_failed
|
||||
*/
|
||||
void transfer_to(Genode::Ram_session_capability to, size_t amount)
|
||||
{
|
||||
Genode::Lock::Guard guard(_lock);
|
||||
|
||||
int const ret = _ram.transfer_quota(to, amount);
|
||||
|
||||
if (ret != 0)
|
||||
throw Transfer_quota_failed();
|
||||
|
||||
_validate_preservation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return singleton object
|
||||
*/
|
||||
static Ram &ram();
|
||||
};
|
||||
|
||||
#endif /* _RAM_H_ */
|
123
os/src/app/cli_monitor/ram_command.h
Normal file
123
os/src/app/cli_monitor/ram_command.h
Normal file
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* \brief RAM command
|
||||
* \author Norman Feske
|
||||
* \date 2013-10-05
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2013 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 _RAM_COMMAND_H_
|
||||
#define _RAM_COMMAND_H_
|
||||
|
||||
/* local includes */
|
||||
#include <child_registry.h>
|
||||
#include <process_arg_registry.h>
|
||||
|
||||
struct Ram_command : Command
|
||||
{
|
||||
Child_registry &_children;
|
||||
Process_arg_registry &_process_args;
|
||||
|
||||
Ram_command(Child_registry &children, Process_arg_registry &process_args)
|
||||
:
|
||||
Command("ram", "set RAM quota of subsystem"),
|
||||
_children(children),
|
||||
_process_args(process_args)
|
||||
{
|
||||
add_parameter(new Parameter("--quota", Parameter::NUMBER, "new RAM quota"));
|
||||
add_parameter(new Parameter("--limit", Parameter::NUMBER, "on-demand quota limit"));
|
||||
}
|
||||
|
||||
void _set_quota(Terminal::Session &terminal, Child &child, size_t const new_quota)
|
||||
{
|
||||
size_t const old_quota = child.ram_status().quota;
|
||||
|
||||
if (new_quota > old_quota) {
|
||||
|
||||
size_t amount = new_quota - old_quota;
|
||||
size_t const avail = Genode::env()->ram_session()->avail();
|
||||
if (amount > avail) {
|
||||
tprintf(terminal, "upgrade of '%s' exceeds available quota of ",
|
||||
child.name());
|
||||
tprint_bytes(terminal, avail);
|
||||
tprintf(terminal, "\n");
|
||||
amount = avail;
|
||||
}
|
||||
|
||||
tprintf(terminal, "upgrading quota of '%s' to ", child.name());
|
||||
tprint_bytes(terminal, old_quota + amount);
|
||||
tprintf(terminal, "\n");
|
||||
|
||||
try {
|
||||
child.upgrade_ram_quota(amount); }
|
||||
catch (Ram::Transfer_quota_failed) {
|
||||
tprintf(terminal, "Error: transfer_quota failed\n"); }
|
||||
|
||||
} if (new_quota < old_quota) {
|
||||
|
||||
size_t amount = old_quota - new_quota;
|
||||
size_t const avail = child.ram_status().avail;
|
||||
|
||||
if (amount > avail) {
|
||||
tprintf(terminal, "withdrawal of ");
|
||||
tprint_bytes(terminal, amount);
|
||||
tprintf(terminal, " exceeds available quota of ");
|
||||
tprint_bytes(terminal, avail);
|
||||
tprintf(terminal, "\n");
|
||||
amount = avail;
|
||||
}
|
||||
|
||||
tprintf(terminal, "depleting quota of '%s' to ", child.name());
|
||||
tprint_bytes(terminal, old_quota - amount);
|
||||
tprintf(terminal, "\n");
|
||||
|
||||
try {
|
||||
child.withdraw_ram_quota(amount); }
|
||||
catch (Ram::Transfer_quota_failed) {
|
||||
tprintf(terminal, "Error: transfer_quota failed\n"); }
|
||||
}
|
||||
}
|
||||
|
||||
void execute(Command_line &cmd, Terminal::Session &terminal)
|
||||
{
|
||||
char label[128];
|
||||
label[0] = 0;
|
||||
if (cmd.argument(0, label, sizeof(label)) == false) {
|
||||
tprintf(terminal, "Error: no subsystem name specified\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* lookup child by its unique name */
|
||||
Child *child = _children.first();
|
||||
for (; child; child = child->next())
|
||||
if (strcmp(child->name(), label) == 0)
|
||||
break;
|
||||
|
||||
if (!child) {
|
||||
tprintf(terminal, "Error: subsystem '%s' does not exist\n", label);
|
||||
return;
|
||||
}
|
||||
|
||||
bool const limit_specified = cmd.parameter_exists("--limit");
|
||||
Genode::Number_of_bytes limit = 0;
|
||||
if (limit_specified) {
|
||||
cmd.parameter("--limit", limit);
|
||||
child->ram_limit(limit);
|
||||
}
|
||||
|
||||
if (cmd.parameter_exists("--quota")) {
|
||||
Genode::Number_of_bytes quota = 0;
|
||||
cmd.parameter("--quota", quota);
|
||||
_set_quota(terminal, *child, quota);
|
||||
}
|
||||
}
|
||||
|
||||
List<Argument> &arguments() { return _process_args.list; }
|
||||
};
|
||||
|
||||
#endif /* _RAM_COMMAND_H_ */
|
216
os/src/app/cli_monitor/start_command.h
Normal file
216
os/src/app/cli_monitor/start_command.h
Normal file
@ -0,0 +1,216 @@
|
||||
/*
|
||||
* \brief Start command
|
||||
* \author Norman Feske
|
||||
* \date 2013-03-18
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2013 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 _START_COMMAND_H_
|
||||
#define _START_COMMAND_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <util/xml_node.h>
|
||||
|
||||
struct Start_command : Command
|
||||
{
|
||||
typedef Genode::Xml_node Xml_node;
|
||||
typedef Genode::Signal_context_capability Signal_context_capability;
|
||||
|
||||
Ram &_ram;
|
||||
Child_registry &_children;
|
||||
Genode::Cap_session &_cap;
|
||||
Xml_node _config;
|
||||
Process_arg_registry &_process_args;
|
||||
List<Argument> _arguments;
|
||||
Signal_context_capability _yield_response_sigh_cap;
|
||||
|
||||
Start_command(Ram &ram, Genode::Cap_session &cap, Child_registry &children,
|
||||
Xml_node config, Process_arg_registry &process_args,
|
||||
Signal_context_capability yield_response_sigh_cap)
|
||||
:
|
||||
Command("start", "create new subsystem"),
|
||||
_ram(ram), _children(children), _cap(cap), _config(config),
|
||||
_process_args(process_args),
|
||||
_yield_response_sigh_cap(yield_response_sigh_cap)
|
||||
{
|
||||
/* scan config for possible subsystem arguments */
|
||||
try {
|
||||
Xml_node node = _config.sub_node("subsystem");
|
||||
for (;; node = node.next("subsystem")) {
|
||||
|
||||
char name[Parameter::NAME_MAX_LEN];
|
||||
try { node.attribute("name").value(name, sizeof(name)); }
|
||||
catch (Xml_node::Nonexistent_attribute) {
|
||||
PWRN("Missing name in '<subsystem>' configuration");
|
||||
continue;
|
||||
}
|
||||
|
||||
char const *prefix = "config: ";
|
||||
size_t const prefix_len = strlen(prefix);
|
||||
|
||||
char help[Parameter::SHORT_HELP_MAX_LEN + prefix_len];
|
||||
strncpy(help, prefix, ~0);
|
||||
try { node.attribute("help").value(help + prefix_len,
|
||||
sizeof(help) - prefix_len); }
|
||||
catch (Xml_node::Nonexistent_attribute) {
|
||||
PWRN("Missing help in '<subsystem>' configuration");
|
||||
continue;
|
||||
}
|
||||
|
||||
_arguments.insert(new Argument(name, help));
|
||||
}
|
||||
} catch (Xml_node::Nonexistent_sub_node) { /* end of list */ }
|
||||
|
||||
add_parameter(new Parameter("--count", Parameter::NUMBER, "number of instances"));
|
||||
add_parameter(new Parameter("--ram", Parameter::NUMBER, "initial RAM quota"));
|
||||
add_parameter(new Parameter("--ram-limit", Parameter::NUMBER, "limit for expanding RAM quota"));
|
||||
add_parameter(new Parameter("--verbose", Parameter::VOID, "show diagnostics"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup subsystem in config
|
||||
*/
|
||||
Xml_node _subsystem_node(char const *name)
|
||||
{
|
||||
Xml_node node = _config.sub_node("subsystem");
|
||||
for (;; node = node.next("subsystem")) {
|
||||
if (node.attribute("name").has_value(name))
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
void execute(Command_line &cmd, Terminal::Session &terminal)
|
||||
{
|
||||
size_t count = 1;
|
||||
Genode::Number_of_bytes ram = 0;
|
||||
Genode::Number_of_bytes ram_limit = 0;
|
||||
|
||||
char name[128];
|
||||
name[0] = 0;
|
||||
if (cmd.argument(0, name, sizeof(name)) == false) {
|
||||
tprintf(terminal, "Error: no configuration name specified\n");
|
||||
return;
|
||||
}
|
||||
|
||||
char buf[128];
|
||||
if (cmd.argument(1, buf, sizeof(buf))) {
|
||||
tprintf(terminal, "Error: unexpected argument \"%s\"\n", buf);
|
||||
return;
|
||||
}
|
||||
|
||||
/* check if a configuration for the subsystem exists */
|
||||
try { _subsystem_node(name); }
|
||||
catch (Xml_node::Nonexistent_sub_node) {
|
||||
tprintf(terminal, "Error: no configuration for \"%s\"\n", name);
|
||||
return;
|
||||
}
|
||||
|
||||
/* read default RAM quota from config */
|
||||
try {
|
||||
Xml_node rsc = _subsystem_node(name).sub_node("resource");
|
||||
for (;; rsc = rsc.next("resource")) {
|
||||
if (rsc.attribute("name").has_value("RAM")) {
|
||||
rsc.attribute("quantum").value(&ram);
|
||||
|
||||
if (rsc.has_attribute("limit"))
|
||||
rsc.attribute("limit").value(&ram_limit);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (...) { }
|
||||
|
||||
cmd.parameter("--count", count);
|
||||
cmd.parameter("--ram", ram);
|
||||
cmd.parameter("--ram-limit", ram_limit);
|
||||
|
||||
size_t preserve_ram = 1*1024*1024;
|
||||
if (ram + preserve_ram > Genode::env()->ram_session()->avail()) {
|
||||
tprintf(terminal, "Error: RAM quota exceeds available quota\n");
|
||||
return;
|
||||
}
|
||||
|
||||
bool const verbose = cmd.parameter_exists("--verbose");
|
||||
|
||||
/*
|
||||
* Determine binary name
|
||||
*
|
||||
* Use subsystem name by default, override with '<binary>' declaration.
|
||||
*/
|
||||
char binary_name[128];
|
||||
strncpy(binary_name, name, sizeof(binary_name));
|
||||
try {
|
||||
Xml_node bin = _subsystem_node(name).sub_node("binary");
|
||||
bin.attribute("name").value(binary_name, sizeof(binary_name));
|
||||
} catch (...) { }
|
||||
|
||||
for (unsigned i = 0; i < count; i++) {
|
||||
|
||||
/* generate unique child name */
|
||||
char label[Child_registry::CHILD_NAME_MAX_LEN];
|
||||
_children.unique_child_name(name, label, sizeof(label));
|
||||
|
||||
tprintf(terminal, "starting new subsystem '%s'\n", label);
|
||||
|
||||
if (verbose) {
|
||||
tprintf(terminal, " RAM quota: ");
|
||||
tprint_bytes(terminal, ram);
|
||||
tprintf(terminal,"\n");
|
||||
if (ram_limit) {
|
||||
tprintf(terminal, " RAM limit: ");
|
||||
tprint_bytes(terminal, ram_limit);
|
||||
tprintf(terminal,"\n");
|
||||
}
|
||||
tprintf(terminal, " binary: %s\n", binary_name);
|
||||
}
|
||||
|
||||
Child *child = 0;
|
||||
try {
|
||||
child = new (Genode::env()->heap())
|
||||
Child(_ram, label, binary_name, _cap, ram, ram_limit,
|
||||
_yield_response_sigh_cap);
|
||||
}
|
||||
catch (Genode::Rom_connection::Rom_connection_failed) {
|
||||
tprintf(terminal, "Error: could not obtain ROM module \"%s\"\n",
|
||||
binary_name);
|
||||
return;
|
||||
}
|
||||
catch (Child::Quota_exceeded) {
|
||||
tprintf(terminal, "Error: insufficient memory, need ");
|
||||
tprint_bytes(terminal, ram + Child::DONATED_RAM_QUOTA);
|
||||
tprintf(terminal, ", have ");
|
||||
tprint_bytes(terminal, Genode::env()->ram_session()->avail());
|
||||
tprintf(terminal, "\n");
|
||||
return;
|
||||
}
|
||||
catch (Genode::Allocator::Out_of_memory) {
|
||||
tprintf(terminal, "Error: could not allocate meta data, out of memory\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* configure child */
|
||||
try {
|
||||
Xml_node config_node = _subsystem_node(name).sub_node("config");
|
||||
child->configure(config_node.addr(), config_node.size());
|
||||
if (verbose)
|
||||
tprintf(terminal, " config: inline\n");
|
||||
} catch (...) {
|
||||
if (verbose)
|
||||
tprintf(terminal, " config: none\n");
|
||||
}
|
||||
|
||||
_process_args.list.insert(&child->argument);
|
||||
_children.insert(child);
|
||||
child->start();
|
||||
}
|
||||
}
|
||||
|
||||
List<Argument> &arguments() { return _arguments; }
|
||||
};
|
||||
|
||||
#endif /* _START_COMMAND_H_ */
|
154
os/src/app/cli_monitor/status_command.h
Normal file
154
os/src/app/cli_monitor/status_command.h
Normal file
@ -0,0 +1,154 @@
|
||||
/*
|
||||
* \brief Status command
|
||||
* \author Norman Feske
|
||||
* \date 2013-10-05
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2013 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 _STATUS_COMMAND_H_
|
||||
#define _STATUS_COMMAND_H_
|
||||
|
||||
/* local includes */
|
||||
#include <table.h>
|
||||
#include <child_registry.h>
|
||||
|
||||
struct Status_command : Command
|
||||
{
|
||||
Child_registry &_children;
|
||||
Ram &_ram;
|
||||
|
||||
Status_command(Ram &ram, Child_registry &children)
|
||||
:
|
||||
Command("status", "show runtime status"),
|
||||
_children(children), _ram(ram)
|
||||
{ }
|
||||
|
||||
void execute(Command_line &, Terminal::Session &terminal)
|
||||
{
|
||||
using Terminal::tprintf;
|
||||
|
||||
Ram::Status const ram_status = _ram.status();
|
||||
|
||||
tprint_status_bytes(terminal, " RAM quota: ", ram_status.quota);
|
||||
tprint_status_bytes(terminal, " used: ", ram_status.used);
|
||||
tprint_status_bytes(terminal, " avail: ", ram_status.avail);
|
||||
tprint_status_bytes(terminal, " preserve: ", ram_status.preserve);
|
||||
|
||||
tprintf(terminal, "\n");
|
||||
|
||||
struct Child_info
|
||||
{
|
||||
enum Column { NAME, QUOTA, LIMIT, XFER, USED, AVAIL, STATUS };
|
||||
|
||||
constexpr static size_t num_columns() { return STATUS + 1; }
|
||||
|
||||
char const *name = 0;
|
||||
Child::Ram_status ram_status;
|
||||
|
||||
static char const *label(Column column)
|
||||
{
|
||||
switch (column) {
|
||||
case NAME: return "process";
|
||||
case QUOTA: return "quota";
|
||||
case LIMIT: return "limit";
|
||||
case XFER: return "xfer";
|
||||
case USED: return "alloc";
|
||||
case AVAIL: return "avail";
|
||||
case STATUS: return "status";
|
||||
};
|
||||
return "";
|
||||
}
|
||||
|
||||
size_t len(Column column) const
|
||||
{
|
||||
switch (column) {
|
||||
case NAME: return strlen(name);
|
||||
case QUOTA: return format_mib(ram_status.quota);
|
||||
case LIMIT:
|
||||
return ram_status.limit ? format_mib(ram_status.limit) : 0;
|
||||
|
||||
case XFER: return format_mib(ram_status.xfer);
|
||||
case USED: return format_mib(ram_status.used);
|
||||
case AVAIL: return format_mib(ram_status.avail);
|
||||
case STATUS:
|
||||
{
|
||||
size_t const req = ram_status.req;
|
||||
return req ? strlen("req ") + format_mib(req) : 0;
|
||||
}
|
||||
};
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool left_aligned(Column column)
|
||||
{
|
||||
switch (column) {
|
||||
case NAME: return true;
|
||||
case QUOTA: return false;
|
||||
case LIMIT: return false;
|
||||
case XFER: return false;
|
||||
case USED: return false;
|
||||
case AVAIL: return false;
|
||||
case STATUS: return true;
|
||||
};
|
||||
return false;
|
||||
}
|
||||
|
||||
void print_cell(Terminal::Session &terminal, Column column)
|
||||
{
|
||||
switch (column) {
|
||||
case NAME: tprintf(terminal, "%s", name); break;
|
||||
case QUOTA: tprint_mib(terminal, ram_status.quota); break;
|
||||
case LIMIT:
|
||||
|
||||
if (ram_status.limit)
|
||||
tprint_mib(terminal, ram_status.limit);
|
||||
break;
|
||||
|
||||
case XFER: tprint_mib(terminal, ram_status.xfer); break;
|
||||
case USED: tprint_mib(terminal, ram_status.used); break;
|
||||
case AVAIL: tprint_mib(terminal, ram_status.avail); break;
|
||||
case STATUS:
|
||||
if (ram_status.req) {
|
||||
tprintf(terminal, "req ");
|
||||
tprint_mib(terminal, ram_status.req);
|
||||
}
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
Child_info() { }
|
||||
Child_info(char const *name, Child::Ram_status ram_status)
|
||||
:
|
||||
name(name), ram_status(ram_status)
|
||||
{ }
|
||||
};
|
||||
|
||||
/*
|
||||
* Take snapshot of child information.
|
||||
*/
|
||||
size_t num_children = 0;
|
||||
for (Child *c = _children.first(); c; c = c->next())
|
||||
num_children++;
|
||||
|
||||
Child_info child_info[num_children];
|
||||
unsigned i = 0;
|
||||
for (Child *c = _children.first(); c && i < num_children; c = c->next(), i++)
|
||||
child_info[i] = Child_info(c->name(), c->ram_status());
|
||||
|
||||
/*
|
||||
* Print table
|
||||
*/
|
||||
if (num_children) {
|
||||
Table<Child_info>::print(terminal, child_info, num_children);
|
||||
tprintf(terminal, "\n");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _STATUS_COMMAND_H_ */
|
96
os/src/app/cli_monitor/table.h
Normal file
96
os/src/app/cli_monitor/table.h
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* \brief Utility for printing a table to the terminal
|
||||
* \author Norman Feske
|
||||
* \date 2013-10-05
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2013 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 _TABLE_H_
|
||||
#define _TABLE_H_
|
||||
|
||||
template <typename TI>
|
||||
class Table
|
||||
{
|
||||
private:
|
||||
|
||||
static void _print_cell(TI &info, Terminal::Session &terminal,
|
||||
typename TI::Column column, size_t column_size)
|
||||
{
|
||||
size_t const padding = column_size - info.len(column);
|
||||
|
||||
if (!TI::left_aligned(column))
|
||||
tprint_padding(terminal, padding);
|
||||
|
||||
info.print_cell(terminal, column);
|
||||
|
||||
if (TI::left_aligned(column))
|
||||
tprint_padding(terminal, padding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print centered title of table column
|
||||
*/
|
||||
static void _print_label(Terminal::Session &terminal,
|
||||
typename TI::Column column, size_t column_size)
|
||||
{
|
||||
size_t const padding = column_size - strlen(TI::label(column));
|
||||
size_t const left_padding = padding / 2;
|
||||
|
||||
tprint_padding(terminal, left_padding);
|
||||
tprintf(terminal, "%s", TI::label(column));
|
||||
tprint_padding(terminal, padding - left_padding);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
static void print(Terminal::Session &terminal, TI info[], unsigned num_rows)
|
||||
{
|
||||
/*
|
||||
* Determine formatting of table
|
||||
*/
|
||||
size_t column_size[TI::num_columns()];
|
||||
for (unsigned i = 0; i < TI::num_columns(); i++)
|
||||
column_size[i] = strlen(TI::label((typename TI::Column)i));
|
||||
|
||||
for (unsigned i = 0; i < num_rows; i++) {
|
||||
for (unsigned j = 0; j < TI::num_columns(); j++)
|
||||
column_size[j] = max(column_size[j],
|
||||
info[i].len((typename TI::Column)j));
|
||||
}
|
||||
|
||||
/*
|
||||
* Print table
|
||||
*/
|
||||
tprintf(terminal, " ");
|
||||
for (unsigned j = 0; j < TI::num_columns(); j++) {
|
||||
_print_label(terminal, (typename TI::Column)j, column_size[j]);
|
||||
if (j < TI::num_columns() - 1) tprintf(terminal, " | ");
|
||||
}
|
||||
tprintf(terminal, "\n");
|
||||
|
||||
tprintf(terminal, " ");
|
||||
for (unsigned j = 0; j < TI::num_columns(); j++) {
|
||||
for (unsigned i = 0; i < column_size[j]; i++)
|
||||
tprintf(terminal, "-");
|
||||
if (j < TI::num_columns() - 1) tprintf(terminal, "-+-");
|
||||
}
|
||||
tprintf(terminal, "\n");
|
||||
|
||||
for (unsigned i = 0; i < num_rows; i++) {
|
||||
tprintf(terminal, " ");
|
||||
for (unsigned j = 0; j < TI::num_columns(); j++) {
|
||||
_print_cell(info[i], terminal, (typename TI::Column)j, column_size[j]);
|
||||
if (j < TI::num_columns() - 1) tprintf(terminal, " | ");
|
||||
}
|
||||
tprintf(terminal, "\n");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _TABLE_H_ */
|
71
os/src/app/cli_monitor/yield_command.h
Normal file
71
os/src/app/cli_monitor/yield_command.h
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* \brief Yield command
|
||||
* \author Norman Feske
|
||||
* \date 2013-10-05
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2013 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 _YIELD_COMMAND_H_
|
||||
#define _YIELD_COMMAND_H_
|
||||
|
||||
/* local includes */
|
||||
#include <child_registry.h>
|
||||
#include <process_arg_registry.h>
|
||||
|
||||
struct Yield_command : Command
|
||||
{
|
||||
Child_registry &_children;
|
||||
Process_arg_registry &_process_args;
|
||||
|
||||
Yield_command(Child_registry &children, Process_arg_registry &process_args)
|
||||
:
|
||||
Command("yield", "instruct subsystem to yield resources"),
|
||||
_children(children),
|
||||
_process_args(process_args)
|
||||
{
|
||||
add_parameter(new Parameter("--ram", Parameter::NUMBER, "RAM quota to free"));
|
||||
add_parameter(new Parameter("--greedy", Parameter::VOID, "withdraw yielded RAM quota"));
|
||||
}
|
||||
|
||||
void execute(Command_line &cmd, Terminal::Session &terminal)
|
||||
{
|
||||
char label[128];
|
||||
label[0] = 0;
|
||||
if (cmd.argument(0, label, sizeof(label)) == false) {
|
||||
tprintf(terminal, "Error: no subsystem name specified\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Genode::Number_of_bytes ram = 0;
|
||||
cmd.parameter("--ram", ram);
|
||||
|
||||
bool const greedy = cmd.parameter_exists("--greedy");
|
||||
|
||||
/* lookup child by its unique name */
|
||||
Child *child = _children.first();
|
||||
for (; child; child = child->next())
|
||||
if (strcmp(child->name(), label) == 0)
|
||||
break;
|
||||
|
||||
if (!child) {
|
||||
tprintf(terminal, "Error: subsystem '%s' does not exist\n", label);
|
||||
return;
|
||||
}
|
||||
|
||||
child->yield(ram, greedy);
|
||||
|
||||
tprintf(terminal, "requesting '%s' to yield ", child->name());
|
||||
tprint_bytes(terminal, ram);
|
||||
tprintf(terminal, "\n");
|
||||
}
|
||||
|
||||
List<Argument> &arguments() { return _process_args.list; }
|
||||
};
|
||||
|
||||
#endif /* _YIELD_COMMAND_H_ */
|
Loading…
Reference in New Issue
Block a user