mirror of
https://github.com/genodelabs/genode.git
synced 2025-01-18 18:56:29 +00:00
parent
53f576e50e
commit
ef742001e8
1
repos/gems/recipes/pkg/trace_recorder/README
Normal file
1
repos/gems/recipes/pkg/trace_recorder/README
Normal file
@ -0,0 +1 @@
|
||||
Trace recorder for collecting continuous traces in file system.
|
4
repos/gems/recipes/pkg/trace_recorder/archives
Normal file
4
repos/gems/recipes/pkg/trace_recorder/archives
Normal file
@ -0,0 +1,4 @@
|
||||
_/src/trace_recorder_policy
|
||||
_/src/trace_recorder
|
||||
_/raw/trace_recorder
|
||||
_/src/vfs
|
1
repos/gems/recipes/pkg/trace_recorder/hash
Normal file
1
repos/gems/recipes/pkg/trace_recorder/hash
Normal file
@ -0,0 +1 @@
|
||||
2022-05-11 b0ff8e7876af3b8bd5fcf7d4055290d029e29198
|
28
repos/gems/recipes/pkg/trace_recorder/runtime
Normal file
28
repos/gems/recipes/pkg/trace_recorder/runtime
Normal file
@ -0,0 +1,28 @@
|
||||
<runtime ram="48M" caps="300" binary="trace_recorder">
|
||||
|
||||
<requires>
|
||||
<file_system label="target"/>
|
||||
<trace/>
|
||||
<rtc/>
|
||||
<rom label="platform_info"/>
|
||||
</requires>
|
||||
|
||||
<content>
|
||||
<rom label="trace_recorder"/>
|
||||
<rom label="ld.lib.so"/>
|
||||
<rom label="vfs.lib.so"/>
|
||||
<rom label="ctf0"/>
|
||||
<rom label="pcapng"/>
|
||||
<rom label="metadata"/>
|
||||
</content>
|
||||
|
||||
<!-- example, must be refined and enabled -->
|
||||
<config period_ms="5000" enable="no">
|
||||
<vfs> <fs label="target"/> </vfs>
|
||||
|
||||
<default-policy policy="ctf0">
|
||||
<ctf/>
|
||||
</default-policy>
|
||||
</config>
|
||||
|
||||
</runtime>
|
4
repos/gems/recipes/raw/trace_recorder/content.mk
Normal file
4
repos/gems/recipes/raw/trace_recorder/content.mk
Normal file
@ -0,0 +1,4 @@
|
||||
content: metadata
|
||||
|
||||
metadata:
|
||||
cp $(REP_DIR)/src/app/trace_recorder/ctf/$@ $@
|
1
repos/gems/recipes/raw/trace_recorder/hash
Normal file
1
repos/gems/recipes/raw/trace_recorder/hash
Normal file
@ -0,0 +1 @@
|
||||
2022-05-11 5e78bddea021b50315ea907a1cd003162439da22
|
9
repos/gems/recipes/src/trace_recorder/content.mk
Normal file
9
repos/gems/recipes/src/trace_recorder/content.mk
Normal file
@ -0,0 +1,9 @@
|
||||
MIRROR_FROM_REP_DIR := src/app/trace_recorder
|
||||
|
||||
content: $(MIRROR_FROM_REP_DIR) LICENSE
|
||||
|
||||
$(MIRROR_FROM_REP_DIR):
|
||||
$(mirror_from_rep_dir)
|
||||
|
||||
LICENSE:
|
||||
cp $(GENODE_DIR)/LICENSE $@
|
1
repos/gems/recipes/src/trace_recorder/hash
Normal file
1
repos/gems/recipes/src/trace_recorder/hash
Normal file
@ -0,0 +1 @@
|
||||
2022-05-11 2cc9c248476c5b323740496cc1da0bf4fdbae1f9
|
8
repos/gems/recipes/src/trace_recorder/used_apis
Normal file
8
repos/gems/recipes/src/trace_recorder/used_apis
Normal file
@ -0,0 +1,8 @@
|
||||
base
|
||||
os
|
||||
so
|
||||
vfs
|
||||
ctf
|
||||
rtc_session
|
||||
trace
|
||||
trace_recorder_policy
|
2
repos/gems/recipes/src/trace_recorder_policy/content.mk
Normal file
2
repos/gems/recipes/src/trace_recorder_policy/content.mk
Normal file
@ -0,0 +1,2 @@
|
||||
SRC_DIR = src/lib/trace_recorder/policy
|
||||
include $(GENODE_DIR)/repos/base/recipes/src/content.inc
|
1
repos/gems/recipes/src/trace_recorder_policy/hash
Normal file
1
repos/gems/recipes/src/trace_recorder_policy/hash
Normal file
@ -0,0 +1 @@
|
||||
2022-05-17 2c7fca33754a0cb26bc494a276cb285cdb6650d7
|
5
repos/gems/recipes/src/trace_recorder_policy/used_apis
Normal file
5
repos/gems/recipes/src/trace_recorder_policy/used_apis
Normal file
@ -0,0 +1,5 @@
|
||||
base
|
||||
os
|
||||
ctf
|
||||
trace
|
||||
trace_recorder_policy
|
175
repos/gems/run/trace_recorder_ctf.run
Normal file
175
repos/gems/run/trace_recorder_ctf.run
Normal file
@ -0,0 +1,175 @@
|
||||
assert_spec linux
|
||||
|
||||
# check that babeltrace2 is present
|
||||
set babeltrace_missing [catch {
|
||||
spawn babeltrace2 -V
|
||||
expect {
|
||||
{Babeltrace 2.*} { }
|
||||
eof { return }
|
||||
timeout { return }
|
||||
}
|
||||
}]
|
||||
|
||||
if {$babeltrace_missing} {
|
||||
puts "\nPlease install babeltrace2 on your host system."
|
||||
exit 1;
|
||||
}
|
||||
|
||||
build { server/lx_fs }
|
||||
|
||||
create_boot_directory
|
||||
|
||||
import_from_depot \
|
||||
[depot_user]/src/[base_src] \
|
||||
[depot_user]/src/init \
|
||||
[depot_user]/src/libc \
|
||||
[depot_user]/src/rom_logger \
|
||||
[depot_user]/src/report_rom \
|
||||
[depot_user]/src/vfs \
|
||||
[depot_user]/src/dummy_rtc_drv \
|
||||
[depot_user]/src/trace_recorder \
|
||||
[depot_user]/raw/trace_recorder \
|
||||
[depot_user]/src/trace_recorder_policy \
|
||||
[depot_user]/src/dynamic_rom
|
||||
|
||||
install_config {
|
||||
<config verbose="yes">
|
||||
<parent-provides>
|
||||
<service name="ROM"/>
|
||||
<service name="LOG"/>
|
||||
<service name="RM"/>
|
||||
<service name="CPU"/>
|
||||
<service name="PD"/>
|
||||
<service name="IRQ"/>
|
||||
<service name="TRACE"/>
|
||||
</parent-provides>
|
||||
|
||||
<default-route>
|
||||
<service name="File_system"> <child name="vfs"/> </service>
|
||||
<any-service> <parent/> <any-child/> </any-service>
|
||||
</default-route>
|
||||
<default caps="100"/>
|
||||
|
||||
<start name="timer">
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<provides><service name="Timer"/></provides>
|
||||
</start>
|
||||
|
||||
<start name="dynamic_rom">
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<provides><service name="ROM"/></provides>
|
||||
<config>
|
||||
<rom name="test">
|
||||
<inline>
|
||||
<foo/>
|
||||
</inline>
|
||||
<sleep milliseconds="1000"/>
|
||||
<inline>
|
||||
<bar/>
|
||||
</inline>
|
||||
<sleep milliseconds="1000"/>
|
||||
<inline>
|
||||
<baz/>
|
||||
</inline>
|
||||
<sleep milliseconds="1000"/>
|
||||
<inline>
|
||||
<foo/>
|
||||
</inline>
|
||||
<sleep milliseconds="1000"/>
|
||||
<inline>
|
||||
<bar/>
|
||||
</inline>
|
||||
<sleep milliseconds="1000"/>
|
||||
<inline>
|
||||
<foo/>
|
||||
</inline>
|
||||
<sleep milliseconds="1000"/>
|
||||
<inline>
|
||||
<bar/>
|
||||
</inline>
|
||||
<sleep milliseconds="1000"/>
|
||||
<inline>
|
||||
<foo/>
|
||||
</inline>
|
||||
<sleep milliseconds="1000"/>
|
||||
<inline>
|
||||
<bar/>
|
||||
</inline>
|
||||
<sleep milliseconds="1000"/>
|
||||
<inline>
|
||||
<end/>
|
||||
</inline>
|
||||
<sleep milliseconds="1000"/>
|
||||
</rom>
|
||||
</config>
|
||||
</start>
|
||||
|
||||
<start name="rom_logger">
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<config rom="test"/>
|
||||
<route>
|
||||
<service name="ROM" label="test"> <child name="dynamic_rom"/> </service>
|
||||
<any-service> <parent/> </any-service>
|
||||
</route>
|
||||
</start>
|
||||
|
||||
<start name="dummy_rtc_drv">
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<provides> <service name="Rtc"/> </provides>
|
||||
</start>
|
||||
|
||||
<start name="lx_fs" ld="no">
|
||||
<resource name="RAM" quantum="4M"/>
|
||||
<provides> <service name="File_system"/> </provides>
|
||||
<config>
|
||||
<default-policy root="/fs" writeable="yes"/>
|
||||
</config>
|
||||
</start>
|
||||
|
||||
<start name="report_rom">
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<provides>
|
||||
<service name="Report"/>
|
||||
<service name="ROM"/>
|
||||
</provides>
|
||||
<config verbose="yes">
|
||||
</config>
|
||||
</start>
|
||||
|
||||
<start name="trace_recorder" caps="200">
|
||||
<resource name="RAM" quantum="16M"/>
|
||||
<config period_ms="5000" enable="yes">
|
||||
<vfs> <fs/> </vfs>
|
||||
<policy label_suffix="dynamic_rom" thread="dynamic_rom" policy="ctf0">
|
||||
<ctf/>
|
||||
</policy>
|
||||
<policy label_suffix="rom_logger" policy="ctf0">
|
||||
<ctf/>
|
||||
</policy>
|
||||
</config>
|
||||
<route>
|
||||
<service name="File_system"> <child name="lx_fs"/> </service>
|
||||
<any-service> <parent/> <any-child/> </any-service>
|
||||
</route>
|
||||
</start>
|
||||
|
||||
</config>
|
||||
}
|
||||
|
||||
exec rm -rf bin/fs
|
||||
exec mkdir -p bin/fs
|
||||
|
||||
build_boot_image { lx_fs
|
||||
fs }
|
||||
|
||||
append qemu_args " -nographic "
|
||||
|
||||
run_genode_until {Enabled ctf writer for init -> rom_logger -> ep} 20
|
||||
set spawn_id [output_spawn_id]
|
||||
run_genode_until {<end/>} 60 $spawn_id
|
||||
|
||||
# trace file has non-zero size
|
||||
exec test -s bin/fs/0-0-0\ 0\:0\:0/init/rom_logger/ep
|
||||
|
||||
# check generated trace by reading CTF trace as fast as possible using a dummy output
|
||||
exec babeltrace2 bin/fs/0-0-0\ 0\:0\:0/init/rom_logger --output-format=dummy
|
163
repos/gems/run/trace_recorder_pcapng.run
Normal file
163
repos/gems/run/trace_recorder_pcapng.run
Normal file
@ -0,0 +1,163 @@
|
||||
assert_spec linux
|
||||
|
||||
# check that python-pcapng is present
|
||||
set python_pcapng_missing [catch {
|
||||
spawn -noecho sh -c "echo \"import pcapng\" | python"
|
||||
expect {
|
||||
{No module} { return }
|
||||
{not found} { return }
|
||||
eof { }
|
||||
}
|
||||
}]
|
||||
|
||||
if {$python_pcapng_missing} {
|
||||
puts "\nPlease install python-pcapng on your host system."
|
||||
exit 1;
|
||||
}
|
||||
|
||||
build { server/lx_fs app/ping }
|
||||
|
||||
create_boot_directory
|
||||
|
||||
import_from_depot \
|
||||
[depot_user]/src/[base_src] \
|
||||
[depot_user]/src/init \
|
||||
[depot_user]/src/libc \
|
||||
[depot_user]/src/nic_router \
|
||||
[depot_user]/src/vfs \
|
||||
[depot_user]/src/linux_rtc_drv \
|
||||
[depot_user]/src/trace_recorder \
|
||||
[depot_user]/raw/trace_recorder \
|
||||
[depot_user]/src/trace_recorder_policy \
|
||||
[depot_user]/src/dynamic_rom
|
||||
|
||||
install_config {
|
||||
<config verbose="yes">
|
||||
<parent-provides>
|
||||
<service name="ROM"/>
|
||||
<service name="LOG"/>
|
||||
<service name="RM"/>
|
||||
<service name="CPU"/>
|
||||
<service name="PD"/>
|
||||
<service name="IRQ"/>
|
||||
<service name="TRACE"/>
|
||||
</parent-provides>
|
||||
|
||||
<default-route>
|
||||
<service name="File_system"> <child name="vfs"/> </service>
|
||||
<any-service> <parent/> <any-child/> </any-service>
|
||||
</default-route>
|
||||
<default caps="100"/>
|
||||
|
||||
<start name="timer">
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<provides><service name="Timer"/></provides>
|
||||
</start>
|
||||
|
||||
<start name="linux_rtc_drv" ld="no">
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<provides> <service name="Rtc"/> </provides>
|
||||
</start>
|
||||
|
||||
<start name="lx_fs" ld="no">
|
||||
<resource name="RAM" quantum="4M"/>
|
||||
<provides> <service name="File_system"/> </provides>
|
||||
<config>
|
||||
<default-policy root="/fs" writeable="yes"/>
|
||||
</config>
|
||||
</start>
|
||||
|
||||
<start name="nic_router">
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<provides>
|
||||
<service name="Nic"/>
|
||||
<service name="Uplink"/>
|
||||
</provides>
|
||||
<config icmp_echo_server="yes" trace_packets="yes">
|
||||
<default-policy domain="default"/>
|
||||
|
||||
<domain name="default" interface="10.0.4.1/24">
|
||||
<dhcp-server ip_first="10.0.4.2" ip_last="10.0.4.10"/>
|
||||
</domain>
|
||||
</config>
|
||||
</start>
|
||||
|
||||
<start name="ping">
|
||||
<resource name="RAM" quantum="10M"/>
|
||||
<config dst_ip="10.0.4.1" period_sec="1" count="10" verbose="yes"/>
|
||||
</start>
|
||||
|
||||
<!-- using dynamic_rom to delay enabling of trace_recorder -->
|
||||
<start name="dynamic_rom">
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<provides><service name="ROM"/></provides>
|
||||
<config>
|
||||
<rom name="config">
|
||||
<inline>
|
||||
<config/>
|
||||
</inline>
|
||||
<sleep milliseconds="1000"/>
|
||||
<inline>
|
||||
<config period_ms="3000" enable="yes">
|
||||
<vfs> <fs/> </vfs>
|
||||
<policy label_suffix="nic_router" thread="ep" policy="pcapng">
|
||||
<pcapng/>
|
||||
</policy>
|
||||
</config>
|
||||
</inline>
|
||||
<sleep milliseconds="5000"/>
|
||||
<inline>
|
||||
<config/>
|
||||
</inline>
|
||||
<sleep milliseconds="10000"/>
|
||||
</rom>
|
||||
</config>
|
||||
</start>
|
||||
|
||||
<start name="trace_recorder" caps="200">
|
||||
<resource name="RAM" quantum="16M"/>
|
||||
<route>
|
||||
<service name="File_system"> <child name="lx_fs"/> </service>
|
||||
<service name="ROM" label="config"> <child name="dynamic_rom"/> </service>
|
||||
<any-service> <parent/> <any-child/> </any-service>
|
||||
</route>
|
||||
</start>
|
||||
|
||||
</config>
|
||||
}
|
||||
|
||||
exec rm -rf bin/fs
|
||||
exec mkdir -p bin/fs/
|
||||
|
||||
build_boot_image { lx_fs
|
||||
fs
|
||||
ping }
|
||||
|
||||
append qemu_args " -nographic "
|
||||
|
||||
run_genode_until {Enabled pcapng writer for init -> nic_router -> ep} 15
|
||||
set spawn_id [output_spawn_id]
|
||||
|
||||
run_genode_until {.*child "ping" exited with exit value 0.*} 60 $spawn_id
|
||||
|
||||
set pcap_file [exec find bin/fs -name nic_router.pcapng]
|
||||
|
||||
# trace file has non-zero size?
|
||||
exec test -s $pcap_file
|
||||
|
||||
# create python script for pcapng parsing
|
||||
set fd [open [run_dir]/genode/check_pcapng.py "w"]
|
||||
puts $fd {
|
||||
import sys
|
||||
import pcapng
|
||||
|
||||
with open(sys.argv[1], "rb") as fp:
|
||||
scanner = pcapng.FileScanner(fp)
|
||||
for block in pcapng.FileScanner(fp):
|
||||
pass
|
||||
}
|
||||
close $fd
|
||||
|
||||
# check generated trace by python script
|
||||
exec python [run_dir]/genode/check_pcapng.py $pcap_file
|
||||
|
56
repos/gems/src/app/trace_recorder/README
Normal file
56
repos/gems/src/app/trace_recorder/README
Normal file
@ -0,0 +1,56 @@
|
||||
The trace recorder uses Genode's trace session to insert trace policies and
|
||||
periodically process the trace buffers to record continuous traces in a file
|
||||
system. The trace recorder comprises multiple backends that produce different
|
||||
output formats.
|
||||
|
||||
An examplary configuration is shown below:
|
||||
|
||||
! <config period_ms="5000" target_root="/" enable="true">
|
||||
! <vfs> <fs/> </vfs>
|
||||
! <policy label_suffix="dynamic_rom" policy="ctf0">
|
||||
! <ctf/>
|
||||
! </policy>
|
||||
! </config>
|
||||
|
||||
The mandatory argument 'period_ms' specifies the trace-buffer sampling period
|
||||
in milliseconds. The 'enable' attribute activates trace recording.
|
||||
Whenever the 'enable' attribute is toggled from "no" to "yes", a new directory
|
||||
is created (using the real-time clock) to record a new set of traces.
|
||||
|
||||
The '<config>' node can contain an arbitray number of '<policy>' nodes by which
|
||||
the plugin determines what components and threads are traced.
|
||||
The specified 'label_suffix', 'label_prefix' and/or 'label' attributes are
|
||||
matched against the component labels. By default, all threads of the matching
|
||||
component(s) will be traced. The mandatory 'policy' attribute specifies the name
|
||||
of the trace policy to be applied to the matching threads.
|
||||
|
||||
Every '<policy>' node must contain at least one sub-node specifying what
|
||||
backend(s) shall be used for trace output. Currently, the following backends are
|
||||
available:
|
||||
|
||||
:'ctf':
|
||||
Produces CTF (common trace format) traces of component interactions
|
||||
and checkpoints. These traces can be processed with babeltrace or
|
||||
visualised with TraceCompass. Note that the ctf backend opens a ROM
|
||||
session "metadata" that is used as a blueprint for the
|
||||
[https://diamon.org/ctf/ - CTF metadata file] created with the trace
|
||||
files. Currently, only the frequency of the specified clock is adapted.
|
||||
The metadata file is required for deserialisation of trace data.
|
||||
|
||||
:'pcapng':
|
||||
Captures packets (e.g. Ethernet packets) in a pcapng file that can be read
|
||||
by wireshark, for instance.
|
||||
|
||||
|
||||
The '<config>' node may take the following optional attributes:
|
||||
|
||||
:'enable': Enables/starts tracing (default: 'no')
|
||||
|
||||
:'target_root': Sets the target root directory for trace output.
|
||||
|
||||
|
||||
Furthermore, the '<policy>' nodes may take the following optional attributes:
|
||||
|
||||
:'thread': Restricts the tracing to a certain thread of the matching component(s).
|
||||
|
||||
:'buffer': Sets the size of the trace buffer (default: '64K').
|
57
repos/gems/src/app/trace_recorder/backend.h
Normal file
57
repos/gems/src/app/trace_recorder/backend.h
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* \brief Factory base for creating writers
|
||||
* \author Johannes Schlatow
|
||||
* \date 2022-05-11
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2022 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 _BACKEND_H_
|
||||
#define _BACKEND_H_
|
||||
|
||||
/* local includes */
|
||||
#include <named_registry.h>
|
||||
#include <writer.h>
|
||||
|
||||
namespace Trace_recorder {
|
||||
class Backend_base;
|
||||
|
||||
using Backends = Named_registry<Backend_base>;
|
||||
}
|
||||
|
||||
|
||||
class Trace_recorder::Backend_base : Backends::Element
|
||||
{
|
||||
protected:
|
||||
friend class Backends::Element;
|
||||
friend class Avl_node<Backend_base>;
|
||||
friend class Avl_tree<Backend_base>;
|
||||
|
||||
public:
|
||||
|
||||
using Name = Backends::Element::Name;
|
||||
using Backends::Element::name;
|
||||
using Backends::Element::Element;
|
||||
|
||||
Backend_base(Backends & registry, Name const &name)
|
||||
: Backends::Element(registry, name)
|
||||
{ }
|
||||
|
||||
virtual ~Backend_base() { }
|
||||
|
||||
/***************
|
||||
** Interface **
|
||||
***************/
|
||||
|
||||
virtual Writer_base &create_writer(Genode::Allocator &,
|
||||
Genode::Registry<Writer_base> &,
|
||||
Directory &,
|
||||
Directory::Path const &) = 0;
|
||||
};
|
||||
|
||||
#endif /* _BACKEND_H_ */
|
59
repos/gems/src/app/trace_recorder/config.xsd
Normal file
59
repos/gems/src/app/trace_recorder/config.xsd
Normal file
@ -0,0 +1,59 @@
|
||||
<?xml version="1.0"?>
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||
|
||||
<xs:include schemaLocation="base_types.xsd"/>
|
||||
<xs:include schemaLocation="timeout_types.xsd"/>
|
||||
|
||||
<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="Path">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:minLength value="1"/>
|
||||
<xs:maxLength value="256"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType><!-- Path -->
|
||||
|
||||
<xs:element name="config">
|
||||
<xs:complexType>
|
||||
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||
|
||||
<xs:element name="vfs"/>
|
||||
|
||||
<xs:element name="default-policy">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="thread" type="Thread_name" />
|
||||
<xs:attribute name="buffer" type="Number_of_bytes" />
|
||||
<xs:attribute name="policy" type="Trace_policy_name" use="required"/>
|
||||
</xs:complexType>
|
||||
</xs:element><!-- default-policy -->
|
||||
|
||||
<xs:element name="policy">
|
||||
<xs:complexType>
|
||||
<xs:complexContent>
|
||||
<xs:extension base="Session_policy">
|
||||
<xs:choice minOccurs="1" maxOccurs="unbounded">
|
||||
<xs:element name="ctf"/>
|
||||
<xs:element name="log"/>
|
||||
<xs:element name="pcapng"/>
|
||||
</xs:choice>
|
||||
<xs:attribute name="thread" type="Thread_name" />
|
||||
<xs:attribute name="buffer" type="Number_of_bytes" />
|
||||
<xs:attribute name="policy" type="Trace_policy_name" use="required"/>
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
</xs:element><!-- policy -->
|
||||
|
||||
</xs:choice>
|
||||
<xs:attribute name="period_ms" type="Seconds" use="required"/>
|
||||
<xs:attribute name="target_root" type="Path"/>
|
||||
<xs:attribute name="enable" type="Boolean" />
|
||||
</xs:complexType>
|
||||
</xs:element><!-- config -->
|
||||
|
||||
</xs:schema>
|
59
repos/gems/src/app/trace_recorder/ctf/backend.cc
Normal file
59
repos/gems/src/app/trace_recorder/ctf/backend.cc
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* \brief CTF backend
|
||||
* \author Johannes Schlatow
|
||||
* \date 2022-05-11
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2022 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 <ctf/backend.h>
|
||||
|
||||
using namespace Ctf;
|
||||
|
||||
void Writer::start_iteration(Directory &root,
|
||||
Directory::Path const &path,
|
||||
::Subject_info const &info)
|
||||
{
|
||||
_file_path = Directory::join(path, info.thread_name());
|
||||
|
||||
try {
|
||||
_dst_file.construct(root, _file_path, true);
|
||||
|
||||
/* initialise packet header */
|
||||
_packet_buffer.init_header(info);
|
||||
}
|
||||
catch (New_file::Create_failed) {
|
||||
error("Could not create file."); }
|
||||
}
|
||||
|
||||
void Writer::process_event(Trace_recorder::Trace_event_base const &trace_event, size_t length)
|
||||
{
|
||||
if (!_dst_file.constructed()) return;
|
||||
|
||||
if (trace_event.type() != Trace_recorder::Event_type::CTF) return;
|
||||
|
||||
try {
|
||||
/* write to file if buffer is full */
|
||||
if (_packet_buffer.bytes_remaining() < length)
|
||||
_packet_buffer.write_to_file(*_dst_file, _file_path);
|
||||
|
||||
_packet_buffer.add_event(trace_event.event<Trace_recorder::Ctf_event>(), length - sizeof(Trace_event_base));
|
||||
|
||||
}
|
||||
catch (Buffer::Buffer_too_small) {
|
||||
error("Packet buffer overflow. (Trace buffer wrapped during read?)"); }
|
||||
}
|
||||
|
||||
void Writer::end_iteration()
|
||||
{
|
||||
/* write buffer to file */
|
||||
_packet_buffer.write_to_file(*_dst_file, _file_path);
|
||||
|
||||
_dst_file.destruct();
|
||||
}
|
96
repos/gems/src/app/trace_recorder/ctf/backend.h
Normal file
96
repos/gems/src/app/trace_recorder/ctf/backend.h
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* \brief CTF backend
|
||||
* \author Johannes Schlatow
|
||||
* \date 2021-08-02
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2021 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 _CTF__BACKEND_H_
|
||||
#define _CTF__BACKEND_H_
|
||||
|
||||
/* local includes */
|
||||
#include <ctf/metadata.h>
|
||||
#include <ctf/write_buffer.h>
|
||||
#include <backend.h>
|
||||
#include <timestamp_calibrator.h>
|
||||
|
||||
#include <os/vfs.h>
|
||||
#include <base/attached_rom_dataspace.h>
|
||||
|
||||
namespace Ctf {
|
||||
using namespace Trace_recorder;
|
||||
|
||||
using Genode::Directory;
|
||||
using Genode::New_file;
|
||||
|
||||
using Buffer = Write_buffer<32*1024>;
|
||||
|
||||
class Backend;
|
||||
class Writer;
|
||||
}
|
||||
|
||||
|
||||
class Ctf::Writer : public Trace_recorder::Writer_base
|
||||
{
|
||||
private:
|
||||
Buffer &_packet_buffer;
|
||||
Constructible<New_file> _dst_file { };
|
||||
Directory::Path _file_path { };
|
||||
|
||||
public:
|
||||
Writer(Genode::Registry<Writer_base> ®istry, Buffer &packet_buffer)
|
||||
: Writer_base(registry),
|
||||
_packet_buffer(packet_buffer)
|
||||
{ }
|
||||
|
||||
virtual void start_iteration(Directory &,
|
||||
Directory::Path const &,
|
||||
::Subject_info const &) override;
|
||||
|
||||
virtual void process_event(Trace_recorder::Trace_event_base const &, Genode::size_t) override;
|
||||
|
||||
virtual void end_iteration() override;
|
||||
};
|
||||
|
||||
|
||||
class Ctf::Backend : Trace_recorder::Backend_base
|
||||
{
|
||||
private:
|
||||
|
||||
Attached_rom_dataspace _metadata_rom;
|
||||
Metadata _metadata;
|
||||
|
||||
Buffer _packet_buf { };
|
||||
|
||||
public:
|
||||
|
||||
Backend(Env &env, Timestamp_calibrator const &ts_calibrator, Backends &backends)
|
||||
: Backend_base(backends, "ctf"),
|
||||
_metadata_rom(env, "metadata"),
|
||||
_metadata(_metadata_rom, ts_calibrator.ticks_per_second())
|
||||
{ }
|
||||
|
||||
Writer_base &create_writer(Genode::Allocator &alloc,
|
||||
Genode::Registry<Writer_base> ®istry,
|
||||
Directory &root,
|
||||
Directory::Path const &path) override
|
||||
{
|
||||
/* copy metadata file while adapting clock declaration */
|
||||
Directory::Path metadata_path { Directory::join(path, "metadata") };
|
||||
if (!root.file_exists(metadata_path)) {
|
||||
New_file metadata_file { root, metadata_path };
|
||||
_metadata.write_file(metadata_file);
|
||||
}
|
||||
|
||||
return *new (alloc) Writer(registry, _packet_buf);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif /* _CTF__BACKEND_H_ */
|
121
repos/gems/src/app/trace_recorder/ctf/metadata
Normal file
121
repos/gems/src/app/trace_recorder/ctf/metadata
Normal file
@ -0,0 +1,121 @@
|
||||
/* CTF 1.8 */
|
||||
|
||||
typealias integer { size = 8; align = 8; signed = false; } := uint8_t;
|
||||
typealias uint8_t := char;
|
||||
typealias integer { size = 16; align = 8; signed = false; } := uint16_t;
|
||||
typealias integer { size = 32; align = 8; signed = false; } := uint32_t;
|
||||
typealias integer { size = 64; align = 8; signed = false; } := uint64_t;
|
||||
typealias integer { size = 4; align = 1; signed = false; } := uint4_t;
|
||||
|
||||
trace {
|
||||
major = 1;
|
||||
minor = 8;
|
||||
uuid = "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee";
|
||||
byte_order = le;
|
||||
packet.header := struct {
|
||||
uint32_t magic;
|
||||
uint32_t stream_id;
|
||||
};
|
||||
};
|
||||
|
||||
clock {
|
||||
name = "monotonic";
|
||||
freq = 1000000000; /* Frequency, in Hz (DO NOT REMOVE) */
|
||||
};
|
||||
|
||||
typealias integer {
|
||||
size = 64; align = 8; signed = false;
|
||||
map = clock.monotonic.value;
|
||||
} := uint64_tsc_t;
|
||||
|
||||
struct packet_context {
|
||||
uint64_tsc_t timestamp_begin;
|
||||
uint64_tsc_t timestamp_end;
|
||||
uint32_t packet_size;
|
||||
uint16_t _hdrsz;
|
||||
uint4_t xpos;
|
||||
uint4_t ypos;
|
||||
uint4_t width;
|
||||
uint4_t height;
|
||||
uint8_t priority;
|
||||
string session_label;
|
||||
string thread_name;
|
||||
};
|
||||
|
||||
struct event_header {
|
||||
uint8_t id;
|
||||
uint64_tsc_t timestamp;
|
||||
} align(8);
|
||||
|
||||
stream {
|
||||
id = 0;
|
||||
event.header := struct event_header;
|
||||
packet.context := struct packet_context;
|
||||
};
|
||||
|
||||
event {
|
||||
name = "Rpc_call";
|
||||
id = 1;
|
||||
stream_id = 0;
|
||||
fields := struct {
|
||||
string _name;
|
||||
};
|
||||
};
|
||||
|
||||
event {
|
||||
name = "Rpc_returned";
|
||||
id = 2;
|
||||
stream_id = 0;
|
||||
fields := struct {
|
||||
string _name;
|
||||
};
|
||||
};
|
||||
|
||||
event {
|
||||
name = "Rpc_dispatch";
|
||||
id = 3;
|
||||
stream_id = 0;
|
||||
fields := struct {
|
||||
string _name;
|
||||
};
|
||||
};
|
||||
|
||||
event {
|
||||
name = "Rpc_reply";
|
||||
id = 4;
|
||||
stream_id = 0;
|
||||
fields := struct {
|
||||
string _name;
|
||||
};
|
||||
};
|
||||
|
||||
event {
|
||||
name = "Signal_submit";
|
||||
id = 5;
|
||||
stream_id = 0;
|
||||
fields := struct {
|
||||
uint32_t _number;
|
||||
};
|
||||
};
|
||||
|
||||
event {
|
||||
name = "Signal_receive";
|
||||
id = 6;
|
||||
stream_id = 0;
|
||||
fields := struct {
|
||||
uint32_t _number;
|
||||
uint64_t _context;
|
||||
};
|
||||
};
|
||||
|
||||
event {
|
||||
name = "Checkpoint";
|
||||
id = 7;
|
||||
stream_id = 0;
|
||||
fields := struct {
|
||||
uint32_t _data;
|
||||
uint64_t _addr;
|
||||
uint8_t _type;
|
||||
string _name;
|
||||
};
|
||||
};
|
106
repos/gems/src/app/trace_recorder/ctf/metadata.h
Normal file
106
repos/gems/src/app/trace_recorder/ctf/metadata.h
Normal file
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* \brief Metadata file writer
|
||||
* \author Johannes Schlatow
|
||||
* \date 2021-12-02
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2021 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 _CTF__METADATA_H_
|
||||
#define _CTF__METADATA_H_
|
||||
|
||||
#include <os/vfs.h>
|
||||
#include <base/attached_rom_dataspace.h>
|
||||
#include <base/buffered_output.h>
|
||||
|
||||
namespace Ctf {
|
||||
using namespace Genode;
|
||||
|
||||
class Metadata;
|
||||
}
|
||||
|
||||
class Ctf::Metadata
|
||||
{
|
||||
private:
|
||||
Attached_rom_dataspace &_metadata_rom;
|
||||
uint64_t _timestamp_freq;
|
||||
|
||||
template <typename PROLOGUE, typename EPILOGUE>
|
||||
bool _with_metadata_rom(PROLOGUE && prologue,
|
||||
EPILOGUE && epilogue) const
|
||||
{
|
||||
using Token = Genode::Token<Genode::Scanner_policy_identifier_with_underline>;
|
||||
char const * rom_start = _metadata_rom.local_addr<char>();
|
||||
size_t const rom_size = _metadata_rom.size();
|
||||
|
||||
Token token { rom_start, rom_size };
|
||||
|
||||
for (; token; token = token.next()) {
|
||||
if (token.type() == Token::IDENT) {
|
||||
if (token.matches("freq") && token.len() == 4) {
|
||||
|
||||
prologue(rom_start, token.next().start() - rom_start);
|
||||
|
||||
token = token.next_after("\n");
|
||||
|
||||
/* find null termination */
|
||||
char const * rom_end = token.start();
|
||||
for (; rom_end < rom_start+rom_size && rom_end && *rom_end; rom_end++);
|
||||
|
||||
epilogue(token.start(), rom_end - token.start());
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
error("Error parsing metadata ROM. Could not find 'freq' definition.");
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Metadata(Attached_rom_dataspace & metadata_rom, uint64_t freq)
|
||||
: _metadata_rom(metadata_rom),
|
||||
_timestamp_freq(freq)
|
||||
{ }
|
||||
|
||||
void write_file(Genode::New_file & dst) const
|
||||
{
|
||||
using namespace Genode;
|
||||
|
||||
bool write_error = false;
|
||||
|
||||
auto write = [&] (char const *str)
|
||||
{
|
||||
if (dst.append(str, strlen(str)) != New_file::Append_result::OK)
|
||||
write_error = true;
|
||||
};
|
||||
Buffered_output<32, decltype(write)> output(write);
|
||||
|
||||
_with_metadata_rom(
|
||||
/* prologue, up to 'freq' */
|
||||
[&] (char const * start, size_t len) {
|
||||
if (dst.append(start, len) != New_file::Append_result::OK)
|
||||
write_error = true;
|
||||
|
||||
print(output, " = ", _timestamp_freq, ";\n");
|
||||
},
|
||||
/* epilogue, everything after '\n' */
|
||||
[&] (char const * start, size_t len) {
|
||||
if (dst.append(start, len) != New_file::Append_result::OK)
|
||||
write_error = true;
|
||||
});
|
||||
|
||||
if (write_error)
|
||||
error("Write to 'metadata' failed");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif /* _CTF__METADATA_H_ */
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* \brief Packet header and context types
|
||||
* \brief Packet header
|
||||
* \author Johannes Schlatow
|
||||
* \date 2021-08-04
|
||||
*/
|
||||
@ -11,8 +11,8 @@
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _CTF__PACKET_TYPES_H_
|
||||
#define _CTF__PACKET_TYPES_H_
|
||||
#ifndef _CTF__PACKET_HEADER_H_
|
||||
#define _CTF__PACKET_HEADER_H_
|
||||
|
||||
#include <base/trace/types.h>
|
||||
#include <ctf/timestamp.h>
|
||||
@ -130,4 +130,4 @@ struct Ctf::Packet_header
|
||||
|
||||
} __attribute__((packed));
|
||||
|
||||
#endif /* _CTF__PACKET_TYPES_H_ */
|
||||
#endif /* _CTF__PACKET_HEADER_H_ */
|
82
repos/gems/src/app/trace_recorder/ctf/write_buffer.h
Normal file
82
repos/gems/src/app/trace_recorder/ctf/write_buffer.h
Normal file
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* \brief Convenience helper for creating a CTF packet
|
||||
* \author Johannes Schlatow
|
||||
* \date 2021-08-06
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2021 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 _CTF__WRITE_BUFFER_H_
|
||||
#define _CTF__WRITE_BUFFER_H_
|
||||
|
||||
/* local includes */
|
||||
#include <subject_info.h>
|
||||
#include <ctf/packet_header.h>
|
||||
|
||||
/* Genode includes */
|
||||
#include <os/vfs.h>
|
||||
#include <trace_recorder_policy/ctf.h>
|
||||
|
||||
namespace Ctf {
|
||||
template <unsigned>
|
||||
class Write_buffer;
|
||||
}
|
||||
|
||||
template <unsigned BUFSIZE>
|
||||
class Ctf::Write_buffer
|
||||
{
|
||||
public:
|
||||
struct Buffer_too_small : Genode::Exception { };
|
||||
|
||||
private:
|
||||
char _buffer[BUFSIZE] { };
|
||||
|
||||
Packet_header &_header() { return *(Packet_header*)_buffer; }
|
||||
|
||||
public:
|
||||
|
||||
void init_header(::Subject_info const &info)
|
||||
{
|
||||
construct_at<Packet_header>(_buffer,
|
||||
info.session_label(),
|
||||
info.thread_name(),
|
||||
info.affinity(),
|
||||
info.priority(),
|
||||
BUFSIZE);
|
||||
}
|
||||
|
||||
void add_event(Ctf::Event_header_base const &event, Genode::size_t length)
|
||||
{
|
||||
_header().append_event(_buffer, BUFSIZE,
|
||||
event.timestamp(), length,
|
||||
[&] (char * ptr, Timestamp_base ts)
|
||||
{
|
||||
/* copy event into buffer */
|
||||
memcpy(ptr, &event, length);
|
||||
|
||||
/* update timestamp */
|
||||
((Event_header_base *)ptr)->timestamp(ts);
|
||||
});
|
||||
}
|
||||
|
||||
void write_to_file(Genode::New_file &dst, Genode::Directory::Path const &path)
|
||||
{
|
||||
if (_header().empty())
|
||||
return;
|
||||
|
||||
if (dst.append(_buffer, _header().total_length_bytes()) != New_file::Append_result::OK)
|
||||
error("Write error for ", path);
|
||||
|
||||
_header().reset();
|
||||
}
|
||||
|
||||
Genode::size_t bytes_remaining() { return BUFSIZE - _header().total_length_bytes(); }
|
||||
};
|
||||
|
||||
|
||||
#endif /* _CTF__WRITE_BUFFER_H_ */
|
77
repos/gems/src/app/trace_recorder/main.cc
Normal file
77
repos/gems/src/app/trace_recorder/main.cc
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* \brief Record traces and store in file system
|
||||
* \author Johannes Schlatow
|
||||
* \date 2022-05-09
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2022 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 <base/component.h>
|
||||
#include <base/heap.h>
|
||||
#include <base/attached_rom_dataspace.h>
|
||||
|
||||
namespace Trace_recorder {
|
||||
using namespace Genode;
|
||||
|
||||
struct Main;
|
||||
}
|
||||
|
||||
|
||||
class Trace_recorder::Main
|
||||
{
|
||||
private:
|
||||
Env &_env;
|
||||
|
||||
Heap _heap { _env.ram(), _env.rm() };
|
||||
Monitor _monitor { _env, _heap };
|
||||
|
||||
Attached_rom_dataspace _config_rom { _env, "config" };
|
||||
|
||||
Signal_handler<Main> _config_handler { _env.ep(), *this, &Main::_handle_config };
|
||||
|
||||
bool _enabled { false };
|
||||
|
||||
void _handle_config();
|
||||
|
||||
public:
|
||||
|
||||
Main(Env & env)
|
||||
: _env(env)
|
||||
{
|
||||
_config_rom.sigh(_config_handler);
|
||||
|
||||
_handle_config();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void Trace_recorder::Main::_handle_config()
|
||||
{
|
||||
_config_rom.update();
|
||||
|
||||
bool old_enabled { _enabled };
|
||||
|
||||
_enabled = _config_rom.xml().attribute_value("enable", false);
|
||||
|
||||
if (old_enabled == _enabled) {
|
||||
warning("Config update postponed. Need to toggle 'enable' attribute.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_enabled)
|
||||
_monitor.start(_config_rom.xml());
|
||||
else
|
||||
_monitor.stop();
|
||||
}
|
||||
|
||||
|
||||
void Component::construct(Genode::Env &env) { static Trace_recorder::Main main(env); }
|
189
repos/gems/src/app/trace_recorder/monitor.cc
Normal file
189
repos/gems/src/app/trace_recorder/monitor.cc
Normal file
@ -0,0 +1,189 @@
|
||||
/*
|
||||
* \brief Frontend for controlling the TRACE session
|
||||
* \author Johannes Schlatow
|
||||
* \date 2022-05-09
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2022 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"
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
Directory::Path Trace_recorder::Monitor::Trace_directory::subject_path(::Subject_info const &info)
|
||||
{
|
||||
typedef Path<Session_label::capacity()> Label_path;
|
||||
|
||||
Label_path label_path = path_from_label<Label_path>(info.session_label().string());
|
||||
Directory::Path subject_path(Directory::join(_path, label_path.string()));
|
||||
|
||||
return subject_path;
|
||||
}
|
||||
|
||||
|
||||
void Trace_recorder::Monitor::Attached_buffer::process_events(Trace_directory &trace_directory)
|
||||
{
|
||||
/* start iteration for every writer */
|
||||
_writers.for_each([&] (Writer_base &writer) {
|
||||
writer.start_iteration(trace_directory.root(),
|
||||
trace_directory.subject_path(info()),
|
||||
info());
|
||||
});
|
||||
|
||||
/* iterate entries and pass each entry to every writer */
|
||||
_buffer.for_each_new_entry([&] (Trace::Buffer::Entry &entry) {
|
||||
if (entry.length() == 0)
|
||||
return true;
|
||||
|
||||
_writers.for_each([&] (Writer_base &writer) {
|
||||
writer.process_event(entry.object<Trace_event_base>(), entry.length());
|
||||
});
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
/* end iteration for every writer */
|
||||
_writers.for_each([&] (Writer_base &writer) { writer.end_iteration(); });
|
||||
}
|
||||
|
||||
|
||||
Session_policy Trace_recorder::Monitor::_session_policy(Trace::Subject_info const &info, Xml_node config)
|
||||
{
|
||||
Session_label const label(info.session_label());
|
||||
Session_policy policy(label, config);
|
||||
|
||||
/* must have policy attribute */
|
||||
if (!policy.has_attribute("policy"))
|
||||
throw Session_policy::No_policy_defined();
|
||||
|
||||
if (policy.has_attribute("thread"))
|
||||
if (policy.attribute_value("thread", Trace::Thread_name()) != info.thread_name())
|
||||
throw Session_policy::No_policy_defined();
|
||||
|
||||
return policy;
|
||||
}
|
||||
|
||||
|
||||
void Trace_recorder::Monitor::_handle_timeout()
|
||||
{
|
||||
_trace_buffers.for_each([&] (Attached_buffer &buf) {
|
||||
buf.process_events(*_trace_directory);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void Trace_recorder::Monitor::start(Xml_node config)
|
||||
{
|
||||
stop();
|
||||
|
||||
/* create new trace directory */
|
||||
_trace_directory.construct(_env, _alloc, config, _rtc);
|
||||
|
||||
/* find matching subjects according to config and start tracing */
|
||||
_trace.for_each_subject_info([&] (Trace::Subject_id const &id,
|
||||
Trace::Subject_info const &info) {
|
||||
try {
|
||||
/* skip dead subjects */
|
||||
if (info.state() == Trace::Subject_info::DEAD)
|
||||
return;
|
||||
|
||||
/* check if there is a matching policy in the XML config */
|
||||
Session_policy session_policy = _session_policy(info, config);
|
||||
|
||||
if (!session_policy.has_attribute("policy"))
|
||||
return;
|
||||
|
||||
Number_of_bytes buffer_sz =
|
||||
session_policy.attribute_value("buffer", Number_of_bytes(DEFAULT_BUFFER_SIZE));
|
||||
|
||||
/* find and assign policy; create/insert if not present */
|
||||
Policy::Name const policy_name = session_policy.attribute_value("policy", Policy::Name());
|
||||
bool create = true;
|
||||
_policies.apply(policy_name, [&] (Policy & policy) {
|
||||
_trace.trace(id, policy.id(), buffer_sz);
|
||||
create = false;
|
||||
});
|
||||
|
||||
/* create policy if it did not exist */
|
||||
if (create) {
|
||||
Policy &policy = *new (_alloc) Policy(_env, _trace, policy_name, _policies);
|
||||
_trace.trace(id, policy.id(), buffer_sz);
|
||||
}
|
||||
|
||||
log("Inserting trace policy \"", policy_name, "\" into ",
|
||||
info.session_label(), " -> ", info.thread_name());
|
||||
|
||||
/* attach and remember trace buffer */
|
||||
Attached_buffer &buffer = *new (_alloc) Attached_buffer(_trace_buffers,
|
||||
_env,
|
||||
_trace.buffer(id),
|
||||
info,
|
||||
id);
|
||||
|
||||
/* create and register writers at trace buffer */
|
||||
session_policy.for_each_sub_node([&] (Xml_node & node) {
|
||||
bool present = false;
|
||||
_backends.apply(node.type(), [&] (Backend_base &backend) {
|
||||
backend.create_writer(_alloc,
|
||||
buffer.writers(),
|
||||
_trace_directory->root(),
|
||||
_trace_directory->subject_path(buffer.info()));
|
||||
present = true;
|
||||
});
|
||||
|
||||
if (!present)
|
||||
error("No writer available for <", node.type(), "/>.");
|
||||
else
|
||||
log("Enabled ", node.type(), " writer for ", info.session_label(),
|
||||
" -> ", info.thread_name());
|
||||
});
|
||||
}
|
||||
catch (Session_policy::No_policy_defined) { return; }
|
||||
});
|
||||
|
||||
/* register timeout */
|
||||
unsigned period_ms { 0 };
|
||||
if (!config.has_attribute("period_ms"))
|
||||
error("missing XML attribute 'period_ms'");
|
||||
else
|
||||
period_ms = config.attribute_value("period_ms", period_ms);
|
||||
|
||||
_timer.trigger_periodic(period_ms * 1000);
|
||||
}
|
||||
|
||||
|
||||
void Trace_recorder::Monitor::stop()
|
||||
{
|
||||
_timer.trigger_periodic(0);
|
||||
|
||||
_trace_buffers.for_each([&] (Attached_buffer &buf) {
|
||||
try {
|
||||
/* stop tracing */
|
||||
_trace.pause(buf.subject_id());
|
||||
} catch (Trace::Nonexistent_subject) { }
|
||||
|
||||
/* read remaining events from buffers */
|
||||
buf.process_events(*_trace_directory);
|
||||
|
||||
/* destroy writers */
|
||||
buf.writers().for_each([&] (Writer_base &writer) {
|
||||
destroy(_alloc, &writer);
|
||||
});
|
||||
|
||||
/* destroy buffer */
|
||||
destroy(_alloc, &buf);
|
||||
|
||||
try {
|
||||
/* detach buffer */
|
||||
_trace.free(buf.subject_id());
|
||||
} catch (Trace::Nonexistent_subject) { }
|
||||
});
|
||||
|
||||
_trace_directory.destruct();
|
||||
}
|
148
repos/gems/src/app/trace_recorder/monitor.h
Normal file
148
repos/gems/src/app/trace_recorder/monitor.h
Normal file
@ -0,0 +1,148 @@
|
||||
/*
|
||||
* \brief Frontend for controlling the TRACE session
|
||||
* \author Johannes Schlatow
|
||||
* \date 2022-05-09
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2022 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 <subject_info.h>
|
||||
#include <policy.h>
|
||||
#include <backend.h>
|
||||
#include <ctf/backend.h>
|
||||
#include <pcapng/backend.h>
|
||||
#include <timestamp_calibrator.h>
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/registry.h>
|
||||
#include <os/session_policy.h>
|
||||
#include <os/vfs.h>
|
||||
#include <trace/trace_buffer.h>
|
||||
#include <rtc_session/connection.h>
|
||||
|
||||
namespace Trace_recorder {
|
||||
using namespace Genode;
|
||||
|
||||
class Monitor;
|
||||
}
|
||||
|
||||
class Trace_recorder::Monitor
|
||||
{
|
||||
private:
|
||||
enum { DEFAULT_BUFFER_SIZE = 64 * 1024 };
|
||||
enum { TRACE_SESSION_RAM = 1024 * 1024 };
|
||||
enum { TRACE_SESSION_ARG_BUFFER = 128 * 1024 };
|
||||
|
||||
class Trace_directory
|
||||
{
|
||||
private:
|
||||
Root_directory _root;
|
||||
Directory::Path _path;
|
||||
|
||||
public:
|
||||
|
||||
static Directory::Path root_from_config(Xml_node &config) {
|
||||
return config.attribute_value("target_root", Directory::Path("/")); }
|
||||
|
||||
Trace_directory(Env &env,
|
||||
Allocator &alloc,
|
||||
Xml_node &config,
|
||||
Rtc::Connection &rtc)
|
||||
: _root(env, alloc, config.sub_node("vfs")),
|
||||
_path(Directory::join(root_from_config(config), rtc.current_time()))
|
||||
{ };
|
||||
|
||||
Directory &root() { return _root; }
|
||||
Directory::Path subject_path(::Subject_info const &info);
|
||||
};
|
||||
|
||||
class Attached_buffer
|
||||
{
|
||||
private:
|
||||
|
||||
Env &_env;
|
||||
Trace_buffer _buffer;
|
||||
Registry<Attached_buffer>::Element _element;
|
||||
Subject_info _info;
|
||||
Trace::Subject_id _subject_id;
|
||||
Registry<Writer_base> _writers { };
|
||||
|
||||
public:
|
||||
|
||||
Attached_buffer(Registry<Attached_buffer> ®istry,
|
||||
Genode::Env &env,
|
||||
Genode::Dataspace_capability ds,
|
||||
Trace::Subject_info const &info,
|
||||
Trace::Subject_id id)
|
||||
: _env(env),
|
||||
_buffer(*((Trace::Buffer*)_env.rm().attach(ds))),
|
||||
_element(registry, *this),
|
||||
_info(info),
|
||||
_subject_id(id)
|
||||
{ }
|
||||
|
||||
~Attached_buffer()
|
||||
{
|
||||
_env.rm().detach(_buffer.address());
|
||||
}
|
||||
|
||||
void process_events(Trace_directory &);
|
||||
|
||||
Registry<Writer_base> &writers() { return _writers; }
|
||||
|
||||
Subject_info const &info() const { return _info; }
|
||||
Trace::Subject_id const subject_id() const { return _subject_id; }
|
||||
};
|
||||
|
||||
Env &_env;
|
||||
Allocator &_alloc;
|
||||
Registry<Attached_buffer> _trace_buffers { };
|
||||
Policies _policies { };
|
||||
Backends _backends { };
|
||||
Constructible<Trace_directory> _trace_directory { };
|
||||
|
||||
Rtc::Connection _rtc { _env };
|
||||
Timer::Connection _timer { _env };
|
||||
Trace::Connection _trace { _env,
|
||||
TRACE_SESSION_RAM,
|
||||
TRACE_SESSION_ARG_BUFFER,
|
||||
0 };
|
||||
|
||||
Signal_handler<Monitor> _timeout_handler { _env.ep(),
|
||||
*this,
|
||||
&Monitor::_handle_timeout };
|
||||
|
||||
Timestamp_calibrator _ts_calibrator { _env, _rtc, _timer };
|
||||
|
||||
/* built-in backends */
|
||||
Ctf::Backend _ctf_backend { _env, _ts_calibrator, _backends };
|
||||
Pcapng::Backend _pcapng_backend { _alloc, _ts_calibrator, _backends };
|
||||
|
||||
/* methods */
|
||||
Session_policy _session_policy(Trace::Subject_info const &info, Xml_node config);
|
||||
void _handle_timeout();
|
||||
|
||||
public:
|
||||
|
||||
Monitor(Env &env, Allocator &alloc)
|
||||
: _env(env),
|
||||
_alloc(alloc)
|
||||
{
|
||||
_timer.sigh(_timeout_handler);
|
||||
}
|
||||
|
||||
void start(Xml_node config);
|
||||
void stop();
|
||||
};
|
||||
|
||||
|
||||
#endif /* _MONITOR_H_ */
|
102
repos/gems/src/app/trace_recorder/named_registry.h
Normal file
102
repos/gems/src/app/trace_recorder/named_registry.h
Normal file
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* \brief Utility for finding objecs by name
|
||||
* \author Norman Feske
|
||||
* \date 2021-11-11
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2021 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 _NAMED_REGISTRY_H_
|
||||
#define _NAMED_REGISTRY_H_
|
||||
|
||||
#include <util/string.h>
|
||||
#include <util/avl_tree.h>
|
||||
#include <util/noncopyable.h>
|
||||
|
||||
namespace Trace_recorder {
|
||||
using namespace Genode;
|
||||
|
||||
template <typename T> class Named_registry;
|
||||
|
||||
template <size_t N1, size_t N2>
|
||||
static inline bool operator > (String<N1> const &s1, String<N2> const &s2)
|
||||
{
|
||||
return strcmp(s1.string(), s2.string()) > 0;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
class Trace_recorder::Named_registry : Noncopyable
|
||||
{
|
||||
private:
|
||||
|
||||
Avl_tree<T> _tree { };
|
||||
|
||||
public:
|
||||
|
||||
class Element : private Avl_node<T>
|
||||
{
|
||||
public:
|
||||
|
||||
using Name = Genode::String<64>;
|
||||
Name const name;
|
||||
|
||||
private:
|
||||
|
||||
Named_registry<T> &_registry;
|
||||
|
||||
bool higher(T const *other) const { return name > other->name; }
|
||||
|
||||
friend class Avl_tree<T>;
|
||||
friend class Avl_node<T>;
|
||||
friend class Named_registry<T>;
|
||||
|
||||
static T *_matching_sub_tree(T &curr, Name const &name)
|
||||
{
|
||||
typename Avl_node<T>::Side side = (curr.name > name);
|
||||
|
||||
return curr.Avl_node<T>::child(side);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Element(Named_registry ®istry, Name const &name)
|
||||
:
|
||||
name(name), _registry(registry)
|
||||
{
|
||||
_registry._tree.insert(this);
|
||||
}
|
||||
|
||||
~Element()
|
||||
{
|
||||
_registry._tree.remove(this);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename FN>
|
||||
void apply(typename Element::Name const &name, FN && fn)
|
||||
{
|
||||
T *curr_ptr = _tree.first();
|
||||
for (;;) {
|
||||
if (!curr_ptr)
|
||||
return;
|
||||
|
||||
if (curr_ptr->name == name) {
|
||||
fn(*curr_ptr);
|
||||
return;
|
||||
}
|
||||
|
||||
curr_ptr = Element::_matching_sub_tree(*curr_ptr, name);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename FN>
|
||||
void for_each(FN && fn) { _tree.for_each(fn); }
|
||||
};
|
||||
|
||||
#endif /* _NAMED_REGISTRY_H_ */
|
136
repos/gems/src/app/trace_recorder/pcapng/backend.cc
Normal file
136
repos/gems/src/app/trace_recorder/pcapng/backend.cc
Normal file
@ -0,0 +1,136 @@
|
||||
/*
|
||||
* \brief PCAPNG backend
|
||||
* \author Johannes Schlatow
|
||||
* \date 2022-05-13
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2022 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 <pcapng/backend.h>
|
||||
#include <pcapng/enhanced_packet_block.h>
|
||||
#include <pcapng/interface_description_block.h>
|
||||
#include <pcapng/section_header_block.h>
|
||||
|
||||
using namespace Pcapng;
|
||||
using Append_error = Buffer::Append_error;
|
||||
using Append_result = Buffer::Append_result;
|
||||
using Pcapng_event = Trace_recorder::Pcapng_event;
|
||||
|
||||
|
||||
void Writer::start_iteration(Directory &root,
|
||||
Directory::Path const &path,
|
||||
::Subject_info const &)
|
||||
{
|
||||
/* write to '${path}.pcapng */
|
||||
Path<Directory::MAX_PATH_LEN> pcap_file { path };
|
||||
pcap_file.append(".pcapng");
|
||||
|
||||
_file_path = Directory::Path(pcap_file.string());
|
||||
|
||||
/* append to file */
|
||||
try {
|
||||
_dst_file.construct(root, _file_path, true);
|
||||
|
||||
_interface_registry.clear();
|
||||
_buffer.clear();
|
||||
_buffer.append<Section_header_block>();
|
||||
_empty_section = true;
|
||||
}
|
||||
catch (New_file::Create_failed) {
|
||||
error("Could not create file."); }
|
||||
}
|
||||
|
||||
|
||||
void Writer::process_event(Trace_recorder::Trace_event_base const &trace_event, size_t length)
|
||||
{
|
||||
if (!_dst_file.constructed()) return;
|
||||
|
||||
if (trace_event.type() != Trace_recorder::Event_type::PCAPNG) return;
|
||||
|
||||
/* event is of type Pcapng::Trace_event */
|
||||
Pcapng_event const &event = trace_event.event<Pcapng_event>();
|
||||
|
||||
/* map interface name to id of interface description block (IDB) */
|
||||
unsigned id = 0;
|
||||
bool buffer_full = false;
|
||||
_interface_registry.from_name(event.interface(),
|
||||
[&] (Interface const &iface) {
|
||||
/* IDB alread exists */
|
||||
id = iface.id();
|
||||
},
|
||||
[&] (Interface_name const &if_name, unsigned if_id) { /* IDB must be created */
|
||||
id = if_id;
|
||||
Append_result result =
|
||||
_buffer.append<Interface_description_block>(if_name,
|
||||
Enhanced_packet_block::MAX_CAPTURE_LENGTH);
|
||||
|
||||
result.with_error([&] (Append_error err) {
|
||||
switch (err)
|
||||
{
|
||||
case Append_error::OUT_OF_MEM:
|
||||
/* non-error, write to file and retry */
|
||||
buffer_full = true;
|
||||
break;
|
||||
case Append_error::OVERFLOW:
|
||||
error("Interface_description_block exceeds its MAX_SIZE");
|
||||
break;
|
||||
}
|
||||
});
|
||||
return !buffer_full;
|
||||
}
|
||||
);
|
||||
|
||||
/* add enhanced packet block to buffer */
|
||||
if (!buffer_full) {
|
||||
uint64_t us_since_epoch = _ts_calibrator.epoch_from_timestamp_in_us(event.timestamp());
|
||||
Append_result result = _buffer.append<Enhanced_packet_block>(id, event.packet(), us_since_epoch);
|
||||
|
||||
result.with_error([&] (Append_error err) {
|
||||
switch (err)
|
||||
{
|
||||
case Append_error::OUT_OF_MEM:
|
||||
/* non-error, write to file and retry */
|
||||
buffer_full = true;
|
||||
break;
|
||||
case Append_error::OVERFLOW:
|
||||
error("Enhanced_packet_block exceeds its MAX_SIZE");
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* write to file if buffer is full and process current event again */
|
||||
if (buffer_full) {
|
||||
_buffer.write_to_file(*_dst_file, _file_path);
|
||||
process_event(event, length);
|
||||
}
|
||||
else {
|
||||
_empty_section = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Writer::end_iteration()
|
||||
{
|
||||
/* write buffer to file */
|
||||
if (!_empty_section)
|
||||
_buffer.write_to_file(*_dst_file, _file_path);
|
||||
|
||||
_buffer.clear();
|
||||
_dst_file.destruct();
|
||||
}
|
||||
|
||||
|
||||
Trace_recorder::Writer_base &Backend::create_writer(Genode::Allocator &alloc,
|
||||
Genode::Registry<Writer_base> ®istry,
|
||||
Directory &,
|
||||
Directory::Path const &)
|
||||
{
|
||||
return *new (alloc) Writer(registry, _interface_registry, _buffer, _ts_calibrator);
|
||||
}
|
90
repos/gems/src/app/trace_recorder/pcapng/backend.h
Normal file
90
repos/gems/src/app/trace_recorder/pcapng/backend.h
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* \brief PCAPNG backend
|
||||
* \author Johannes Schlatow
|
||||
* \date 2022-05-13
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2022 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 _PCAPNG__BACKEND_H_
|
||||
#define _PCAPNG__BACKEND_H_
|
||||
|
||||
/* local includes */
|
||||
#include <backend.h>
|
||||
#include <pcapng/write_buffer.h>
|
||||
#include <pcapng/interface_registry.h>
|
||||
#include <timestamp_calibrator.h>
|
||||
|
||||
/* Genode includes */
|
||||
#include <os/vfs.h>
|
||||
|
||||
namespace Pcapng {
|
||||
using namespace Trace_recorder;
|
||||
|
||||
using Genode::Directory;
|
||||
using Genode::New_file;
|
||||
|
||||
using Buffer = Write_buffer<32*1024>;
|
||||
|
||||
class Backend;
|
||||
class Writer;
|
||||
}
|
||||
|
||||
|
||||
class Pcapng::Writer : public Trace_recorder::Writer_base
|
||||
{
|
||||
private:
|
||||
Interface_registry &_interface_registry;
|
||||
Buffer &_buffer;
|
||||
Timestamp_calibrator const &_ts_calibrator;
|
||||
Constructible<New_file> _dst_file { };
|
||||
Directory::Path _file_path { };
|
||||
bool _empty_section { false };
|
||||
|
||||
public:
|
||||
Writer(Genode::Registry<Writer_base> ®istry, Interface_registry &interface_registry, Buffer &buffer, Timestamp_calibrator const &ts_calibrator)
|
||||
: Writer_base(registry),
|
||||
_interface_registry(interface_registry),
|
||||
_buffer(buffer),
|
||||
_ts_calibrator(ts_calibrator)
|
||||
{ }
|
||||
|
||||
virtual void start_iteration(Directory &,
|
||||
Directory::Path const &,
|
||||
::Subject_info const &) override;
|
||||
|
||||
virtual void process_event(Trace_recorder::Trace_event_base const &, Genode::size_t) override;
|
||||
|
||||
virtual void end_iteration() override;
|
||||
};
|
||||
|
||||
|
||||
class Pcapng::Backend : Trace_recorder::Backend_base
|
||||
{
|
||||
private:
|
||||
|
||||
Interface_registry _interface_registry;
|
||||
Buffer _buffer { };
|
||||
Timestamp_calibrator const &_ts_calibrator;
|
||||
|
||||
public:
|
||||
|
||||
Backend(Allocator &alloc, Timestamp_calibrator const &ts_calibrator, Backends &backends)
|
||||
: Backend_base(backends, "pcapng"),
|
||||
_interface_registry(alloc),
|
||||
_ts_calibrator(ts_calibrator)
|
||||
{ }
|
||||
|
||||
Writer_base &create_writer(Genode::Allocator &,
|
||||
Genode::Registry<Writer_base> &,
|
||||
Directory &,
|
||||
Directory::Path const &) override;
|
||||
};
|
||||
|
||||
|
||||
#endif /* _PCAPNG__BACKEND_H_ */
|
88
repos/gems/src/app/trace_recorder/pcapng/block.h
Normal file
88
repos/gems/src/app/trace_recorder/pcapng/block.h
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* \brief Generic type for PCAPNG blocks
|
||||
* \author Johannes Schlatow
|
||||
* \date 2022-05-12
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2022 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 _PCAPNG__BLOCK_H_
|
||||
#define _PCAPNG__BLOCK_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/fixed_stdint.h>
|
||||
|
||||
namespace Pcapng {
|
||||
struct Block_base;
|
||||
|
||||
template <unsigned ID>
|
||||
struct Block;
|
||||
}
|
||||
|
||||
struct Pcapng::Block_base
|
||||
{
|
||||
/**
|
||||
* Layout: ----- 32-bit -----
|
||||
* | Type |
|
||||
* ------------------
|
||||
* | Length |
|
||||
* ------------------
|
||||
* | ... |
|
||||
* ------------------
|
||||
* | Length |
|
||||
* ------------------
|
||||
*/
|
||||
|
||||
uint32_t const _type;
|
||||
uint32_t _length { 0 };
|
||||
|
||||
static constexpr uint32_t padded_size(uint32_t hdr_sz)
|
||||
{
|
||||
/* add padding to 4-byte boundary */
|
||||
const uint32_t hdr_sz_padded = Genode::align_addr(hdr_sz, 2);
|
||||
|
||||
return hdr_sz_padded;
|
||||
}
|
||||
|
||||
static constexpr uint32_t block_size(uint32_t sz)
|
||||
{
|
||||
return padded_size(sz) + sizeof(uint32_t);
|
||||
}
|
||||
|
||||
Block_base(uint32_t type)
|
||||
: _type(type)
|
||||
{ }
|
||||
|
||||
void commit(uint32_t hdr_sz)
|
||||
{
|
||||
const uint32_t hdr_sz_padded = padded_size(hdr_sz);
|
||||
|
||||
_length = hdr_sz_padded + sizeof(uint32_t);
|
||||
|
||||
/* store length also after payload to support backward navigation */
|
||||
((uint32_t*)this)[hdr_sz_padded/4] = _length;
|
||||
}
|
||||
|
||||
bool has_type(uint32_t type) const { return _type == type; }
|
||||
uint32_t size() const { return _length; }
|
||||
|
||||
} __attribute__((packed));
|
||||
|
||||
|
||||
template <unsigned TYPE_ID>
|
||||
struct Pcapng::Block : Pcapng::Block_base
|
||||
{
|
||||
Block()
|
||||
: Block_base(TYPE_ID)
|
||||
{ }
|
||||
|
||||
static uint32_t type() { return TYPE_ID; };
|
||||
|
||||
} __attribute__((packed));
|
||||
|
||||
#endif /* _PCAPNG__BLOCK_H_ */
|
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* \brief Enhanced packet block
|
||||
* \author Johannes Schlatow
|
||||
* \date 2022-05-12
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2022 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 _PCAPNG__ENHANCED_PACKET_BLOCK_H_
|
||||
#define _PCAPNG__ENHANCED_PACKET_BLOCK_H_
|
||||
|
||||
/* local includes */
|
||||
#include <trace_recorder_policy/pcapng.h>
|
||||
|
||||
/* Genode includes */
|
||||
#include <pcapng/block.h>
|
||||
|
||||
namespace Pcapng {
|
||||
using namespace Genode;
|
||||
|
||||
struct Enhanced_packet_block;
|
||||
}
|
||||
|
||||
|
||||
/* converts Traced_packet into a Pcapng block structure */
|
||||
struct Pcapng::Enhanced_packet_block : Block<0x6>
|
||||
{
|
||||
/**
|
||||
* Layout: -------- 32-bit -------
|
||||
* | 0x00000006 |
|
||||
* -----------------------
|
||||
* | Length |
|
||||
* -----------------------
|
||||
* | Interface ID |
|
||||
* -----------------------
|
||||
* | Timestamp High |
|
||||
* -----------------------
|
||||
* | Timestamp Low |
|
||||
* -----------------------
|
||||
* | Captured Length |
|
||||
* -----------------------
|
||||
* | Original Length |
|
||||
* -----------------------
|
||||
* | Packet Data |
|
||||
* | ... |
|
||||
* | (padded) |
|
||||
* -----------------------
|
||||
* | Length |
|
||||
* -----------------------
|
||||
*/
|
||||
|
||||
uint32_t _interface_id;
|
||||
uint32_t _timestamp_high;
|
||||
uint32_t _timestamp_low;
|
||||
Traced_packet _data;
|
||||
|
||||
enum {
|
||||
MAX_CAPTURE_LENGTH = 1600,
|
||||
MAX_SIZE = block_size(sizeof(Block_base) +
|
||||
sizeof(_interface_id) +
|
||||
sizeof(_timestamp_high) +
|
||||
sizeof(_timestamp_low) +
|
||||
sizeof(Traced_packet) +
|
||||
MAX_CAPTURE_LENGTH)
|
||||
};
|
||||
|
||||
Enhanced_packet_block(uint32_t interface_id, Traced_packet const &packet, uint64_t timestamp)
|
||||
: _interface_id(interface_id),
|
||||
_timestamp_high((uint32_t)(timestamp >> 32)),
|
||||
_timestamp_low(timestamp & 0xFFFFFFFF),
|
||||
_data(packet)
|
||||
{ commit(sizeof(Enhanced_packet_block) + _data.data_length()); }
|
||||
|
||||
} __attribute__((packed));
|
||||
|
||||
#endif /* _PCAPNG__ENHANCED_PACKET_BLOCK_H_ */
|
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* \brief Interface description block
|
||||
* \author Johannes Schlatow
|
||||
* \date 2022-05-12
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2022 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 _PCAPNG__INTERFACE_DESCRIPTION_BLOCK_H_
|
||||
#define _PCAPNG__INTERFACE_DESCRIPTION_BLOCK_H_
|
||||
|
||||
/* local includes */
|
||||
#include <pcapng/block.h>
|
||||
#include <pcapng/option.h>
|
||||
|
||||
/* genode includes */
|
||||
#include <trace_recorder_policy/pcapng.h>
|
||||
|
||||
namespace Pcapng {
|
||||
using namespace Genode;
|
||||
|
||||
struct Interface_description_block;
|
||||
}
|
||||
|
||||
|
||||
struct Pcapng::Interface_description_block : Block<0x1>
|
||||
{
|
||||
/**
|
||||
* Layout: -------- 32-bit -------
|
||||
* | 0x00000001 |
|
||||
* -----------------------
|
||||
* | Length |
|
||||
* -----------------------
|
||||
* | LinkType | Reserved |
|
||||
* -----------------------
|
||||
* | SnapLen |
|
||||
* -----------------------
|
||||
* | 0x0002 | NameLen |
|
||||
* -----------------------
|
||||
* | Name |
|
||||
* | ... |
|
||||
* | (padded) |
|
||||
* -----------------------
|
||||
* | 0x0001 | 0x0000 |
|
||||
* -----------------------
|
||||
* | Length |
|
||||
* -----------------------
|
||||
*/
|
||||
|
||||
uint16_t const _link_type;
|
||||
uint16_t const _reserved { 0 };
|
||||
uint32_t _snaplen;
|
||||
uint32_t _data[0] { };
|
||||
|
||||
enum {
|
||||
MAX_SIZE = block_size(sizeof(Block_base) +
|
||||
Interface_name::MAX_NAME_LEN +
|
||||
sizeof(_link_type) +
|
||||
sizeof(_reserved) +
|
||||
sizeof(_snaplen) +
|
||||
sizeof(Option_ifname) +
|
||||
sizeof(Option_end))
|
||||
};
|
||||
|
||||
Interface_description_block(Interface_name const &name, uint32_t snaplen)
|
||||
: _link_type(name._link_type),
|
||||
_snaplen(snaplen)
|
||||
{
|
||||
Option_ifname &opt_ifname = *construct_at<Option_ifname>(&_data[0], name);
|
||||
Option_end &opt_end = *construct_at<Option_end> (&_data[opt_ifname.total_length()/4]);
|
||||
|
||||
commit((uint32_t)sizeof(Interface_description_block) + opt_ifname.total_length() + opt_end.total_length());
|
||||
}
|
||||
|
||||
} __attribute__((packed));
|
||||
|
||||
#endif /* _PCAPNG__INTERFACE_DESCRIPTION_BLOCK_H_ */
|
101
repos/gems/src/app/trace_recorder/pcapng/interface_registry.h
Normal file
101
repos/gems/src/app/trace_recorder/pcapng/interface_registry.h
Normal file
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* \brief Registry for storing interfaces description blocks
|
||||
* \author Johannes Schlatow
|
||||
* \date 2022-05-16
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2022 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 _PCAPNG__INTERFACE_REGISTRY_H_
|
||||
#define _PCAPNG__INTERFACE_REGISTRY_H_
|
||||
|
||||
/* local includes */
|
||||
#include <base/registry.h>
|
||||
#include <pcapng/interface_description_block.h>
|
||||
|
||||
namespace Pcapng {
|
||||
using namespace Trace_recorder;
|
||||
using namespace Genode;
|
||||
|
||||
class Interface;
|
||||
class Interface_registry;
|
||||
}
|
||||
|
||||
|
||||
class Pcapng::Interface
|
||||
{
|
||||
public:
|
||||
|
||||
using Name = String<Interface_name::MAX_NAME_LEN>;
|
||||
|
||||
private:
|
||||
|
||||
Name const _name;
|
||||
unsigned const _id;
|
||||
|
||||
Registry<Interface>::Element _element;
|
||||
|
||||
public:
|
||||
|
||||
Interface(Name const &name, unsigned id, Registry<Interface> ®istry)
|
||||
: _name(name),
|
||||
_id(id),
|
||||
_element(registry, *this)
|
||||
{ }
|
||||
|
||||
/*************
|
||||
* Accessors *
|
||||
*************/
|
||||
|
||||
unsigned id() const { return _id; }
|
||||
Name const &name() const { return _name; }
|
||||
};
|
||||
|
||||
|
||||
class Pcapng::Interface_registry : private Registry<Interface>
|
||||
{
|
||||
private:
|
||||
|
||||
unsigned _next_id { 0 };
|
||||
Allocator &_alloc;
|
||||
|
||||
public:
|
||||
|
||||
Interface_registry(Allocator &alloc)
|
||||
: _alloc(alloc)
|
||||
{ }
|
||||
|
||||
/* apply to existing Interface or create new one */
|
||||
template <typename FUNC_EXISTS, typename FUNC_NEW>
|
||||
void from_name(Interface_name const &name, FUNC_EXISTS && fn_exists, FUNC_NEW && fn_new)
|
||||
{
|
||||
bool found = false;
|
||||
for_each([&] (Interface const &iface) {
|
||||
if (iface.name() == name.string()) {
|
||||
found = true;
|
||||
fn_exists(iface);
|
||||
}
|
||||
});
|
||||
|
||||
/* create new interface */
|
||||
if (!found) {
|
||||
if (fn_new(name, _next_id))
|
||||
new (_alloc) Interface(Interface::Name(name.string()), _next_id++, *this);
|
||||
}
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
for_each([&] (Interface &iface) { destroy(_alloc, &iface); });
|
||||
|
||||
_next_id = 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif /* _PCAPNG__INTERFACE_REGISTRY_H_ */
|
69
repos/gems/src/app/trace_recorder/pcapng/option.h
Normal file
69
repos/gems/src/app/trace_recorder/pcapng/option.h
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* \brief Option fields
|
||||
* \author Johannes Schlatow
|
||||
* \date 2022-05-12
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2022 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 _PCAPNG__OPTION_H_
|
||||
#define _PCAPNG__OPTION_H_
|
||||
|
||||
#include <trace_recorder_policy/pcapng.h>
|
||||
|
||||
namespace Pcapng {
|
||||
using namespace Genode;
|
||||
|
||||
template <uint16_t TYPE>
|
||||
struct Option;
|
||||
|
||||
struct Option_ifname;
|
||||
struct Option_end;
|
||||
}
|
||||
|
||||
template <Genode::uint16_t TYPE>
|
||||
struct Pcapng::Option
|
||||
{
|
||||
uint16_t _type { TYPE };
|
||||
uint16_t _length;
|
||||
uint32_t _data[0];
|
||||
|
||||
static uint16_t padded_size(uint16_t sz) {
|
||||
return (uint16_t)align_addr((uint32_t)sz, 2); }
|
||||
|
||||
Option(uint16_t length)
|
||||
: _length(length)
|
||||
{ }
|
||||
|
||||
template <typename T>
|
||||
T *data() { return (T*)_data; }
|
||||
|
||||
uint16_t total_length() const { return padded_size(sizeof(Option) + _length); }
|
||||
} __attribute__((packed));
|
||||
|
||||
|
||||
struct Pcapng::Option_end : Option<1>
|
||||
{
|
||||
Option_end() : Option(0) { }
|
||||
} __attribute__((packed));
|
||||
|
||||
|
||||
struct Pcapng::Option_ifname : Option<2>
|
||||
{
|
||||
static uint16_t padded_size(Interface_name const &name) {
|
||||
return (uint16_t)align_addr((uint32_t)name.data_length() - 1, 2); }
|
||||
|
||||
Option_ifname(Interface_name const &name)
|
||||
: Option((uint16_t)(name.data_length()-1))
|
||||
{
|
||||
/* copy string leaving out null-termination */
|
||||
memcpy(data<void>(), name.string(), name.data_length()-1);
|
||||
}
|
||||
} __attribute__((packed));
|
||||
|
||||
#endif /* _PCAPNG__OPTION_H_ */
|
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* \brief Section header block
|
||||
* \author Johannes Schlatow
|
||||
* \date 2022-05-12
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2022 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 _PCAPNG__SECTION_HEADER_BLOCK_H_
|
||||
#define _PCAPNG__SECTION_HEADER_BLOCK_H_
|
||||
|
||||
/* local includes */
|
||||
#include <pcapng/block.h>
|
||||
|
||||
namespace Pcapng {
|
||||
using namespace Genode;
|
||||
|
||||
struct Section_header_block;
|
||||
}
|
||||
|
||||
|
||||
struct Pcapng::Section_header_block : Block<0x0A0D0D0A>
|
||||
{
|
||||
/**
|
||||
* Layout: ----- 32-bit ----
|
||||
* | 0x0A0D0D0A |
|
||||
* -----------------
|
||||
* | Length |
|
||||
* -----------------
|
||||
* | 0x1A2B3C4D |
|
||||
* -----------------
|
||||
* | Major | Minor |
|
||||
* -----------------
|
||||
* | SectionLen Hi |
|
||||
* -----------------
|
||||
* | SectionLen Lo |
|
||||
* -----------------
|
||||
* | Length |
|
||||
* -----------------
|
||||
*/
|
||||
|
||||
uint32_t const _byte_order_magic { 0x1A2B3C4D };
|
||||
uint16_t const _major_version { 1 };
|
||||
uint16_t const _minor_version { 0 };
|
||||
uint64_t const _section_length { 0xFFFFFFFFFFFFFFFF }; /* unspecified */
|
||||
|
||||
enum : size_t {
|
||||
MAX_SIZE = block_size(sizeof(Block_base) +
|
||||
sizeof(_byte_order_magic) +
|
||||
sizeof(_major_version) +
|
||||
sizeof(_minor_version) +
|
||||
sizeof(_section_length))
|
||||
};
|
||||
|
||||
Section_header_block()
|
||||
{ commit(sizeof(Section_header_block)); }
|
||||
|
||||
/**
|
||||
* XXX instead of using an unspecified section length, we could add an
|
||||
* interface similar to Ctf::Packet_header for append sub-blocks and
|
||||
* keeping track of the length field.
|
||||
*/
|
||||
|
||||
} __attribute__((packed));
|
||||
|
||||
#endif /* _PCAPNG__SECTION_HEADER_BLOCK_H_ */
|
79
repos/gems/src/app/trace_recorder/pcapng/write_buffer.h
Normal file
79
repos/gems/src/app/trace_recorder/pcapng/write_buffer.h
Normal file
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* \brief Convenience helper for batching pcapng blocks before writing to file
|
||||
* \author Johannes Schlatow
|
||||
* \date 2022-05-16
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2022 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 _PCAPNG__WRITE_BUFFER_H_
|
||||
#define _PCAPNG__WRITE_BUFFER_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <util/attempt.h>
|
||||
#include <os/vfs.h>
|
||||
|
||||
namespace Pcapng
|
||||
{
|
||||
using namespace Genode;
|
||||
|
||||
template <unsigned>
|
||||
class Write_buffer;
|
||||
}
|
||||
|
||||
template <unsigned BUFSIZE>
|
||||
class Pcapng::Write_buffer
|
||||
{
|
||||
public:
|
||||
|
||||
enum class Append_error { OUT_OF_MEM, OVERFLOW };
|
||||
struct Append_ok { };
|
||||
using Append_result = Attempt<Append_ok, Append_error>;
|
||||
|
||||
private:
|
||||
|
||||
size_t _total_length { 0 };
|
||||
char _buffer[BUFSIZE] { };
|
||||
|
||||
public:
|
||||
|
||||
template <typename T, typename... ARGS>
|
||||
Append_result append(ARGS &&... args)
|
||||
{
|
||||
if (T::MAX_SIZE > BUFSIZE || _total_length > BUFSIZE - T::MAX_SIZE)
|
||||
return Append_error::OUT_OF_MEM;
|
||||
|
||||
void *ptr = &_buffer[_total_length];
|
||||
|
||||
T const &block = *construct_at<T>(ptr, args...);
|
||||
|
||||
if (block.size() > T::MAX_SIZE) {
|
||||
error("block size of ", block.size(), " exceeds reserved size ", (unsigned)T::MAX_SIZE);
|
||||
return Append_error::OVERFLOW;
|
||||
}
|
||||
|
||||
_total_length += block.size();
|
||||
|
||||
return Append_ok();
|
||||
}
|
||||
|
||||
void write_to_file(Genode::New_file &dst, Directory::Path const &path)
|
||||
{
|
||||
if (_total_length == 0)
|
||||
return;
|
||||
|
||||
if (dst.append(_buffer, _total_length) != New_file::Append_result::OK)
|
||||
error("Write error for ", path);
|
||||
|
||||
clear();
|
||||
}
|
||||
|
||||
void clear() { _total_length = 0; }
|
||||
};
|
||||
|
||||
#endif /* _PCAPNG__WRITE_BUFFER_H_ */
|
33
repos/gems/src/app/trace_recorder/policy.cc
Normal file
33
repos/gems/src/app/trace_recorder/policy.cc
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* \brief Installs and maintains a tracing policy
|
||||
* \author Johannes Schlatow
|
||||
* \date 2022-05-10
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2022 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;
|
||||
|
||||
Trace_recorder::Policy::Policy(Env &env,
|
||||
Trace::Connection &trace,
|
||||
Policy::Name const &name,
|
||||
Policies ®istry)
|
||||
:
|
||||
Policies::Element(registry, name),
|
||||
_env(env), _trace(trace), _rom(env, name.string())
|
||||
{
|
||||
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);
|
||||
}
|
68
repos/gems/src/app/trace_recorder/policy.h
Normal file
68
repos/gems/src/app/trace_recorder/policy.h
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* \brief Installs and maintains a tracing policy
|
||||
* \author Johannes Schlatow
|
||||
* \date 2022-05-10
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2022 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_
|
||||
|
||||
/* local includes */
|
||||
#include <named_registry.h>
|
||||
|
||||
/* Genode includes */
|
||||
#include <rom_session/connection.h>
|
||||
#include <trace_session/connection.h>
|
||||
#include <dataspace/client.h>
|
||||
|
||||
namespace Trace_recorder {
|
||||
class Policy;
|
||||
|
||||
using Policies = Named_registry<Policy>;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Installs and maintains a tracing policy
|
||||
*/
|
||||
class Trace_recorder::Policy : Policies::Element
|
||||
{
|
||||
private:
|
||||
friend class Policies::Element;
|
||||
friend class Genode::Avl_node<Policy>;
|
||||
friend class Genode::Avl_tree<Policy>;
|
||||
|
||||
Genode::Env &_env;
|
||||
Genode::Trace::Connection &_trace;
|
||||
Genode::Rom_connection _rom;
|
||||
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:
|
||||
|
||||
using Name = Policies::Element::Name;
|
||||
using Policies::Element::name;
|
||||
using Policies::Element::Element;
|
||||
|
||||
Policy(Genode::Env &env,
|
||||
Genode::Trace::Connection &trace,
|
||||
Name const &name,
|
||||
Policies ®istry);
|
||||
|
||||
|
||||
/***************
|
||||
** Accessors **
|
||||
***************/
|
||||
|
||||
Genode::Trace::Policy_id id() const { return _id; }
|
||||
};
|
||||
|
||||
#endif /* _POLICY_H_ */
|
39
repos/gems/src/app/trace_recorder/subject_info.h
Normal file
39
repos/gems/src/app/trace_recorder/subject_info.h
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* \brief Helper for storing static parts of Trace::Subject_info
|
||||
* \author Johannes Schlatow
|
||||
* \date 2021-08-06
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2021 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 _SUBJECT_INFO_H_
|
||||
#define _SUBJECT_INFO_H_
|
||||
|
||||
#include <base/trace/types.h>
|
||||
|
||||
struct Subject_info
|
||||
{
|
||||
Genode::Session_label _session_label;
|
||||
Genode::Trace::Thread_name _thread_name;
|
||||
Genode::Affinity::Location _affinity;
|
||||
unsigned _priority;
|
||||
|
||||
Subject_info(Genode::Trace::Subject_info const &info)
|
||||
: _session_label(info.session_label()),
|
||||
_thread_name(info.thread_name()),
|
||||
_affinity(info.affinity()),
|
||||
_priority(info.execution_time().priority)
|
||||
{ }
|
||||
|
||||
Genode::Session_label const &session_label() const { return _session_label; }
|
||||
Genode::Trace::Thread_name const &thread_name() const { return _thread_name; }
|
||||
Genode::Affinity::Location const &affinity() const { return _affinity; }
|
||||
unsigned priority() const { return _priority; }
|
||||
};
|
||||
|
||||
#endif /* _SUBJECT_INFO_H_ */
|
5
repos/gems/src/app/trace_recorder/target.mk
Normal file
5
repos/gems/src/app/trace_recorder/target.mk
Normal file
@ -0,0 +1,5 @@
|
||||
TARGET = trace_recorder
|
||||
INC_DIR += $(PRG_DIR)
|
||||
SRC_CC = main.cc monitor.cc policy.cc ctf/backend.cc pcapng/backend.cc
|
||||
CONFIG_XSD = config.xsd
|
||||
LIBS += base vfs
|
127
repos/gems/src/app/trace_recorder/timestamp_calibrator.h
Normal file
127
repos/gems/src/app/trace_recorder/timestamp_calibrator.h
Normal file
@ -0,0 +1,127 @@
|
||||
/*
|
||||
* \brief Helper for converting Trace::Timestamp to epoch
|
||||
* \author Johannes Schlatow
|
||||
* \date 2022-05-19
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2022 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 _TIMESTAMP_CALIBRATOR_H_
|
||||
#define _TIMESTAMP_CALIBRATOR_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <trace/timestamp.h>
|
||||
#include <rtc_session/connection.h>
|
||||
#include <timer_session/connection.h>
|
||||
#include <base/attached_rom_dataspace.h>
|
||||
|
||||
namespace Trace_recorder {
|
||||
using namespace Genode;
|
||||
|
||||
class Timestamp_calibrator;
|
||||
}
|
||||
|
||||
|
||||
class Trace_recorder::Timestamp_calibrator
|
||||
{
|
||||
private:
|
||||
|
||||
uint64_t const _frequency_hz;
|
||||
uint64_t const _epoch_start_in_us;
|
||||
Trace::Timestamp const _ts_start { Trace::timestamp() };
|
||||
|
||||
enum : uint64_t {
|
||||
USEC_PER_SEC = 1000ULL * 1000ULL,
|
||||
USEC_PER_MIN = USEC_PER_SEC * 60,
|
||||
USEC_PER_HOUR = USEC_PER_MIN * 60,
|
||||
USEC_PER_DAY = USEC_PER_HOUR * 24,
|
||||
};
|
||||
|
||||
static uint64_t _day_of_year(Rtc::Timestamp time)
|
||||
{
|
||||
/* look up table, starts with month=0 */
|
||||
unsigned days_until_month[] = { 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
|
||||
|
||||
uint64_t result = time.day + days_until_month[time.month];
|
||||
|
||||
/* check for leap year */
|
||||
if (time.month >= 3) {
|
||||
if ((time.year % 1000) == 0 || ((time.year % 4) == 0 && !(time.year % 100) == 0))
|
||||
return result + 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uint64_t _timestamp_frequency(Env &env, Timer::Connection &timer)
|
||||
{
|
||||
using namespace Genode;
|
||||
|
||||
/* try getting tsc frequency from platform info, measure if failed */
|
||||
try {
|
||||
Attached_rom_dataspace const platform_info (env, "platform_info");
|
||||
Xml_node const hardware = platform_info.xml().sub_node("hardware");
|
||||
uint64_t const tsc_freq = hardware.sub_node("tsc").attribute_value("freq_khz", 0ULL);
|
||||
bool const invariant = hardware.sub_node("tsc").attribute_value("invariant", true);
|
||||
|
||||
if (!invariant)
|
||||
error("No invariant TSC available");
|
||||
|
||||
if (tsc_freq)
|
||||
return tsc_freq * 1000ULL;
|
||||
} catch (...) { }
|
||||
|
||||
warning("Falling back to measured timestamp frequency");
|
||||
/* measure frequency using timer */
|
||||
Trace::Timestamp start = Trace::timestamp();
|
||||
timer.msleep(1000);
|
||||
return (Trace::timestamp() - start);
|
||||
}
|
||||
|
||||
uint64_t _current_epoch_us(Rtc::Connection &rtc)
|
||||
{
|
||||
Rtc::Timestamp const current_time { rtc.current_time() };
|
||||
|
||||
// assuming year > 2000 or year == 0
|
||||
uint64_t usec_until_y2k = (30*365 + 30/4) * USEC_PER_DAY;
|
||||
uint64_t years_since_y2k = current_time.year ? current_time.year - 2000 : 0;
|
||||
uint64_t days_since_y2k = years_since_y2k * 365 + years_since_y2k/4 -
|
||||
years_since_y2k/100 +
|
||||
years_since_y2k/1000 +
|
||||
_day_of_year(current_time);
|
||||
|
||||
return usec_until_y2k +
|
||||
days_since_y2k * USEC_PER_DAY +
|
||||
current_time.hour * USEC_PER_HOUR +
|
||||
current_time.minute * USEC_PER_MIN +
|
||||
current_time.second * USEC_PER_SEC +
|
||||
current_time.microsecond;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Timestamp_calibrator(Env &env, Rtc::Connection &rtc, Timer::Connection &timer)
|
||||
: _frequency_hz (_timestamp_frequency(env, timer)),
|
||||
_epoch_start_in_us(_current_epoch_us(rtc))
|
||||
{
|
||||
log("Timestamp frequency is ", _frequency_hz, "Hz");
|
||||
}
|
||||
|
||||
uint64_t ticks_per_second() const { return _frequency_hz; }
|
||||
|
||||
uint64_t epoch_from_timestamp_in_us(Trace::Timestamp ts) const
|
||||
{
|
||||
/* intentionally ignoring timestamp wraparounds */
|
||||
uint64_t ts_diff = ts - _ts_start;
|
||||
|
||||
return _epoch_start_in_us + (ts_diff / (ticks_per_second() / USEC_PER_SEC));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif /* _TIMESTAMP_CALIBRATOR_H_ */
|
62
repos/gems/src/app/trace_recorder/writer.h
Normal file
62
repos/gems/src/app/trace_recorder/writer.h
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* \brief Base class for processing traces and writing outputs
|
||||
* \author Johannes Schlatow
|
||||
* \date 2022-05-11
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2022 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 _WRITER_H_
|
||||
#define _WRITER_H_
|
||||
|
||||
/* local includes */
|
||||
#include <subject_info.h>
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/registry.h>
|
||||
#include <os/vfs.h>
|
||||
#include <trace/trace_buffer.h>
|
||||
#include <trace_recorder_policy/event.h>
|
||||
|
||||
namespace Trace_recorder {
|
||||
class Writer_base;
|
||||
|
||||
using Directory = Genode::Directory;
|
||||
using Writer_registry = Genode::Registry<Writer_base>;
|
||||
}
|
||||
|
||||
|
||||
class Trace_recorder::Writer_base
|
||||
{
|
||||
protected:
|
||||
|
||||
Writer_registry::Element _element;
|
||||
|
||||
public:
|
||||
|
||||
Writer_base(Writer_registry ®istry)
|
||||
: _element(registry, *this)
|
||||
{ }
|
||||
|
||||
virtual ~Writer_base() { }
|
||||
|
||||
/***************
|
||||
** Interface **
|
||||
***************/
|
||||
|
||||
virtual void start_iteration(Directory &,
|
||||
Directory::Path const &,
|
||||
::Subject_info const &) = 0;
|
||||
|
||||
virtual void process_event(Trace_recorder::Trace_event_base const &, Genode::size_t) = 0;
|
||||
|
||||
virtual void end_iteration() = 0;
|
||||
};
|
||||
|
||||
|
||||
#endif /* _WRITER_H_ */
|
Loading…
Reference in New Issue
Block a user