diff --git a/gems/run/terminal_mux.run b/gems/run/terminal_mux.run
index 98121dc814..ba8319e086 100644
--- a/gems/run/terminal_mux.run
+++ b/gems/run/terminal_mux.run
@@ -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 {
+
+
+
+
+
@@ -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
}
diff --git a/os/src/app/cli_monitor/child.h b/os/src/app/cli_monitor/child.h
new file mode 100644
index 0000000000..06c7c82975
--- /dev/null
+++ b/os/src/app/cli_monitor/child.h
@@ -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
+#include
+#include
+#include
+
+class Child : public List::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_ */
diff --git a/os/src/app/cli_monitor/child_registry.h b/os/src/app/cli_monitor/child_registry.h
new file mode 100644
index 0000000000..83b00f09ad
--- /dev/null
+++ b/os/src/app/cli_monitor/child_registry.h
@@ -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
+
+/* local includes */
+#include
+
+class Child_registry : public List
+{
+ 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_ */
diff --git a/os/src/app/cli_monitor/format_util.h b/os/src/app/cli_monitor/format_util.h
new file mode 100644
index 0000000000..8dd0e8ba84
--- /dev/null
+++ b/os/src/app/cli_monitor/format_util.h
@@ -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
+
+
+/**
+ * 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_ */
diff --git a/os/src/app/cli_monitor/help_command.h b/os/src/app/cli_monitor/help_command.h
new file mode 100644
index 0000000000..6289429496
--- /dev/null
+++ b/os/src/app/cli_monitor/help_command.h
@@ -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_ */
diff --git a/os/src/app/cli_monitor/kill_command.h b/os/src/app/cli_monitor/kill_command.h
new file mode 100644
index 0000000000..ec1a0758ed
--- /dev/null
+++ b/os/src/app/cli_monitor/kill_command.h
@@ -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
+#include
+
+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 &arguments() { return _process_args.list; }
+};
+
+#endif /* _KILL_COMMAND_H_ */
diff --git a/os/src/app/cli_monitor/line_editor.h b/os/src/app/cli_monitor/line_editor.h
index 00bd647c89..0fb2d8895a 100644
--- a/os/src/app/cli_monitor/line_editor.h
+++ b/os/src/app/cli_monitor/line_editor.h
@@ -101,21 +101,25 @@ struct Command_line;
*/
struct Command : List::Element, Completable
{
- List _arguments;
List _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 ¶meters() { return _parameters; }
- List &arguments() { return _arguments; }
+
+ /**
+ * To be overridden by commands that accept auto-completion of arguments
+ */
+ virtual List &arguments()
+ {
+ static List 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();
}
diff --git a/os/src/app/cli_monitor/main.cc b/os/src/app/cli_monitor/main.cc
index d0335c3d22..3e9843ea94 100644
--- a/os/src/app/cli_monitor/main.cc
+++ b/os/src/app/cli_monitor/main.cc
@@ -12,499 +12,33 @@
*/
/* Genode includes */
-#include
#include
#include
#include
-#include
-#include
-#include
#include
-#include
/* local includes */
#include
#include
-#include
+#include
#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
-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::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
-{
- 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 '' 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 '' 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 '' 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;
diff --git a/os/src/app/cli_monitor/process_arg_registry.h b/os/src/app/cli_monitor/process_arg_registry.h
new file mode 100644
index 0000000000..2ec5b5cdce
--- /dev/null
+++ b/os/src/app/cli_monitor/process_arg_registry.h
@@ -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 list;
+};
+
+#endif /* _PROCESS_ARG_REGISTRY_H_ */
diff --git a/os/src/app/cli_monitor/ram.h b/os/src/app/cli_monitor/ram.h
new file mode 100644
index 0000000000..d7ab181905
--- /dev/null
+++ b/os/src/app/cli_monitor/ram.h
@@ -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_ */
diff --git a/os/src/app/cli_monitor/ram_command.h b/os/src/app/cli_monitor/ram_command.h
new file mode 100644
index 0000000000..7f4d103ac2
--- /dev/null
+++ b/os/src/app/cli_monitor/ram_command.h
@@ -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
+#include
+
+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 &arguments() { return _process_args.list; }
+};
+
+#endif /* _RAM_COMMAND_H_ */
diff --git a/os/src/app/cli_monitor/start_command.h b/os/src/app/cli_monitor/start_command.h
new file mode 100644
index 0000000000..665dce6f00
--- /dev/null
+++ b/os/src/app/cli_monitor/start_command.h
@@ -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
+
+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 _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 '' 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 '' 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 '' 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 &arguments() { return _arguments; }
+};
+
+#endif /* _START_COMMAND_H_ */
diff --git a/os/src/app/cli_monitor/status_command.h b/os/src/app/cli_monitor/status_command.h
new file mode 100644
index 0000000000..7b46db586e
--- /dev/null
+++ b/os/src/app/cli_monitor/status_command.h
@@ -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
+#include
+
+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::print(terminal, child_info, num_children);
+ tprintf(terminal, "\n");
+ }
+ }
+};
+
+#endif /* _STATUS_COMMAND_H_ */
diff --git a/os/src/app/cli_monitor/table.h b/os/src/app/cli_monitor/table.h
new file mode 100644
index 0000000000..8510facf0d
--- /dev/null
+++ b/os/src/app/cli_monitor/table.h
@@ -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
+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_ */
diff --git a/os/src/app/cli_monitor/yield_command.h b/os/src/app/cli_monitor/yield_command.h
new file mode 100644
index 0000000000..13b99011a3
--- /dev/null
+++ b/os/src/app/cli_monitor/yield_command.h
@@ -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
+#include
+
+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 &arguments() { return _process_args.list; }
+};
+
+#endif /* _YIELD_COMMAND_H_ */