diff --git a/repos/os/run/trace_logger.run b/repos/os/run/trace_logger.run
new file mode 100644
index 0000000000..708cd798d2
--- /dev/null
+++ b/repos/os/run/trace_logger.run
@@ -0,0 +1,172 @@
+#
+# Build
+#
+
+set build_components {
+ core
+ init
+ drivers/timer
+ server/dynamic_rom
+ app/cpu_burner
+ test/trace_logger
+ app/trace_logger
+ lib/trace/policy/null
+ lib/trace/policy/rpc_name
+}
+
+proc gpio_drv { } { if {[have_spec rpi] && [have_spec hw]} { return hw_gpio_drv }
+ if {[have_spec rpi] && [have_spec foc]} { return foc_gpio_drv }
+ return gpio_drv }
+
+source ${genode_dir}/repos/base/run/platform_drv.inc
+
+lappend_if [have_spec gpio] build_components drivers/gpio
+
+append_platform_drv_build_components
+
+build $build_components
+
+create_boot_directory
+
+#
+# Generate config
+#
+
+append config {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+
+append_platform_drv_config
+
+append_if [have_spec gpio] config "
+
+
+
+
+ "
+
+append config {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+}
+
+install_config $config
+
+
+#
+# Boot modules
+#
+
+# generic modules
+set boot_modules {
+ core
+ ld.lib.so
+ init
+ timer
+ dynamic_rom
+ cpu_burner
+ trace_logger
+ test-trace_logger
+ null
+ rpc_name
+}
+
+# platform-specific modules
+append_platform_drv_boot_modules
+
+lappend_if [have_spec gpio] boot_modules [gpio_drv]
+
+build_boot_image $boot_modules
+
+append qemu_args " -smp 4,cores=4 "
+
+run_genode_until forever
diff --git a/repos/os/src/app/trace_logger/README b/repos/os/src/app/trace_logger/README
new file mode 100644
index 0000000000..17671819bf
--- /dev/null
+++ b/repos/os/src/app/trace_logger/README
@@ -0,0 +1,100 @@
+
+ ==========================
+ Convenient tracing fronted
+ ==========================
+
+
+Brief
+#####
+
+The 'trace_logger' component can be used to easily gather, process and export
+different types of tracing data. Which subjects to select is configurable via
+session label policies and thread names. Which data to collect from the
+selected subjects can be configured for each subject individually, for groups
+of subjects, or for all subjects. The gathered data can be exported as log
+output.
+
+
+Configuration
+#############
+
+This is an example configuration of the 'trace_logger' component which shows
+the default value for each attribute except the policy.thread and
+policy.label:
+
+!
+!
+!
+!
+!
+!
+
+
+This is a short description of the tags and attributes:
+
+:config.verbose:
+ Optional. Toggles wether the trace_logger shall log debugging information.
+
+:config.session_ram:
+ Optional. Amount of RAM donated to the trace session.
+
+:config.session_arg_buffer:
+ Optional. Size of the trace sessions argument buffer.
+
+:config.session_parent_levels:
+ Optional. Number of parent levels to trace.
+
+:config.period_sec:
+ Optional. Length of processing/export interval in seconds.
+
+:config.activity:
+ Optional. Wether to export thread-activity information.
+
+:config.affinity:
+ Optional. Wether to export thread-affinity information.
+
+:config.default_policy:
+ Optional. Name of tracing policy for subjects without individual config.
+
+:config.default_policy:
+ Optional. Size of tracing buffer for subjects without individual config.
+
+:config.policy:
+ Subject selector. For matching subjects, tracing is enabled and the defined
+ individual configuration is applied.
+
+:config.policy.label:
+:config.policy.label_prefix:
+:config.policy.label_suffix:
+ Mutually exclusive. Filters subjects according to their session label.
+
+:config.policy.thread:
+ Optional. Filters subjects according to their exact thread name.
+
+:config.policy.buffer:
+ Optional. Size of tracing buffer used for matching subjects.
+
+:config.policy.policy:
+ Optional. Name of tracing policy used for matching subjects.
+
+
+Sessions
+########
+
+This is an overview of the sessions required and provided by the
+'trace_logger' component apart from the environment sessions:
+
+* Requires ROM sessions to all configured tracing policies.
+* Requires one TRACE session that provides the desired subjects.
+* Requires one Timer session.
diff --git a/repos/os/src/app/trace_logger/avl_tree.h b/repos/os/src/app/trace_logger/avl_tree.h
new file mode 100644
index 0000000000..0d9e6b74e2
--- /dev/null
+++ b/repos/os/src/app/trace_logger/avl_tree.h
@@ -0,0 +1,59 @@
+/*
+ * \brief AVL-tree wrapper for additional functionality
+ * \author Martin Stein
+ * \date 2018-01-12
+ */
+
+/*
+ * Copyright (C) 2018 Genode Labs GmbH
+ *
+ * This file is part of the Genode OS framework, which is distributed
+ * under the terms of the GNU Affero General Public License version 3.
+ */
+
+#ifndef _AVL_TREE_H_
+#define _AVL_TREE_H_
+
+/* Genode includes */
+#include
+
+namespace Local {
+
+ template class Avl_node;
+ template class Avl_tree;
+}
+
+
+/**
+ * AVL-node wrapper for additional functionality
+ */
+template
+struct Local::Avl_node : Genode::Avl_node
+{
+ using Base = Genode::Avl_node;
+
+ template
+ void for_each(FUNC && functor)
+ {
+ if (NT * l = Base::child(Avl_node::LEFT)) l->for_each(functor);
+ functor(*static_cast(this));
+ if (NT * r = Base::child(Avl_node::RIGHT)) r->for_each(functor);
+ }
+};
+
+
+/**
+ * AVL-tree wrapper for additional functionality
+ */
+template
+struct Local::Avl_tree : Genode::Avl_tree
+{
+ using Base = Genode::Avl_tree;
+
+ template
+ void for_each(FUNC && functor) {
+ if (Base::first()) Base::first()->for_each(functor); }
+};
+
+
+#endif /* _AVL_TREE_H_ */
diff --git a/repos/os/src/app/trace_logger/config.xsd b/repos/os/src/app/trace_logger/config.xsd
new file mode 100644
index 0000000000..21987052f7
--- /dev/null
+++ b/repos/os/src/app/trace_logger/config.xsd
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/repos/os/src/app/trace_logger/main.cc b/repos/os/src/app/trace_logger/main.cc
new file mode 100644
index 0000000000..a87b371121
--- /dev/null
+++ b/repos/os/src/app/trace_logger/main.cc
@@ -0,0 +1,196 @@
+/*
+ * \brief Log information about trace subjects
+ * \author Martin Stein
+ * \date 2018-01-15
+ */
+
+/*
+ * Copyright (C) 2018 Genode Labs GmbH
+ *
+ * This file is part of the Genode OS framework, which is distributed
+ * under the terms of the GNU Affero General Public License version 3.
+ */
+
+/* local includes */
+#include
+#include
+#include
+
+/* Genode includes */
+#include
+#include
+#include
+#include
+#include
+#include
+
+using namespace Genode;
+using Thread_name = String<40>;
+
+
+class Main
+{
+ private:
+
+ enum { MAX_SUBJECTS = 512 };
+ enum { DEFAULT_PERIOD_SEC = 5 };
+ enum { DEFAULT_BUFFER = 1024 * 4 };
+ enum { DEFAULT_SESSION_ARG_BUFFER = 1024 * 4 };
+ enum { DEFAULT_SESSION_RAM = 1024 * 1024 };
+ enum { DEFAULT_SESSION_PARENT_LEVELS = 0 };
+
+ Env &_env;
+ Timer::Connection _timer { _env };
+ Attached_rom_dataspace _config_rom { _env, "config" };
+ Xml_node const _config { _config_rom.xml() };
+ Trace::Connection _trace { _env,
+ _config.attribute_value("session_ram", Number_of_bytes(DEFAULT_SESSION_RAM)),
+ _config.attribute_value("session_arg_buffer", Number_of_bytes(DEFAULT_SESSION_ARG_BUFFER)),
+ _config.attribute_value("session_parent_levels", (unsigned)DEFAULT_SESSION_PARENT_LEVELS) };
+ bool const _affinity { _config.attribute_value("affinity", false) };
+ bool const _activity { _config.attribute_value("activity", false) };
+ bool const _verbose { _config.attribute_value("verbose", false) };
+ Microseconds const _period_us { read_sec_attr(_config, "period_sec", DEFAULT_PERIOD_SEC) };
+ Number_of_bytes const _default_buf_sz { _config.attribute_value("default_buffer", Number_of_bytes(DEFAULT_BUFFER)) };
+ Timer::Periodic_timeout _period { _timer, *this, &Main::_handle_period, _period_us };
+ Heap _heap { _env.ram(), _env.rm() };
+ Monitor_tree _monitors_0 { };
+ Monitor_tree _monitors_1 { };
+ bool _monitors_switch { false };
+ Policy_tree _policies { };
+ Policy_name _default_policy_name { _config.attribute_value("default_policy", Policy_name("null")) };
+ Policy _default_policy { _env, _trace, _default_policy_name };
+ unsigned long _report_id { 0 };
+ unsigned long _num_subjects { 0 };
+ unsigned long _num_monitors { 0 };
+ Trace::Subject_id _subjects[MAX_SUBJECTS];
+
+ void _handle_period(Duration)
+ {
+ /*
+ * Update monitors
+ *
+ * Which monitors are held and how they are configured depends on:
+ *
+ * 1) Which subjects are available at the Trace session,
+ * 2) which tracing state the subjects are currently in,
+ * 3) the configuration of this component about which subjects
+ * to monitor and how
+ *
+ * All this might have changed since the last call of this method.
+ * So, adapt the monitors and the monitor tree accordingly.
+ */
+
+ /*
+ * Switch monitor trees so that the new tree is empty and the old
+ * tree contains all monitors.
+ */
+ Monitor_tree &old_monitors = _monitors_switch ? _monitors_1 : _monitors_0;
+ Monitor_tree &new_monitors = _monitors_switch ? _monitors_0 : _monitors_1;
+ _monitors_switch = !_monitors_switch;
+
+ /* update available subject IDs and iterate over them */
+ try { _num_subjects = _trace.subjects(_subjects, MAX_SUBJECTS); }
+ catch (Out_of_ram ) { warning("Cannot list subjects: Out_of_ram" ); return; }
+ catch (Out_of_caps) { warning("Cannot list subjects: Out_of_caps"); return; }
+ for (unsigned i = 0; i < _num_subjects; i++) {
+
+ Trace::Subject_id const id = _subjects[i];
+ try {
+ /* skip dead subjects */
+ if (_trace.subject_info(id).state() == Trace::Subject_info::DEAD)
+ continue;
+
+ /* check if there is a matching policy in the XML config */
+ Session_policy session_policy = _session_policy(id);
+ try {
+ /* lookup monitor by subject ID */
+ Monitor &monitor = old_monitors.find_by_subject_id(id);
+
+ /* move monitor from old to new tree */
+ old_monitors.remove(&monitor);
+ new_monitors.insert(&monitor);
+
+ } catch (Monitor_tree::No_match) {
+
+ /* create monitor for subject in the new tree */
+ _new_monitor(new_monitors, id, session_policy);
+ }
+ }
+ catch (Trace::Nonexistent_subject ) { continue; }
+ catch (Session_policy::No_policy_defined) { continue; }
+ }
+ /* all monitors in the old tree are deprecated, destroy them */
+ while (Monitor *monitor = old_monitors.first())
+ _destroy_monitor(old_monitors, *monitor);
+
+ /* dump information of each monitor in the new tree */
+ log("");
+ log("--- Report ", _report_id++, " (", _num_monitors, "/", _num_subjects, " subjects) ---");
+ new_monitors.for_each([&] (Monitor &monitor) {
+ monitor.print(_activity, _affinity);
+ });
+ }
+
+ void _destroy_monitor(Monitor_tree &monitors, Monitor &monitor)
+ {
+ if (_verbose)
+ log("destroy monitor: subject ", monitor.subject_id().id);
+
+ try { _trace.free(monitor.subject_id()); }
+ catch (Trace::Nonexistent_subject) { }
+ monitors.remove(&monitor);
+ destroy(_heap, &monitor);
+ _num_monitors--;
+ }
+
+ void _new_monitor(Monitor_tree &monitors,
+ Trace::Subject_id id,
+ Session_policy &session_policy)
+ {
+ try {
+ Number_of_bytes const buffer_sz = session_policy.attribute_value("buffer", _default_buf_sz);
+ Policy_name const policy_name = session_policy.attribute_value("policy", _default_policy_name);
+ try {
+ _trace.trace(id.id, _policies.find_by_name(policy_name).id(), buffer_sz);
+ } catch (Policy_tree::No_match) {
+ Policy &policy = *new (_heap) Policy(_env, _trace, policy_name);
+ _policies.insert(policy);
+ _trace.trace(id.id, policy.id(), buffer_sz);
+ }
+ monitors.insert(new (_heap) Monitor(_trace, _env.rm(), id));
+ }
+ catch (Out_of_ram ) { warning("Cannot activate tracing: Out_of_ram" ); return; }
+ catch (Out_of_caps ) { warning("Cannot activate tracing: Out_of_caps" ); return; }
+ catch (Trace::Already_traced ) { warning("Cannot activate tracing: Already_traced" ); return; }
+ catch (Trace::Source_is_dead ) { warning("Cannot activate tracing: Source_is_dead" ); return; }
+ catch (Trace::Nonexistent_policy ) { warning("Cannot activate tracing: Nonexistent_policy" ); return; }
+ catch (Trace::Traced_by_other_session) { warning("Cannot activate tracing: Traced_by_other_session"); return; }
+ catch (Trace::Nonexistent_subject ) { warning("Cannot activate tracing: Nonexistent_subject" ); return; }
+ catch (Region_map::Invalid_dataspace ) { warning("Cannot activate tracing: Loading policy failed" ); return; }
+
+
+ _num_monitors++;
+ if (_verbose)
+ log("new monitor: subject ", id.id);
+ }
+
+ Session_policy _session_policy(Trace::Subject_id id)
+ {
+ Trace::Subject_info info = _trace.subject_info(id);
+ Session_label const label(info.session_label());
+ Session_policy policy(label, _config);
+ if (policy.has_attribute("thread"))
+ if (policy.attribute_value("thread", Thread_name()) != info.thread_name())
+ throw Session_policy::No_policy_defined();
+
+ return policy;
+ }
+
+ public:
+
+ Main(Env &env) : _env(env) { _policies.insert(_default_policy); }
+};
+
+
+void Component::construct(Env &env) { static Main main(env); }
diff --git a/repos/os/src/app/trace_logger/monitor.cc b/repos/os/src/app/trace_logger/monitor.cc
new file mode 100644
index 0000000000..a65eb6110c
--- /dev/null
+++ b/repos/os/src/app/trace_logger/monitor.cc
@@ -0,0 +1,155 @@
+/*
+ * \brief Monitoring of a trace subject
+ * \author Martin Stein
+ * \date 2018-01-12
+ */
+
+/*
+ * Copyright (C) 2018 Genode Labs GmbH
+ *
+ * This file is part of the Genode OS framework, which is distributed
+ * under the terms of the GNU Affero General Public License version 3.
+ */
+
+/* local includes */
+#include
+
+/* Genode includes */
+#include
+
+using namespace Genode;
+
+
+/******************
+ ** Monitor_base **
+ ******************/
+
+Monitor_base::Monitor_base(Trace::Connection &trace,
+ Region_map &rm,
+ Trace::Subject_id subject_id)
+:
+ _trace(trace), _rm(rm),
+ _buffer_raw(*(Trace::Buffer *)rm.attach(_trace.buffer(subject_id)))
+{ }
+
+
+Monitor_base::~Monitor_base()
+{
+ _rm.detach(&_buffer_raw);
+}
+
+
+/*************
+ ** Monitor **
+ *************/
+
+Monitor &Monitor::find_by_subject_id(Trace::Subject_id const subject_id)
+{
+ if (subject_id.id == _subject_id.id) {
+ return *this; }
+
+ bool const side = subject_id.id > _subject_id.id;
+
+ Monitor *const monitor = Avl_node::child(side);
+ if (!monitor) {
+ throw Monitor_tree::No_match(); }
+
+ return monitor->find_by_subject_id(subject_id);
+}
+
+
+Monitor::Monitor(Trace::Connection &trace,
+ Region_map &rm,
+ Trace::Subject_id subject_id)
+:
+ Monitor_base(trace, rm, subject_id),
+ _subject_id(subject_id), _buffer(_buffer_raw)
+{
+ _update_info();
+}
+
+
+void Monitor::_update_info()
+{
+ try {
+ Trace::Subject_info const &info =
+ _trace.subject_info(_subject_id);
+
+ unsigned long long const last_execution_time =
+ _info.execution_time().value;
+
+ _info = info;
+ _recent_exec_time =
+ _info.execution_time().value - last_execution_time;
+ }
+ catch (Trace::Nonexistent_subject) { warning("Cannot update subject info: Nonexistent_subject"); }
+}
+
+
+void Monitor::print(bool activity, bool affinity)
+{
+ _update_info();
+
+ /* print general subject information */
+ typedef Trace::Subject_info Subject_info;
+ Subject_info::State const state = _info.state();
+ log("");
+
+ /* print subjects activity if desired */
+ if (activity)
+ log(" ");
+
+ /* print subjects affinity if desired */
+ if (affinity)
+ log(" ");
+
+ /* print all buffer entries that we haven't yet printed */
+ bool printed_buf_entries = false;
+ _buffer.for_each_new_entry([&] (Trace::Buffer::Entry entry) {
+
+ /* get readable data length and skip empty entries */
+ size_t length = min(entry.length(), (unsigned)MAX_ENTRY_LENGTH - 1);
+ if (!length)
+ return;
+
+ /* copy entry data from buffer and add terminating '0' */
+ memcpy(_curr_entry_data, entry.data(), length);
+ _curr_entry_data[length] = '\0';
+
+ /* print copied entry data out to log */
+ if (!printed_buf_entries) {
+ log(" ");
+ printed_buf_entries = true;
+ }
+ log(Cstring(_curr_entry_data));
+ });
+ /* print end tags */
+ if (printed_buf_entries)
+ log(" ");
+ else
+ log(" ");
+ log("");
+}
+
+
+/******************
+ ** Monitor_tree **
+ ******************/
+
+Monitor &Monitor_tree::find_by_subject_id(Trace::Subject_id const subject_id)
+{
+ Monitor *const monitor = first();
+
+ if (!monitor)
+ throw No_match();
+
+ return monitor->find_by_subject_id(subject_id);
+}
diff --git a/repos/os/src/app/trace_logger/monitor.h b/repos/os/src/app/trace_logger/monitor.h
new file mode 100644
index 0000000000..e701ecf721
--- /dev/null
+++ b/repos/os/src/app/trace_logger/monitor.h
@@ -0,0 +1,103 @@
+/*
+ * \brief Monitoring of a trace subject
+ * \author Martin Stein
+ * \date 2018-01-12
+ */
+
+/*
+ * Copyright (C) 2018 Genode Labs GmbH
+ *
+ * This file is part of the Genode OS framework, which is distributed
+ * under the terms of the GNU Affero General Public License version 3.
+ */
+
+#ifndef _MONITOR_H_
+#define _MONITOR_H_
+
+/* local includes */
+#include
+#include
+
+/* Genode includes */
+#include
+
+namespace Genode { namespace Trace { class Connection; } }
+
+
+/**
+ * To attach and detach trace-buffer dataspace in the right moments
+ */
+class Monitor_base
+{
+ protected:
+
+ Genode::Trace::Connection &_trace;
+ Genode::Region_map &_rm;
+ Genode::Trace::Buffer &_buffer_raw;
+
+ Monitor_base(Genode::Trace::Connection &trace,
+ Genode::Region_map &rm,
+ Genode::Trace::Subject_id subject_id);
+
+ ~Monitor_base();
+};
+
+
+/**
+ * Monitors tracing information of one tracing subject
+ */
+class Monitor : public Monitor_base,
+ public Local::Avl_node
+{
+ private:
+
+ enum { MAX_ENTRY_LENGTH = 256 };
+
+ Genode::Trace::Subject_id const _subject_id;
+ Trace_buffer _buffer;
+ unsigned long _report_id { 0 };
+ Genode::Trace::Subject_info _info { };
+ unsigned long long _recent_exec_time { 0 };
+ char _curr_entry_data[MAX_ENTRY_LENGTH];
+
+ void _update_info();
+
+ public:
+
+ Monitor(Genode::Trace::Connection &trace,
+ Genode::Region_map &rm,
+ Genode::Trace::Subject_id subject_id);
+
+ void print(bool activity, bool affinity);
+
+
+ /**************
+ ** Avl_node **
+ **************/
+
+ Monitor &find_by_subject_id(Genode::Trace::Subject_id const subject_id);
+
+ bool higher(Monitor *monitor) { return monitor->_subject_id.id > _subject_id.id; }
+
+
+ /***************
+ ** Accessors **
+ ***************/
+
+ Genode::Trace::Subject_id subject_id() const { return _subject_id; }
+ Genode::Trace::Subject_info const &info() const { return _info; }
+};
+
+
+/**
+ * AVL tree of monitors with their subject ID as index
+ */
+struct Monitor_tree : Local::Avl_tree
+{
+ struct No_match : Genode::Exception { };
+
+ Monitor &find_by_subject_id(Genode::Trace::Subject_id const subject_id);
+};
+
+
+#endif /* _MONITOR_H_ */
diff --git a/repos/os/src/app/trace_logger/policy.cc b/repos/os/src/app/trace_logger/policy.cc
new file mode 100644
index 0000000000..ac48619084
--- /dev/null
+++ b/repos/os/src/app/trace_logger/policy.cc
@@ -0,0 +1,68 @@
+/*
+ * \brief Installs and maintains a tracing policy
+ * \author Martin Stein
+ * \date 2018-01-12
+ */
+
+/*
+ * Copyright (C) 2018 Genode Labs GmbH
+ *
+ * This file is part of the Genode OS framework, which is distributed
+ * under the terms of the GNU Affero General Public License version 3.
+ */
+
+/* local includes */
+#include
+
+using namespace Genode;
+
+
+/***********************
+ ** Policy_avl_member **
+ ***********************/
+
+Policy_avl_member::Policy_avl_member(Policy_name const &name,
+ ::Policy &policy)
+:
+ Avl_string_base(name.string()), _policy(policy)
+{ }
+
+
+/************
+ ** Policy **
+ ************/
+
+Policy::Policy(Env &env, Trace::Connection &trace, Policy_name const &name)
+:
+ Policy_base(name), _avl_member(_name, *this), _env(env), _trace(trace)
+{
+ Dataspace_capability dst_ds = _trace.policy(_id);
+ void *dst = _env.rm().attach(dst_ds);
+ void *src = _env.rm().attach(_ds);
+ memcpy(dst, src, _size);
+ _env.rm().detach(dst);
+ _env.rm().detach(src);
+}
+
+
+/*****************
+ ** Policy_tree **
+ *****************/
+
+Policy &Policy_tree::policy(Avl_string_base const &node)
+{
+ return static_cast(&node)->policy();
+}
+
+
+Policy &Policy_tree::find_by_name(Policy_name name)
+{
+ if (name == Policy_name() || !first()) {
+ throw No_match(); }
+
+ Avl_string_base *node = first()->find_by_name(name.string());
+ if (!node) {
+ throw No_match(); }
+
+ return policy(*node);
+}
diff --git a/repos/os/src/app/trace_logger/policy.h b/repos/os/src/app/trace_logger/policy.h
new file mode 100644
index 0000000000..46921a4be8
--- /dev/null
+++ b/repos/os/src/app/trace_logger/policy.h
@@ -0,0 +1,120 @@
+/*
+ * \brief Installs and maintains a tracing policy
+ * \author Martin Stein
+ * \date 2018-01-12
+ */
+
+/*
+ * Copyright (C) 2018 Genode Labs GmbH
+ *
+ * This file is part of the Genode OS framework, which is distributed
+ * under the terms of the GNU Affero General Public License version 3.
+ */
+
+#ifndef _POLICY_H_
+#define _POLICY_H_
+
+/* Genode includes */
+#include
+#include
+#include
+#include
+
+using Policy_name = Genode::String<40>;
+
+class Policy;
+
+
+/**
+ * Member of a policy that allows the policy to be managed in a tree
+ */
+class Policy_avl_member : public Genode::Avl_string_base
+{
+ private:
+
+ ::Policy &_policy;
+
+ public:
+
+ Policy_avl_member(Policy_name const &name,
+ ::Policy &policy);
+
+
+ /***************
+ ** Accessors **
+ ***************/
+
+ ::Policy &policy() const { return _policy; }
+};
+
+
+/**
+ * Ensure that policy name is constructed before it is used as tree index
+ */
+class Policy_base
+{
+ protected:
+
+ Policy_name const _name;
+
+ Policy_base(Policy_name const &name) : _name(name) { }
+};
+
+
+/**
+ * Installs and maintains a tracing policy
+ */
+class Policy : public Policy_base
+{
+ private:
+
+ Policy_avl_member _avl_member;
+ Genode::Env &_env;
+ Genode::Trace::Connection &_trace;
+ Genode::Rom_connection _rom { _env, _name.string() };
+ Genode::Rom_dataspace_capability const _ds { _rom.dataspace() };
+ Genode::size_t const _size { Genode::Dataspace_client(_ds).size() };
+ Genode::Trace::Policy_id const _id { _trace.alloc_policy(_size) };
+
+ public:
+
+
+ Policy(Genode::Env &env,
+ Genode::Trace::Connection &trace,
+ Policy_name const &name);
+
+
+ /***************
+ ** Accessors **
+ ***************/
+
+ Genode::Trace::Policy_id id() const { return _id; }
+ Policy_avl_member &avl_member() { return _avl_member; }
+};
+
+
+/**
+ * AVL tree of policies with their name as index
+ */
+struct Policy_tree : Genode::Avl_tree
+{
+ using Avl_tree = Genode::Avl_tree;
+
+ struct No_match : Genode::Exception { };
+
+ static ::Policy &policy(Genode::Avl_string_base const &node);
+
+ ::Policy &find_by_name(Policy_name name);
+
+ template
+ void for_each(FUNC && functor) const {
+ Avl_tree::for_each([&] (Genode::Avl_string_base const &node) {
+ functor(policy(node));
+ });
+ }
+
+ void insert(::Policy &policy) { Avl_tree::insert(&policy.avl_member()); }
+};
+
+
+#endif /* _POLICY_H_ */
diff --git a/repos/os/src/app/trace_logger/target.mk b/repos/os/src/app/trace_logger/target.mk
new file mode 100644
index 0000000000..788e2a360c
--- /dev/null
+++ b/repos/os/src/app/trace_logger/target.mk
@@ -0,0 +1,5 @@
+TARGET = trace_logger
+INC_DIR += $(PRG_DIR)
+SRC_CC = main.cc monitor.cc policy.cc xml_node.cc
+CONFIG_XSD = config.xsd
+LIBS += base
diff --git a/repos/os/src/app/trace_logger/trace_buffer.h b/repos/os/src/app/trace_logger/trace_buffer.h
new file mode 100644
index 0000000000..45c433e4bb
--- /dev/null
+++ b/repos/os/src/app/trace_logger/trace_buffer.h
@@ -0,0 +1,61 @@
+/*
+ * \brief Wrapper for Trace::Buffer that adds some convenient functionality
+ * \author Martin Stein
+ * \date 2018-01-12
+ */
+
+/*
+ * Copyright (C) 2018 Genode Labs GmbH
+ *
+ * This file is part of the Genode OS framework, which is distributed
+ * under the terms of the GNU Affero General Public License version 3.
+ */
+
+#ifndef _TRACE_BUFFER_H_
+#define _TRACE_BUFFER_H_
+
+/* Genode includes */
+#include
+
+
+/**
+ * Wrapper for Trace::Buffer that adds some convenient functionality
+ */
+class Trace_buffer
+{
+ private:
+
+ Genode::Trace::Buffer &_buffer;
+ Genode::Trace::Buffer::Entry _curr { _buffer.first() };
+
+ public:
+
+ Trace_buffer(Genode::Trace::Buffer &buffer) : _buffer(buffer) { }
+
+ /**
+ * Call functor for each entry that wasn't yet processed
+ */
+ template
+ void for_each_new_entry(FUNC && functor)
+ {
+ using namespace Genode;
+
+ /* initialize _curr if _buffer was empty until now */
+ if (_curr.last())
+ _curr = _buffer.first();
+
+ /* iterate over all entries that were not processed yet */
+ Trace::Buffer::Entry e1 = _curr;
+ for (Trace::Buffer::Entry e2 = _curr; !e2.last();
+ e2 = _buffer.next(e2))
+ {
+ e1 = e2;
+ functor(e1);
+ }
+ /* remember the last processed entry in _curr */
+ _curr = e1;
+ }
+};
+
+
+#endif /* _TRACE_BUFFER_H_ */
diff --git a/repos/os/src/app/trace_logger/xml_node.cc b/repos/os/src/app/trace_logger/xml_node.cc
new file mode 100644
index 0000000000..be7d7b80cb
--- /dev/null
+++ b/repos/os/src/app/trace_logger/xml_node.cc
@@ -0,0 +1,29 @@
+/*
+ * \brief Genode XML nodes plus local utilities
+ * \author Martin Stein
+ * \date 2016-08-19
+ */
+
+/*
+ * Copyright (C) 2016-2017 Genode Labs GmbH
+ *
+ * This file is part of the Genode OS framework, which is distributed
+ * under the terms of the GNU Affero General Public License version 3.
+ */
+
+/* local includes */
+#include
+
+using namespace Genode;
+
+
+Microseconds Genode::read_sec_attr(Xml_node const node,
+ char const *name,
+ unsigned long const default_sec)
+{
+ unsigned long sec = node.attribute_value(name, 0UL);
+ if (!sec) {
+ sec = default_sec;
+ }
+ return Microseconds(sec * 1000 * 1000);
+}
diff --git a/repos/os/src/app/trace_logger/xml_node.h b/repos/os/src/app/trace_logger/xml_node.h
new file mode 100644
index 0000000000..86f3840a62
--- /dev/null
+++ b/repos/os/src/app/trace_logger/xml_node.h
@@ -0,0 +1,29 @@
+/*
+ * \brief Genode XML nodes plus local utilities
+ * \author Martin Stein
+ * \date 2016-08-19
+ */
+
+/*
+ * Copyright (C) 2016-2017 Genode Labs GmbH
+ *
+ * This file is part of the Genode OS framework, which is distributed
+ * under the terms of the GNU Affero General Public License version 3.
+ */
+
+#ifndef _XML_NODE_H_
+#define _XML_NODE_H_
+
+/* Genode includes */
+#include
+#include
+
+
+namespace Genode {
+
+ Microseconds read_sec_attr(Xml_node const node,
+ char const *name,
+ unsigned long const default_sec);
+}
+
+#endif /* _XML_NODE_H_ */
diff --git a/repos/os/src/test/trace_logger/main.cc b/repos/os/src/test/trace_logger/main.cc
new file mode 100644
index 0000000000..af1c5e5745
--- /dev/null
+++ b/repos/os/src/test/trace_logger/main.cc
@@ -0,0 +1,30 @@
+/*
+ * \brief Test functionality of the trace logger
+ * \author Martin Stein
+ * \date 2017-01-23
+ */
+
+/*
+ * Copyright (C) 2017 Genode Labs GmbH
+ *
+ * This file is part of the Genode OS framework, which is distributed
+ * under the terms of the GNU Affero General Public License version 3.
+ */
+
+/* Genode includes */
+#include
+#include
+#include
+
+using namespace Genode;
+
+
+void Component::construct(Genode::Env &env)
+{
+ Timer::Connection timer(env);
+ for (unsigned i = 0; i < 20; i++) {
+ timer.msleep(500);
+ Thread::trace(String<32>(i, " ", Trace::timestamp()).string());
+ }
+ env.parent().exit(0);
+}
diff --git a/repos/os/src/test/trace_logger/target.mk b/repos/os/src/test/trace_logger/target.mk
new file mode 100644
index 0000000000..5febf0e7ef
--- /dev/null
+++ b/repos/os/src/test/trace_logger/target.mk
@@ -0,0 +1,3 @@
+TARGET = test-trace_logger
+SRC_CC = main.cc
+LIBS += base