trace_logger: convenient tracing frontend

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.

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:

! <config verbose="no"
!         session_ram="10M"
!         session_arg_buffer="4K"
!         session_parent_levels="0"
!         period_sec="5"
!         activity="no"
!         affinity="no"
!         default_policy="null"
!         default_buffer="4K">
!
!    <policy label="init -> timer" />
!    <policy label_suffix=" -> ram_fs" />
!    <policy label_prefix="init -> encryption -> "
!            thread="worker"
!            buffer="4K"
!            policy="null" />
! </config>

For more details see os/src/app/trace_logger/README.

Fixes #2654
This commit is contained in:
Martin Stein 2018-01-24 14:28:29 +01:00 committed by Norman Feske
parent 3e6d1b96e7
commit ccc67d6f68
15 changed files with 1207 additions and 0 deletions

View File

@ -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 {
<config prio_levels="2" ld_verbose="yes">
<parent-provides>
<service name="ROM"/>
<service name="IRQ"/>
<service name="IO_MEM"/>
<service name="IO_PORT"/>
<service name="PD"/>
<service name="RM"/>
<service name="CPU"/>
<service name="LOG"/>
<service name="TRACE"/>
</parent-provides>
<default-route>
<any-service> <parent/> <any-child/> </any-service>
</default-route>
<default caps="100"/>}
append_platform_drv_config
append_if [have_spec gpio] config "
<start name=\"[gpio_drv]\">
<resource name=\"RAM\" quantum=\"4M\"/>
<provides><service name=\"Gpio\"/></provides>
<config/>
</start>"
append config {
<start name="timer">
<resource name="RAM" quantum="1M"/>
<provides><service name="Timer"/></provides>
</start>
<start name="trace_logger" >
<resource name="RAM" quantum="100M"/>
<config verbose="yes"
session_ram="10M"
session_parent_levels="1"
session_arg_buffer="64K"
period_sec="3"
activity="yes"
affinity="yes"
default_policy="null"
default_buffer="1K">
<policy label_prefix="init -> cpu_burner"
thread="ep"/>
<policy label="init -> test-trace_logger"
thread="ep"
buffer="1M"
policy="rpc_name"/>
</config>
</start>
<start name="dynamic_rom">
<resource name="RAM" quantum="4M"/>
<provides><service name="ROM"/></provides>
<config verbose="yes">
<rom name="cpu_burner1.config">
<inline description="initial state">
<config percent="5"/>
</inline>
<sleep milliseconds="5000" />
<inline description="50%">
<config percent="50" />
</inline>
<sleep milliseconds="5000" />
</rom>
<rom name="cpu_burner2.config">
<inline description="initial state">
<config percent="5"/>
</inline>
<sleep milliseconds="4800" />
<inline description="100%">
<config percent="70" />
</inline>
<sleep milliseconds="2700" />
</rom>
</config>
</start>
<start name="test-trace_logger" >
<resource name="RAM" quantum="1M"/>
</start>
<start name="cpu_burner.1">
<binary name="cpu_burner"/>
<resource name="RAM" quantum="1M"/>
<configfile name="cpu_burner1.config"/>
<route>
<service name="ROM" label="cpu_burner1.config"> <child name="dynamic_rom"/> </service>
<any-service> <parent/> <any-child/> </any-service>
</route>
</start>
<start name="cpu_burner.2">
<binary name="cpu_burner"/>
<resource name="RAM" quantum="1M"/>
<configfile name="cpu_burner2.config"/>
<route>
<service name="ROM" label="cpu_burner2.config"> <child name="dynamic_rom"/> </service>
<any-service> <parent/> <any-child/> </any-service>
</route>
</start>
</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

View File

@ -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:
! <config verbose="no"
! session_ram="10M"
! session_arg_buffer="4K"
! session_parent_levels="0"
! period_sec="5"
! activity="no"
! affinity="no"
! default_policy="null"
! default_buffer="4K">
!
! <policy label="init -> timer" />
! <policy label_suffix=" -> ram_fs" />
! <policy label_prefix="init -> encryption -> "
! thread="worker"
! buffer="4K"
! policy="null" />
! </config>
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.

View File

@ -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 <util/avl_tree.h>
namespace Local {
template <typename> class Avl_node;
template <typename> class Avl_tree;
}
/**
* AVL-node wrapper for additional functionality
*/
template <typename NT>
struct Local::Avl_node : Genode::Avl_node<NT>
{
using Base = Genode::Avl_node<NT>;
template <typename FUNC>
void for_each(FUNC && functor)
{
if (NT * l = Base::child(Avl_node<NT>::LEFT)) l->for_each(functor);
functor(*static_cast<NT *>(this));
if (NT * r = Base::child(Avl_node<NT>::RIGHT)) r->for_each(functor);
}
};
/**
* AVL-tree wrapper for additional functionality
*/
template <typename NT>
struct Local::Avl_tree : Genode::Avl_tree<NT>
{
using Base = Genode::Avl_tree<NT>;
template <typename FUNC>
void for_each(FUNC && functor) {
if (Base::first()) Base::first()->for_each(functor); }
};
#endif /* _AVL_TREE_H_ */

View File

@ -0,0 +1,77 @@
<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:simpleType name="Boolean">
<xs:restriction base="xs:string">
<xs:enumeration value="true" />
<xs:enumeration value="yes" />
<xs:enumeration value="1" />
<xs:enumeration value="false" />
<xs:enumeration value="no" />
<xs:enumeration value="0" />
</xs:restriction>
</xs:simpleType><!-- Boolean -->
<xs:simpleType name="Seconds">
<xs:restriction base="xs:integer">
<xs:minInclusive value="1"/>
<xs:maxInclusive value="3600"/>
</xs:restriction>
</xs:simpleType><!-- Seconds -->
<xs:simpleType name="Number_of_bytes">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
</xs:restriction>
</xs:simpleType><!-- Thread_name -->
<xs:simpleType name="Thread_name">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:maxLength value="40"/>
</xs:restriction>
</xs:simpleType><!-- Thread_name -->
<xs:simpleType name="Trace_policy_name">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:maxLength value="40"/>
</xs:restriction>
</xs:simpleType><!-- Trace_policy_name -->
<xs:simpleType name="Session_label">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:maxLength value="160"/>
</xs:restriction>
</xs:simpleType><!-- Session_label -->
<xs:element name="config">
<xs:complexType>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="policy">
<xs:complexType>
<xs:attribute name="label_prefix" type="Session_label" />
<xs:attribute name="label_suffix" type="Session_label" />
<xs:attribute name="label" type="Session_label" />
<xs:attribute name="thread" type="Thread_name" />
<xs:attribute name="buffer" type="Number_of_bytes" />
<xs:attribute name="policy" type="Trace_policy_name" />
</xs:complexType>
</xs:element><!-- policy -->
</xs:choice>
<xs:attribute name="verbose" type="Boolean" />
<xs:attribute name="activity" type="Boolean" />
<xs:attribute name="affinity" type="Boolean" />
<xs:attribute name="session_arg_buffer" type="Number_of_bytes" />
<xs:attribute name="session_ram" type="Number_of_bytes" />
<xs:attribute name="session_parent_levels" type="xs:nonNegativeInteger" />
<xs:attribute name="default_policy" type="Trace_policy_name" />
<xs:attribute name="period_sec" type="Seconds" />
<xs:attribute name="default_buffer" type="Number_of_bytes" />
</xs:complexType>
</xs:element><!-- config -->
</xs:schema>

View File

@ -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 <policy.h>
#include <monitor.h>
#include <xml_node.h>
/* Genode includes */
#include <base/component.h>
#include <base/attached_rom_dataspace.h>
#include <base/heap.h>
#include <os/session_policy.h>
#include <timer_session/connection.h>
#include <util/construct_at.h>
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<Main> _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); }

View File

@ -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 <monitor.h>
/* Genode includes */
#include <trace_session/connection.h>
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<Monitor>::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("<subject label=\"", _info.session_label().string(),
"\" thread=\"", _info.thread_name().string(),
"\" id=\"", _subject_id.id,
"\" state=\"", Subject_info::state_name(state),
"\">");
/* print subjects activity if desired */
if (activity)
log(" <activity total=\"", _info.execution_time().value,
"\" recent=\"", _recent_exec_time,
"\">");
/* print subjects affinity if desired */
if (affinity)
log(" <affinity xpos=\"", _info.affinity().xpos(),
"\" ypos=\"", _info.affinity().ypos(),
"\">");
/* 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(" <buffer>");
printed_buf_entries = true;
}
log(Cstring(_curr_entry_data));
});
/* print end tags */
if (printed_buf_entries)
log(" </buffer>");
else
log(" <buffer />");
log("</subject>");
}
/******************
** 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);
}

View File

@ -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 <avl_tree.h>
#include <trace_buffer.h>
/* Genode includes */
#include <base/trace/types.h>
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<Monitor>
{
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<Monitor>
{
struct No_match : Genode::Exception { };
Monitor &find_by_subject_id(Genode::Trace::Subject_id const subject_id);
};
#endif /* _MONITOR_H_ */

View File

@ -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 <policy.h>
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<Policy_avl_member const *>(&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);
}

View File

@ -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 <rom_session/connection.h>
#include <trace_session/connection.h>
#include <dataspace/client.h>
#include <util/avl_string.h>
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<Genode::Avl_string_base>
{
using Avl_tree = Genode::Avl_tree<Genode::Avl_string_base>;
struct No_match : Genode::Exception { };
static ::Policy &policy(Genode::Avl_string_base const &node);
::Policy &find_by_name(Policy_name name);
template <typename FUNC>
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_ */

View File

@ -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

View File

@ -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 <base/trace/buffer.h>
/**
* 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 <typename FUNC>
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_ */

View File

@ -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 <xml_node.h>
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);
}

View File

@ -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 <util/xml_node.h>
#include <os/duration.h>
namespace Genode {
Microseconds read_sec_attr(Xml_node const node,
char const *name,
unsigned long const default_sec);
}
#endif /* _XML_NODE_H_ */

View File

@ -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 <base/component.h>
#include <timer_session/connection.h>
#include <trace/timestamp.h>
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);
}

View File

@ -0,0 +1,3 @@
TARGET = test-trace_logger
SRC_CC = main.cc
LIBS += base