From ef742001e8755a7d135674ec15f19b2118e46ac7 Mon Sep 17 00:00:00 2001 From: Johannes Schlatow Date: Fri, 20 May 2022 14:33:07 +0200 Subject: [PATCH] trace_recorder: implementation genodelabs/genode#4352 --- repos/gems/recipes/pkg/trace_recorder/README | 1 + .../gems/recipes/pkg/trace_recorder/archives | 4 + repos/gems/recipes/pkg/trace_recorder/hash | 1 + repos/gems/recipes/pkg/trace_recorder/runtime | 28 +++ .../recipes/raw/trace_recorder/content.mk | 4 + repos/gems/recipes/raw/trace_recorder/hash | 1 + .../recipes/src/trace_recorder/content.mk | 9 + repos/gems/recipes/src/trace_recorder/hash | 1 + .../gems/recipes/src/trace_recorder/used_apis | 8 + .../src/trace_recorder_policy/content.mk | 2 + .../recipes/src/trace_recorder_policy/hash | 1 + .../src/trace_recorder_policy/used_apis | 5 + repos/gems/run/trace_recorder_ctf.run | 175 ++++++++++++++++ repos/gems/run/trace_recorder_pcapng.run | 163 +++++++++++++++ repos/gems/src/app/trace_recorder/README | 56 ++++++ repos/gems/src/app/trace_recorder/backend.h | 57 ++++++ repos/gems/src/app/trace_recorder/config.xsd | 59 ++++++ .../src/app/trace_recorder/ctf/backend.cc | 59 ++++++ .../gems/src/app/trace_recorder/ctf/backend.h | 96 +++++++++ .../gems/src/app/trace_recorder/ctf/metadata | 121 +++++++++++ .../src/app/trace_recorder/ctf/metadata.h | 106 ++++++++++ .../app/trace_recorder/ctf/packet_header.h} | 8 +- .../src/app/trace_recorder/ctf/write_buffer.h | 82 ++++++++ repos/gems/src/app/trace_recorder/main.cc | 77 +++++++ repos/gems/src/app/trace_recorder/monitor.cc | 189 ++++++++++++++++++ repos/gems/src/app/trace_recorder/monitor.h | 148 ++++++++++++++ .../src/app/trace_recorder/named_registry.h | 102 ++++++++++ .../src/app/trace_recorder/pcapng/backend.cc | 136 +++++++++++++ .../src/app/trace_recorder/pcapng/backend.h | 90 +++++++++ .../src/app/trace_recorder/pcapng/block.h | 88 ++++++++ .../pcapng/enhanced_packet_block.h | 81 ++++++++ .../pcapng/interface_description_block.h | 82 ++++++++ .../pcapng/interface_registry.h | 101 ++++++++++ .../src/app/trace_recorder/pcapng/option.h | 69 +++++++ .../pcapng/section_header_block.h | 71 +++++++ .../app/trace_recorder/pcapng/write_buffer.h | 79 ++++++++ repos/gems/src/app/trace_recorder/policy.cc | 33 +++ repos/gems/src/app/trace_recorder/policy.h | 68 +++++++ .../src/app/trace_recorder/subject_info.h | 39 ++++ repos/gems/src/app/trace_recorder/target.mk | 5 + .../app/trace_recorder/timestamp_calibrator.h | 127 ++++++++++++ repos/gems/src/app/trace_recorder/writer.h | 62 ++++++ 42 files changed, 2690 insertions(+), 4 deletions(-) create mode 100644 repos/gems/recipes/pkg/trace_recorder/README create mode 100644 repos/gems/recipes/pkg/trace_recorder/archives create mode 100644 repos/gems/recipes/pkg/trace_recorder/hash create mode 100644 repos/gems/recipes/pkg/trace_recorder/runtime create mode 100644 repos/gems/recipes/raw/trace_recorder/content.mk create mode 100644 repos/gems/recipes/raw/trace_recorder/hash create mode 100644 repos/gems/recipes/src/trace_recorder/content.mk create mode 100644 repos/gems/recipes/src/trace_recorder/hash create mode 100644 repos/gems/recipes/src/trace_recorder/used_apis create mode 100644 repos/gems/recipes/src/trace_recorder_policy/content.mk create mode 100644 repos/gems/recipes/src/trace_recorder_policy/hash create mode 100644 repos/gems/recipes/src/trace_recorder_policy/used_apis create mode 100644 repos/gems/run/trace_recorder_ctf.run create mode 100644 repos/gems/run/trace_recorder_pcapng.run create mode 100644 repos/gems/src/app/trace_recorder/README create mode 100644 repos/gems/src/app/trace_recorder/backend.h create mode 100644 repos/gems/src/app/trace_recorder/config.xsd create mode 100644 repos/gems/src/app/trace_recorder/ctf/backend.cc create mode 100644 repos/gems/src/app/trace_recorder/ctf/backend.h create mode 100644 repos/gems/src/app/trace_recorder/ctf/metadata create mode 100644 repos/gems/src/app/trace_recorder/ctf/metadata.h rename repos/gems/{include/ctf/packet_types.h => src/app/trace_recorder/ctf/packet_header.h} (96%) create mode 100644 repos/gems/src/app/trace_recorder/ctf/write_buffer.h create mode 100644 repos/gems/src/app/trace_recorder/main.cc create mode 100644 repos/gems/src/app/trace_recorder/monitor.cc create mode 100644 repos/gems/src/app/trace_recorder/monitor.h create mode 100644 repos/gems/src/app/trace_recorder/named_registry.h create mode 100644 repos/gems/src/app/trace_recorder/pcapng/backend.cc create mode 100644 repos/gems/src/app/trace_recorder/pcapng/backend.h create mode 100644 repos/gems/src/app/trace_recorder/pcapng/block.h create mode 100644 repos/gems/src/app/trace_recorder/pcapng/enhanced_packet_block.h create mode 100644 repos/gems/src/app/trace_recorder/pcapng/interface_description_block.h create mode 100644 repos/gems/src/app/trace_recorder/pcapng/interface_registry.h create mode 100644 repos/gems/src/app/trace_recorder/pcapng/option.h create mode 100644 repos/gems/src/app/trace_recorder/pcapng/section_header_block.h create mode 100644 repos/gems/src/app/trace_recorder/pcapng/write_buffer.h create mode 100644 repos/gems/src/app/trace_recorder/policy.cc create mode 100644 repos/gems/src/app/trace_recorder/policy.h create mode 100644 repos/gems/src/app/trace_recorder/subject_info.h create mode 100644 repos/gems/src/app/trace_recorder/target.mk create mode 100644 repos/gems/src/app/trace_recorder/timestamp_calibrator.h create mode 100644 repos/gems/src/app/trace_recorder/writer.h diff --git a/repos/gems/recipes/pkg/trace_recorder/README b/repos/gems/recipes/pkg/trace_recorder/README new file mode 100644 index 0000000000..4c6233b377 --- /dev/null +++ b/repos/gems/recipes/pkg/trace_recorder/README @@ -0,0 +1 @@ +Trace recorder for collecting continuous traces in file system. diff --git a/repos/gems/recipes/pkg/trace_recorder/archives b/repos/gems/recipes/pkg/trace_recorder/archives new file mode 100644 index 0000000000..15ce2383f4 --- /dev/null +++ b/repos/gems/recipes/pkg/trace_recorder/archives @@ -0,0 +1,4 @@ +_/src/trace_recorder_policy +_/src/trace_recorder +_/raw/trace_recorder +_/src/vfs diff --git a/repos/gems/recipes/pkg/trace_recorder/hash b/repos/gems/recipes/pkg/trace_recorder/hash new file mode 100644 index 0000000000..74b75c363c --- /dev/null +++ b/repos/gems/recipes/pkg/trace_recorder/hash @@ -0,0 +1 @@ +2022-05-11 b0ff8e7876af3b8bd5fcf7d4055290d029e29198 diff --git a/repos/gems/recipes/pkg/trace_recorder/runtime b/repos/gems/recipes/pkg/trace_recorder/runtime new file mode 100644 index 0000000000..ac1937b812 --- /dev/null +++ b/repos/gems/recipes/pkg/trace_recorder/runtime @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/repos/gems/recipes/raw/trace_recorder/content.mk b/repos/gems/recipes/raw/trace_recorder/content.mk new file mode 100644 index 0000000000..aee25ae047 --- /dev/null +++ b/repos/gems/recipes/raw/trace_recorder/content.mk @@ -0,0 +1,4 @@ +content: metadata + +metadata: + cp $(REP_DIR)/src/app/trace_recorder/ctf/$@ $@ diff --git a/repos/gems/recipes/raw/trace_recorder/hash b/repos/gems/recipes/raw/trace_recorder/hash new file mode 100644 index 0000000000..4d5fe455a4 --- /dev/null +++ b/repos/gems/recipes/raw/trace_recorder/hash @@ -0,0 +1 @@ +2022-05-11 5e78bddea021b50315ea907a1cd003162439da22 diff --git a/repos/gems/recipes/src/trace_recorder/content.mk b/repos/gems/recipes/src/trace_recorder/content.mk new file mode 100644 index 0000000000..b922f72a76 --- /dev/null +++ b/repos/gems/recipes/src/trace_recorder/content.mk @@ -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 $@ diff --git a/repos/gems/recipes/src/trace_recorder/hash b/repos/gems/recipes/src/trace_recorder/hash new file mode 100644 index 0000000000..d7866bc6f8 --- /dev/null +++ b/repos/gems/recipes/src/trace_recorder/hash @@ -0,0 +1 @@ +2022-05-11 2cc9c248476c5b323740496cc1da0bf4fdbae1f9 diff --git a/repos/gems/recipes/src/trace_recorder/used_apis b/repos/gems/recipes/src/trace_recorder/used_apis new file mode 100644 index 0000000000..0652c7ef28 --- /dev/null +++ b/repos/gems/recipes/src/trace_recorder/used_apis @@ -0,0 +1,8 @@ +base +os +so +vfs +ctf +rtc_session +trace +trace_recorder_policy diff --git a/repos/gems/recipes/src/trace_recorder_policy/content.mk b/repos/gems/recipes/src/trace_recorder_policy/content.mk new file mode 100644 index 0000000000..efd6f92d7f --- /dev/null +++ b/repos/gems/recipes/src/trace_recorder_policy/content.mk @@ -0,0 +1,2 @@ +SRC_DIR = src/lib/trace_recorder/policy +include $(GENODE_DIR)/repos/base/recipes/src/content.inc diff --git a/repos/gems/recipes/src/trace_recorder_policy/hash b/repos/gems/recipes/src/trace_recorder_policy/hash new file mode 100644 index 0000000000..63238dc288 --- /dev/null +++ b/repos/gems/recipes/src/trace_recorder_policy/hash @@ -0,0 +1 @@ +2022-05-17 2c7fca33754a0cb26bc494a276cb285cdb6650d7 diff --git a/repos/gems/recipes/src/trace_recorder_policy/used_apis b/repos/gems/recipes/src/trace_recorder_policy/used_apis new file mode 100644 index 0000000000..b55b67f02f --- /dev/null +++ b/repos/gems/recipes/src/trace_recorder_policy/used_apis @@ -0,0 +1,5 @@ +base +os +ctf +trace +trace_recorder_policy diff --git a/repos/gems/run/trace_recorder_ctf.run b/repos/gems/run/trace_recorder_ctf.run new file mode 100644 index 0000000000..4310428a11 --- /dev/null +++ b/repos/gems/run/trace_recorder_ctf.run @@ -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 { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +} + +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 {} 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 diff --git a/repos/gems/run/trace_recorder_pcapng.run b/repos/gems/run/trace_recorder_pcapng.run new file mode 100644 index 0000000000..27d6770b7c --- /dev/null +++ b/repos/gems/run/trace_recorder_pcapng.run @@ -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 { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +} + +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 + diff --git a/repos/gems/src/app/trace_recorder/README b/repos/gems/src/app/trace_recorder/README new file mode 100644 index 0000000000..47c3ef5990 --- /dev/null +++ b/repos/gems/src/app/trace_recorder/README @@ -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: + +! +! +! +! +! +! + +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 '' node can contain an arbitray number of '' 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 '' 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 '' 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 '' 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'). diff --git a/repos/gems/src/app/trace_recorder/backend.h b/repos/gems/src/app/trace_recorder/backend.h new file mode 100644 index 0000000000..0dbb4ac1dc --- /dev/null +++ b/repos/gems/src/app/trace_recorder/backend.h @@ -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 +#include + +namespace Trace_recorder { + class Backend_base; + + using Backends = Named_registry; +} + + +class Trace_recorder::Backend_base : Backends::Element +{ + protected: + friend class Backends::Element; + friend class Avl_node; + friend class Avl_tree; + + 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 &, + Directory &, + Directory::Path const &) = 0; +}; + +#endif /* _BACKEND_H_ */ diff --git a/repos/gems/src/app/trace_recorder/config.xsd b/repos/gems/src/app/trace_recorder/config.xsd new file mode 100644 index 0000000000..737dc5bac3 --- /dev/null +++ b/repos/gems/src/app/trace_recorder/config.xsd @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/repos/gems/src/app/trace_recorder/ctf/backend.cc b/repos/gems/src/app/trace_recorder/ctf/backend.cc new file mode 100644 index 0000000000..ad1188d4fd --- /dev/null +++ b/repos/gems/src/app/trace_recorder/ctf/backend.cc @@ -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 + +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(), 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(); +} diff --git a/repos/gems/src/app/trace_recorder/ctf/backend.h b/repos/gems/src/app/trace_recorder/ctf/backend.h new file mode 100644 index 0000000000..77dd6596d1 --- /dev/null +++ b/repos/gems/src/app/trace_recorder/ctf/backend.h @@ -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 +#include +#include +#include + +#include +#include + +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 _dst_file { }; + Directory::Path _file_path { }; + + public: + Writer(Genode::Registry ®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 ®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_ */ diff --git a/repos/gems/src/app/trace_recorder/ctf/metadata b/repos/gems/src/app/trace_recorder/ctf/metadata new file mode 100644 index 0000000000..2e314f6ce2 --- /dev/null +++ b/repos/gems/src/app/trace_recorder/ctf/metadata @@ -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; + }; +}; diff --git a/repos/gems/src/app/trace_recorder/ctf/metadata.h b/repos/gems/src/app/trace_recorder/ctf/metadata.h new file mode 100644 index 0000000000..e4978afa06 --- /dev/null +++ b/repos/gems/src/app/trace_recorder/ctf/metadata.h @@ -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 +#include +#include + +namespace Ctf { + using namespace Genode; + + class Metadata; +} + +class Ctf::Metadata +{ + private: + Attached_rom_dataspace &_metadata_rom; + uint64_t _timestamp_freq; + + template + bool _with_metadata_rom(PROLOGUE && prologue, + EPILOGUE && epilogue) const + { + using Token = Genode::Token; + char const * rom_start = _metadata_rom.local_addr(); + 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_ */ diff --git a/repos/gems/include/ctf/packet_types.h b/repos/gems/src/app/trace_recorder/ctf/packet_header.h similarity index 96% rename from repos/gems/include/ctf/packet_types.h rename to repos/gems/src/app/trace_recorder/ctf/packet_header.h index 5e4d7a6ed8..1dfeaef220 100644 --- a/repos/gems/include/ctf/packet_types.h +++ b/repos/gems/src/app/trace_recorder/ctf/packet_header.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 #include @@ -130,4 +130,4 @@ struct Ctf::Packet_header } __attribute__((packed)); -#endif /* _CTF__PACKET_TYPES_H_ */ +#endif /* _CTF__PACKET_HEADER_H_ */ diff --git a/repos/gems/src/app/trace_recorder/ctf/write_buffer.h b/repos/gems/src/app/trace_recorder/ctf/write_buffer.h new file mode 100644 index 0000000000..5ee76f3122 --- /dev/null +++ b/repos/gems/src/app/trace_recorder/ctf/write_buffer.h @@ -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 +#include + +/* Genode includes */ +#include +#include + +namespace Ctf { + template + class Write_buffer; +} + +template +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(_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_ */ diff --git a/repos/gems/src/app/trace_recorder/main.cc b/repos/gems/src/app/trace_recorder/main.cc new file mode 100644 index 0000000000..7650f3e18b --- /dev/null +++ b/repos/gems/src/app/trace_recorder/main.cc @@ -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 + +/* Genode includes */ +#include +#include +#include + +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
_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); } diff --git a/repos/gems/src/app/trace_recorder/monitor.cc b/repos/gems/src/app/trace_recorder/monitor.cc new file mode 100644 index 0000000000..f10cc016d3 --- /dev/null +++ b/repos/gems/src/app/trace_recorder/monitor.cc @@ -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 Label_path; + + Label_path label_path = path_from_label(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(), 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(); +} diff --git a/repos/gems/src/app/trace_recorder/monitor.h b/repos/gems/src/app/trace_recorder/monitor.h new file mode 100644 index 0000000000..64b5511bff --- /dev/null +++ b/repos/gems/src/app/trace_recorder/monitor.h @@ -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 +#include +#include +#include +#include +#include + +/* Genode includes */ +#include +#include +#include +#include +#include + +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::Element _element; + Subject_info _info; + Trace::Subject_id _subject_id; + Registry _writers { }; + + public: + + Attached_buffer(Registry ®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 &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 _trace_buffers { }; + Policies _policies { }; + Backends _backends { }; + Constructible _trace_directory { }; + + Rtc::Connection _rtc { _env }; + Timer::Connection _timer { _env }; + Trace::Connection _trace { _env, + TRACE_SESSION_RAM, + TRACE_SESSION_ARG_BUFFER, + 0 }; + + Signal_handler _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_ */ diff --git a/repos/gems/src/app/trace_recorder/named_registry.h b/repos/gems/src/app/trace_recorder/named_registry.h new file mode 100644 index 0000000000..51e29f98b1 --- /dev/null +++ b/repos/gems/src/app/trace_recorder/named_registry.h @@ -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 +#include +#include + +namespace Trace_recorder { + using namespace Genode; + + template class Named_registry; + + template + static inline bool operator > (String const &s1, String const &s2) + { + return strcmp(s1.string(), s2.string()) > 0; + } +} + +template +class Trace_recorder::Named_registry : Noncopyable +{ + private: + + Avl_tree _tree { }; + + public: + + class Element : private Avl_node + { + public: + + using Name = Genode::String<64>; + Name const name; + + private: + + Named_registry &_registry; + + bool higher(T const *other) const { return name > other->name; } + + friend class Avl_tree; + friend class Avl_node; + friend class Named_registry; + + static T *_matching_sub_tree(T &curr, Name const &name) + { + typename Avl_node::Side side = (curr.name > name); + + return curr.Avl_node::child(side); + } + + public: + + Element(Named_registry ®istry, Name const &name) + : + name(name), _registry(registry) + { + _registry._tree.insert(this); + } + + ~Element() + { + _registry._tree.remove(this); + } + }; + + template + 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 + void for_each(FN && fn) { _tree.for_each(fn); } +}; + +#endif /* _NAMED_REGISTRY_H_ */ diff --git a/repos/gems/src/app/trace_recorder/pcapng/backend.cc b/repos/gems/src/app/trace_recorder/pcapng/backend.cc new file mode 100644 index 0000000000..20f5da2ca2 --- /dev/null +++ b/repos/gems/src/app/trace_recorder/pcapng/backend.cc @@ -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 +#include +#include +#include + +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 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(); + _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(); + + /* 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(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(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 ®istry, + Directory &, + Directory::Path const &) +{ + return *new (alloc) Writer(registry, _interface_registry, _buffer, _ts_calibrator); +} diff --git a/repos/gems/src/app/trace_recorder/pcapng/backend.h b/repos/gems/src/app/trace_recorder/pcapng/backend.h new file mode 100644 index 0000000000..957b6d1590 --- /dev/null +++ b/repos/gems/src/app/trace_recorder/pcapng/backend.h @@ -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 +#include +#include +#include + +/* Genode includes */ +#include + +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 _dst_file { }; + Directory::Path _file_path { }; + bool _empty_section { false }; + + public: + Writer(Genode::Registry ®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 &, + Directory &, + Directory::Path const &) override; +}; + + +#endif /* _PCAPNG__BACKEND_H_ */ diff --git a/repos/gems/src/app/trace_recorder/pcapng/block.h b/repos/gems/src/app/trace_recorder/pcapng/block.h new file mode 100644 index 0000000000..c621f864bb --- /dev/null +++ b/repos/gems/src/app/trace_recorder/pcapng/block.h @@ -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 + +namespace Pcapng { + struct Block_base; + + template + 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 +struct Pcapng::Block : Pcapng::Block_base +{ + Block() + : Block_base(TYPE_ID) + { } + + static uint32_t type() { return TYPE_ID; }; + +} __attribute__((packed)); + +#endif /* _PCAPNG__BLOCK_H_ */ diff --git a/repos/gems/src/app/trace_recorder/pcapng/enhanced_packet_block.h b/repos/gems/src/app/trace_recorder/pcapng/enhanced_packet_block.h new file mode 100644 index 0000000000..f9ec1fdd25 --- /dev/null +++ b/repos/gems/src/app/trace_recorder/pcapng/enhanced_packet_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 + +/* Genode includes */ +#include + +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_ */ diff --git a/repos/gems/src/app/trace_recorder/pcapng/interface_description_block.h b/repos/gems/src/app/trace_recorder/pcapng/interface_description_block.h new file mode 100644 index 0000000000..55ac4e7b3e --- /dev/null +++ b/repos/gems/src/app/trace_recorder/pcapng/interface_description_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 +#include + +/* genode includes */ +#include + +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(&_data[0], name); + Option_end &opt_end = *construct_at (&_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_ */ diff --git a/repos/gems/src/app/trace_recorder/pcapng/interface_registry.h b/repos/gems/src/app/trace_recorder/pcapng/interface_registry.h new file mode 100644 index 0000000000..de1063cbf8 --- /dev/null +++ b/repos/gems/src/app/trace_recorder/pcapng/interface_registry.h @@ -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 +#include + +namespace Pcapng { + using namespace Trace_recorder; + using namespace Genode; + + class Interface; + class Interface_registry; +} + + +class Pcapng::Interface +{ + public: + + using Name = String; + + private: + + Name const _name; + unsigned const _id; + + Registry::Element _element; + + public: + + Interface(Name const &name, unsigned id, Registry ®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 +{ + private: + + unsigned _next_id { 0 }; + Allocator &_alloc; + + public: + + Interface_registry(Allocator &alloc) + : _alloc(alloc) + { } + + /* apply to existing Interface or create new one */ + template + 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_ */ diff --git a/repos/gems/src/app/trace_recorder/pcapng/option.h b/repos/gems/src/app/trace_recorder/pcapng/option.h new file mode 100644 index 0000000000..38addc84ad --- /dev/null +++ b/repos/gems/src/app/trace_recorder/pcapng/option.h @@ -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 + +namespace Pcapng { + using namespace Genode; + + template + struct Option; + + struct Option_ifname; + struct Option_end; +} + +template +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 + 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(), name.string(), name.data_length()-1); + } +} __attribute__((packed)); + +#endif /* _PCAPNG__OPTION_H_ */ diff --git a/repos/gems/src/app/trace_recorder/pcapng/section_header_block.h b/repos/gems/src/app/trace_recorder/pcapng/section_header_block.h new file mode 100644 index 0000000000..4bec4917bc --- /dev/null +++ b/repos/gems/src/app/trace_recorder/pcapng/section_header_block.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 + +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_ */ diff --git a/repos/gems/src/app/trace_recorder/pcapng/write_buffer.h b/repos/gems/src/app/trace_recorder/pcapng/write_buffer.h new file mode 100644 index 0000000000..e7c0f34bd8 --- /dev/null +++ b/repos/gems/src/app/trace_recorder/pcapng/write_buffer.h @@ -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 +#include + +namespace Pcapng +{ + using namespace Genode; + + template + class Write_buffer; +} + +template +class Pcapng::Write_buffer +{ + public: + + enum class Append_error { OUT_OF_MEM, OVERFLOW }; + struct Append_ok { }; + using Append_result = Attempt; + + private: + + size_t _total_length { 0 }; + char _buffer[BUFSIZE] { }; + + public: + + template + 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(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_ */ diff --git a/repos/gems/src/app/trace_recorder/policy.cc b/repos/gems/src/app/trace_recorder/policy.cc new file mode 100644 index 0000000000..03bd389a42 --- /dev/null +++ b/repos/gems/src/app/trace_recorder/policy.cc @@ -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 + +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); +} diff --git a/repos/gems/src/app/trace_recorder/policy.h b/repos/gems/src/app/trace_recorder/policy.h new file mode 100644 index 0000000000..1c7737b870 --- /dev/null +++ b/repos/gems/src/app/trace_recorder/policy.h @@ -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 + +/* Genode includes */ +#include +#include +#include + +namespace Trace_recorder { + class Policy; + + using Policies = Named_registry; +} + + +/** + * Installs and maintains a tracing policy + */ +class Trace_recorder::Policy : Policies::Element +{ + private: + friend class Policies::Element; + friend class Genode::Avl_node; + friend class Genode::Avl_tree; + + 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_ */ diff --git a/repos/gems/src/app/trace_recorder/subject_info.h b/repos/gems/src/app/trace_recorder/subject_info.h new file mode 100644 index 0000000000..c26dd35411 --- /dev/null +++ b/repos/gems/src/app/trace_recorder/subject_info.h @@ -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 + +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_ */ diff --git a/repos/gems/src/app/trace_recorder/target.mk b/repos/gems/src/app/trace_recorder/target.mk new file mode 100644 index 0000000000..4660175b93 --- /dev/null +++ b/repos/gems/src/app/trace_recorder/target.mk @@ -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 diff --git a/repos/gems/src/app/trace_recorder/timestamp_calibrator.h b/repos/gems/src/app/trace_recorder/timestamp_calibrator.h new file mode 100644 index 0000000000..2675890fe6 --- /dev/null +++ b/repos/gems/src/app/trace_recorder/timestamp_calibrator.h @@ -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 +#include +#include +#include + +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_ */ diff --git a/repos/gems/src/app/trace_recorder/writer.h b/repos/gems/src/app/trace_recorder/writer.h new file mode 100644 index 0000000000..5dd95074e0 --- /dev/null +++ b/repos/gems/src/app/trace_recorder/writer.h @@ -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 + +/* Genode includes */ +#include +#include +#include +#include + +namespace Trace_recorder { + class Writer_base; + + using Directory = Genode::Directory; + using Writer_registry = Genode::Registry; +} + + +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_ */