From 18b3253cac4100da7a586a1c02dbf924c5e98644 Mon Sep 17 00:00:00 2001 From: Sebastian Sumpf Date: Thu, 11 Apr 2019 11:20:48 +0200 Subject: [PATCH] vfs_trace: VFS plugin that offers trace buffer access The plugin creates a file-system hierarchy that enabled the access of trace buffers for each component and its threads. issue #3294 --- repos/gems/lib/mk/vfs_trace.mk | 5 + repos/gems/src/lib/vfs/trace/README | 34 ++ repos/gems/src/lib/vfs/trace/directory_tree.h | 150 ++++++ repos/gems/src/lib/vfs/trace/session_label.h | 69 +++ repos/gems/src/lib/vfs/trace/target.mk | 1 + repos/gems/src/lib/vfs/trace/trace_buffer.h | 79 +++ .../src/lib/vfs/trace/value_file_system.h | 228 +++++++++ repos/gems/src/lib/vfs/trace/vfs.cc | 474 ++++++++++++++++++ 8 files changed, 1040 insertions(+) create mode 100644 repos/gems/lib/mk/vfs_trace.mk create mode 100644 repos/gems/src/lib/vfs/trace/README create mode 100644 repos/gems/src/lib/vfs/trace/directory_tree.h create mode 100644 repos/gems/src/lib/vfs/trace/session_label.h create mode 100644 repos/gems/src/lib/vfs/trace/target.mk create mode 100644 repos/gems/src/lib/vfs/trace/trace_buffer.h create mode 100644 repos/gems/src/lib/vfs/trace/value_file_system.h create mode 100644 repos/gems/src/lib/vfs/trace/vfs.cc diff --git a/repos/gems/lib/mk/vfs_trace.mk b/repos/gems/lib/mk/vfs_trace.mk new file mode 100644 index 0000000000..a680307628 --- /dev/null +++ b/repos/gems/lib/mk/vfs_trace.mk @@ -0,0 +1,5 @@ +SRC_CC = vfs.cc + +vpath %.cc $(REP_DIR)/src/lib/vfs/trace + +SHARED_LIB = yes diff --git a/repos/gems/src/lib/vfs/trace/README b/repos/gems/src/lib/vfs/trace/README new file mode 100644 index 0000000000..7cdd7768e2 --- /dev/null +++ b/repos/gems/src/lib/vfs/trace/README @@ -0,0 +1,34 @@ +The VFS trace plugin offers access to Genode's trace session by providing a +file-system that can be mounted at arbitrary location within the VFS of a +component. The file system forms a directory structure that recursively +represents the parent-child relationship of running component. The leave +directories represent the threads within a component. Currently there are three +files for each thread that can be accessed: + +:'enable': Start or stops tracing of a thread by write "true" or "false" into + file. + +:'buffer_size': Allows the configuration of the trace-buffer size for the + thread in the usual Genode format (e.g. 5M, 512K, 1024). + +:'trace_buffer': This read only file contains the current content of the trace + buffer. Every trace entry can only be read once, after that + only new entries appear. "tail -f" can also be used in order to + display continuous output. + +In order to mount the file system configure the of your component as +follow: + +! +! +! + +The "ram" attribute is mandatory and configures the quota of the trace-session +in the plugin. It limits the number of thread that can be traced at once. + +Limitations: + +The plugin retrieves up to 128 running components at load time. There is +currently no support to add or remove components once the plugin has been +started. This implies that components that are started at a later stage will not +appear within the file system. diff --git a/repos/gems/src/lib/vfs/trace/directory_tree.h b/repos/gems/src/lib/vfs/trace/directory_tree.h new file mode 100644 index 0000000000..7930740548 --- /dev/null +++ b/repos/gems/src/lib/vfs/trace/directory_tree.h @@ -0,0 +1,150 @@ +/* + * \brief A tree of AVL trees that forms a directory structure + * \author Sebastian Sumpf + * \date 2019-06-14 + */ + +/* + * Copyright (C) 2019 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 _DIRECTORY_TREE_H_ +#define _DIRECTORY_TREE_H_ + +#include +#include "session_label.h" + + +namespace Vfs { + class Directory_tree; + class Trace_node; + struct Label; +} + + +namespace Genode { + template class Avl_node_tree; +} + + +template +class Genode::Avl_node_tree : public NT +{ + protected: + + using Tree = Avl_tree; + using Node = Avl_node; + + Tree _tree { }; + + public: + + using NT::NT; + + void insert(Node *node) { _tree.insert(node); } + + Tree &tree() { return _tree; } + + Node *find_by_name(char const *name) + { + if (!_tree.first()) return nullptr; + + Node *node = _tree.first()->find_by_name(name); + return node; + } +}; + + +struct Vfs::Label : Genode::String<32> +{ + using String::String; +}; + + +class Vfs::Trace_node : public Vfs::Label, + public Avl_node_tree +{ + private: + + Allocator &_alloc; + Trace::Subject_id const _id; + + Trace_node *_find_by_name(char const *name) + { + Node *node = find_by_name(name); + return node ? static_cast(node) : nullptr; + } + + public: + + Trace_node(Genode::Allocator &alloc, Session_label const &label, + Trace::Subject_id const id = 0) + : Vfs::Label(label), Avl_node_tree(string()), + _alloc(alloc), _id(id) + { } + + Trace_node &insert(Session_label const &label) + { + if (!label.valid()) return *this; + + Trace_node *node = _find_by_name(label.first_element().string()); + if (!node) { + node = new(_alloc) Trace_node(_alloc, label.first_element()); + Avl_node_tree::insert(node); + } + + return node->insert(label.suffix()); + } + + void xml(Genode::Xml_generator &xml) const + { + _tree.for_each([&] (Genode::Avl_string_base const &name) { + Trace_node const &node = static_cast(name); + + if (node.id() == 0) + xml.node("dir", [&] () { + xml.attribute("name", node.name()); + node.xml(xml); + }); + else + xml.node("trace_node", [&] () { + xml.attribute("name", node.name()); + xml.attribute("id", node.id().id); + node.xml(xml); + }); + }); + } + + Trace::Subject_id const &id() const { return _id; } +}; + + +class Vfs::Directory_tree : public Genode::Avl_tree +{ + private: + + Allocator &_alloc; + Trace_node _root { _alloc, Session_label() }; + + public: + + Directory_tree(Genode::Allocator &alloc) + : _alloc(alloc) { } + + void insert(Trace::Subject_info const &info, Trace::Subject_id const id) + { + Trace_node &leaf = _root.insert(info.session_label()); + Trace_node *node = new (_alloc) Trace_node(_alloc, info.thread_name(), id); + leaf.Avl_node_tree::insert(node); + } + + void xml(Genode::Xml_generator &xml) + { + _root.xml(xml); + } +}; + +#endif /* _DIRECTORY_TREE_H_ */ diff --git a/repos/gems/src/lib/vfs/trace/session_label.h b/repos/gems/src/lib/vfs/trace/session_label.h new file mode 100644 index 0000000000..164dc42e49 --- /dev/null +++ b/repos/gems/src/lib/vfs/trace/session_label.h @@ -0,0 +1,69 @@ +/* + * \brief Session label extension + * \author Sebastian Sumpf + * \date 2019-06-04 + */ + +/* + * Copyright (C) 2019 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 _SESSION_LABEL_H_ +#define _SESSION_LABEL_H_ + +#include +#include +#include + +namespace Vfs { +using namespace Genode; +struct Session_label; +} + +struct Vfs::Session_label : Genode::Session_label +{ + private: + + static char const *_separator() { return " -> "; } + static size_t _separator_len() { return 4; } + + public: + + using Genode::Session_label::Session_label; + + Session_label first_element() const + { + char const * const full = string(); + if (length() < _separator_len() + 1) + return Session_label(Cstring(full)); + + unsigned prefix_len; + + for (prefix_len = 0; prefix_len < length() - 1; prefix_len++) + if (!strcmp(_separator(), full + prefix_len, _separator_len())) + break; + + return Session_label(Cstring(full, prefix_len)); + } + + /** + * Return part of the label without first element + */ + Session_label suffix() const + { + if (length() < _separator_len() + 1) + return Session_label(); + + char const * const full = string(); + for (unsigned prefix_len = 0; prefix_len < length() - 1; prefix_len++) + if (!strcmp(_separator(), full + prefix_len, _separator_len())) + return full + prefix_len + _separator_len(); + + return Session_label(); + } +}; + +#endif /* _SESSION_LABEL_H_ */ diff --git a/repos/gems/src/lib/vfs/trace/target.mk b/repos/gems/src/lib/vfs/trace/target.mk new file mode 100644 index 0000000000..22e280a77a --- /dev/null +++ b/repos/gems/src/lib/vfs/trace/target.mk @@ -0,0 +1 @@ +LIBS = vfs_trace diff --git a/repos/gems/src/lib/vfs/trace/trace_buffer.h b/repos/gems/src/lib/vfs/trace/trace_buffer.h new file mode 100644 index 0000000000..17fc3b0c73 --- /dev/null +++ b/repos/gems/src/lib/vfs/trace/trace_buffer.h @@ -0,0 +1,79 @@ +/* + * \brief Wrapper for Trace::Buffer that adds some convenient functionality + * \author Martin Stein + * \date 2018-01-12 + */ + +/* + * Copyright (C) 2018 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _TRACE_BUFFER_H_ +#define _TRACE_BUFFER_H_ + +/* Genode includes */ +#include + + +/** + * Wrapper for Trace::Buffer that adds some convenient functionality + */ +class Trace_buffer +{ + private: + + Genode::Trace::Buffer &_buffer; + Genode::Trace::Buffer::Entry _curr { _buffer.first() }; + unsigned _wrapped_count { 0 }; + + public: + + Trace_buffer(Genode::Trace::Buffer &buffer) : _buffer(buffer) { } + + /** + * Call functor for each entry that wasn't yet processed + */ + template + void for_each_new_entry(FUNC && functor, bool update = true) + { + using namespace Genode; + + bool wrapped = _buffer.wrapped() != _wrapped_count; + if (wrapped) + _wrapped_count = _buffer.wrapped(); + + /* initialize _curr if _buffer was empty until now */ + Trace::Buffer::Entry curr { _curr }; + if (_curr.last()) + curr = _buffer.first(); + + /* iterate over all entries that were not processed yet */ + Trace::Buffer::Entry e1 = curr; + for (Trace::Buffer::Entry e2 = curr; wrapped || !e2.last(); + e2 = _buffer.next(e2)) { + /* if buffer wrapped, we pass the last entry once and continue at first entry */ + if (wrapped && e2.last()) { + wrapped = false; + e2 = _buffer.first(); + if (e2.last()) + break; + } + + e1 = e2; + if (!functor(e1)) + break; + } + + /* remember the last processed entry in _curr */ + curr = e1; + if (update) _curr = curr; + } + + void * address() const { return &_buffer; } +}; + + +#endif /* _TRACE_BUFFER_H_ */ diff --git a/repos/gems/src/lib/vfs/trace/value_file_system.h b/repos/gems/src/lib/vfs/trace/value_file_system.h new file mode 100644 index 0000000000..6c62c48c42 --- /dev/null +++ b/repos/gems/src/lib/vfs/trace/value_file_system.h @@ -0,0 +1,228 @@ +/* + * \brief File system for providing a value as a file + * \author Josef Soentgen + * \author Sebastian Sumpf + * \date 2018-11-24 + */ + +/* + * Copyright (C) 2018-2019 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 _VALUE_FILE_SYSTEM_H_ +#define _VALUE_FILE_SYSTEM_H_ + +/* Genode includes */ +#include +#include + +namespace Vfs { + template + class Value_file_system; +} + + +template +class Vfs::Value_file_system : public Vfs::Single_file_system +{ + public: + + typedef Genode::String<64> Name; + + private: + + typedef Genode::String Buffer; + + Name const _file_name; + + Buffer _buffer { }; + + struct Vfs_handle : Single_vfs_handle + { + Value_file_system &_value_fs; + Buffer &_buffer{ _value_fs._buffer }; + + Vfs_handle(Value_file_system &value_fs, + Allocator &alloc) + : + Single_vfs_handle(value_fs, value_fs, alloc, 0), + _value_fs(value_fs) + { } + + Read_result read(char *dst, file_size count, + file_size &out_count) override + { + out_count = 0; + + if (seek() > _buffer.length()) + return READ_ERR_INVALID; + + char const * const src = _buffer.string() + seek(); + Genode::size_t const len = min(_buffer.length() - seek(), count); + Genode::memcpy(dst, src, len); + + out_count = len; + return READ_OK; + } + + Write_result write(char const *src, file_size count, file_size &out_count) override + { + out_count = 0; + if (seek() > BUF_SIZE) + return WRITE_ERR_INVALID; + + Genode::size_t const len = min(BUF_SIZE- seek(), count); + _buffer = Buffer(Cstring(src, len)); + out_count = len; + + /* inform watchers */ + _value_fs._watch_response(); + + return WRITE_OK; + } + + bool read_ready() override { return true; } + + private: + + Vfs_handle(Vfs_handle const &); + Vfs_handle &operator = (Vfs_handle const &); + }; + + struct Watch_handle; + using Watch_handle_registry = Genode::Registry; + + struct Watch_handle : Vfs_watch_handle + { + typename Watch_handle_registry::Element elem; + + Watch_handle(Watch_handle_registry ®istry, + Vfs::File_system &fs, + Allocator &alloc) + : Vfs_watch_handle(fs, alloc), elem(registry, *this) { } + }; + + Watch_handle_registry _watch_handle_registry { }; + + + void _watch_response() { + _watch_handle_registry.for_each([&] (Watch_handle &h) { + h.watch_response(); + }); + } + + typedef Genode::String<200> Config; + Config _config(Name const &name) const + { + char buf[Config::capacity()] { }; + Genode::Xml_generator xml(buf, sizeof(buf), type_name(), [&] () { + xml.attribute("name", name); }); + return Config(Genode::Cstring(buf)); + } + + + public: + + Value_file_system(Name const &name, Buffer const &initial_value) + : + Single_file_system(NODE_TYPE_CHAR_DEVICE, type(), + Xml_node(_config(name).string())), + _file_name(name) + { + value(initial_value); + } + + static char const *type_name() { return "value"; } + + char const *type() override { return type_name(); } + + void value(Buffer const &value) + { + _buffer = Buffer(value); + } + + T value() + { + T val { 0 }; + Genode::ascii_to(_buffer.string(), val); + + return val; + } + + Buffer buffer() const { return _buffer; } + + bool matches(Xml_node node) const + { + return node.has_type(type_name()) && + node.attribute_value("name", Name()) == _file_name; + } + + + /******************************** + ** File I/O service interface ** + ********************************/ + + Ftruncate_result ftruncate(Vfs::Vfs_handle *, file_size size) override + { + if (size >= BUF_SIZE) + return FTRUNCATE_ERR_NO_SPACE; + + return FTRUNCATE_OK; + } + + + /********************************* + ** Directory-service interface ** + *********************************/ + + Open_result open(char const *path, unsigned, + Vfs::Vfs_handle **out_handle, + Allocator &alloc) override + { + if (!_single_file(path)) + return OPEN_ERR_UNACCESSIBLE; + + try { + *out_handle = new (alloc) Vfs_handle(*this, alloc); + return OPEN_OK; + } + catch (Genode::Out_of_ram) { Genode::error("out of ram"); return OPEN_ERR_OUT_OF_RAM; } + catch (Genode::Out_of_caps) { Genode::error("out of caps");return OPEN_ERR_OUT_OF_CAPS; } + } + + Stat_result stat(char const *path, Stat &out) override + { + Stat_result result = Single_file_system::stat(path, out); + out.mode |= 0666; + out.size = BUF_SIZE + 1; + return result; + } + + Watch_result watch(char const *path, + Vfs_watch_handle **handle, + Allocator &alloc) override + { + if (!_single_file(path)) + return WATCH_ERR_UNACCESSIBLE; + + try { + Watch_handle *wh = new (alloc) + Watch_handle(_watch_handle_registry, *this, alloc); + *handle = wh; + return WATCH_OK; + } + catch (Genode::Out_of_ram) { return WATCH_ERR_OUT_OF_RAM; } + catch (Genode::Out_of_caps) { return WATCH_ERR_OUT_OF_CAPS; } + } + + void close(Vfs_watch_handle *handle) override + { + if (handle && (&handle->fs() == this)) + destroy(handle->alloc(), handle); + } +}; + +#endif /* _VALUE_FILE_SYSTEM_H_ */ diff --git a/repos/gems/src/lib/vfs/trace/vfs.cc b/repos/gems/src/lib/vfs/trace/vfs.cc new file mode 100644 index 0000000000..aa19e90e9e --- /dev/null +++ b/repos/gems/src/lib/vfs/trace/vfs.cc @@ -0,0 +1,474 @@ +/* + * \brief File system for trace buffer access + * \author Sebastian Sumpf + * \date 2019-06-13 + */ + +/* + * Copyright (C) 2019 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. + */ + +#include +#include +#include + + +#include +#include + +#include + +#include "directory_tree.h" +#include "trace_buffer.h" +#include "value_file_system.h" + + +namespace Vfs_trace { + + using namespace Vfs; + using namespace Genode; + using Name = String<32>; + + struct File_system; + class Local_factory; + class Subject; + struct Subject_factory; + class Trace_buffer_file_system; +} + + +class Vfs_trace::Trace_buffer_file_system : public Single_file_system +{ + private: + + /** + * Trace_buffer wrapper + */ + struct Trace_entries + { + Vfs::Env &_env; + Constructible _buffer { }; + + Trace_entries(Vfs::Env &env) : _env(env) { } + + void setup(Dataspace_capability ds) + { + _buffer.construct(*((Trace::Buffer *)_env.env().rm().attach(ds))); + } + + void flush() + { + if (!_buffer.constructed()) return; + + _env.env().rm().detach(_buffer->address()); + _buffer.destruct(); + } + + template + void for_each_new_entry(FUNC && functor, bool update = true) { + if (!_buffer.constructed()) return; + _buffer->for_each_new_entry(functor, update); + } + }; + + enum State { OFF, TRACE, PAUSED } _state { OFF }; + + Vfs::Env &_env; + Trace::Connection &_trace; + Trace::Policy_id _policy; + Trace::Subject_id _id; + size_t _buffer_size { 1024 * 1024 }; + size_t _stat_size { 0 }; + Trace_entries _entries { _env }; + + + typedef String<32> Config; + + static Config _config() + { + char buf[Config::capacity()] { }; + + Xml_generator xml(buf, sizeof(buf), type_name(), [&] () { }); + + return Config(Cstring(buf)); + } + + void _setup_and_trace() + { + _entries.flush(); + + try { + _trace.trace(_id, _policy, _buffer_size); + } catch (...) { + error("failed to start tracing"); + return; + } + + _entries.setup(_trace.buffer(_id)); + } + + public: + + struct Vfs_handle : Single_vfs_handle + { + Trace_entries &_entries; + + Vfs_handle(Directory_service &ds, + File_io_service &fs, + Genode::Allocator &alloc, + Trace_entries &entries) + : Single_vfs_handle(ds, fs, alloc, 0), _entries(entries) + { } + + Read_result read(char *dst, file_size count, + file_size &out_count) override + { + out_count = 0; + _entries.for_each_new_entry([&](Trace::Buffer::Entry entry) { + file_size size = min(count - out_count, entry.length()); + memcpy(dst + out_count, entry.data(), size); + out_count += size; + + if (out_count == count) + return false; + + return true; + }); + + return READ_OK; + } + + Write_result write(char const *, file_size, + file_size &out_count) override + { + out_count = 0; + return WRITE_ERR_INVALID; + } + + bool read_ready() override { return true; } + }; + + Trace_buffer_file_system(Vfs::Env &env, + Trace::Connection &trace, + Trace::Policy_id policy, + Trace::Subject_id id) + : Single_file_system(NODE_TYPE_CHAR_DEVICE, + type_name(), Xml_node(_config().string())), + _env(env), _trace(trace), _policy(policy), _id(id) + { } + + static char const *type_name() { return "trace_buffer"; } + char const *type() override { return type_name(); } + + + /*************************** + ** File-system interface ** + ***************************/ + + Open_result open(char const *path, unsigned, + Vfs::Vfs_handle **out_handle, + Allocator &alloc) override + { + if (!_single_file(path)) + return OPEN_ERR_UNACCESSIBLE; + + *out_handle = new (alloc) Vfs_handle(*this, *this, alloc, _entries); + return OPEN_OK; + } + + Stat_result stat(char const *path, Stat &out) override + { + Stat_result res = Single_file_system::stat(path, out); + if (res != STAT_OK) return res; + + /* update file size */ + if (_state == TRACE) + _entries.for_each_new_entry([&](Trace::Buffer::Entry entry) { + _stat_size += entry.length(); return true; }, false); + + out.size = _stat_size; + + return res; + } + + + /*********************** + ** FS event handlers ** + ***********************/ + + bool resize_buffer(size_t size) + { + if (size == 0) return false; + + _buffer_size = size; + + switch (_state) { + case TRACE: + _trace.pause(_id); + _setup_and_trace(); + break; + case PAUSED: + _state = OFF; + break; + case OFF: + break; + } + return true; + } + + void trace(bool enable) + { + if (enable) { + switch (_state) { + case TRACE: break; + case OFF: _setup_and_trace(); break; + case PAUSED: _trace.resume(_id); break; + + } + _state = TRACE; + } else { + switch (_state) { + case OFF: return; + case PAUSED: return; + case TRACE: _trace.pause(_id); _state = PAUSED; return; + } + } + } +}; + + +struct Vfs_trace::Subject_factory : File_system_factory +{ + Vfs::Env &_env; + Value_file_system _enabled_fs + { "enable", "false\n"}; + + Value_file_system _buffer_size_fs + { "buffer_size", "1M\n"}; + + String<17> _buffer_string + { _buffer_size_fs.buffer() }; + + Trace_buffer_file_system _trace_fs; + + Subject_factory(Vfs::Env &env, + Trace::Connection &trace, + Trace::Policy_id policy, + Trace::Subject_id id) + : _env(env), _trace_fs(env, trace, policy, id) { } + + Vfs::File_system *create(Vfs::Env &, Xml_node node) override + { + if (node.has_type(Value_file_system::type_name())) { + if (_enabled_fs.matches(node)) return &_enabled_fs; + if (_buffer_size_fs.matches(node)) return &_buffer_size_fs; + } + + if (node.has_type(Trace_buffer_file_system::type_name())) + return &_trace_fs; + + return nullptr; + } +}; + + +class Vfs_trace::Subject : private Subject_factory, + public Vfs::Dir_file_system +{ + private: + + typedef String<200> Config; + + Watch_handler _enable_handler { + _enabled_fs, "/enable", + Subject_factory::_env.alloc(), + *this, &Subject::_enable_subject }; + + Watch_handler _buffer_size_handler { + _buffer_size_fs, "/buffer_size", + Subject_factory::_env.alloc(), + *this, &Subject::_buffer_size }; + + + static Config _config(Xml_node node) + { + char buf[Config::capacity()] { }; + + Xml_generator xml(buf, sizeof(buf), "dir", [&] () { + xml.attribute("name", node.attribute_value("name", Vfs_trace::Name())); + xml.node("value", [&] () { xml.attribute("name", "enable"); }); + xml.node("value", [&] () { xml.attribute("name", "buffer_size"); }); + xml.node(Trace_buffer_file_system::type_name(), [&] () {}); + }); + + return Config(Cstring(buf)); + } + + + /******************** + ** Watch handlers ** + ********************/ + + void _enable_subject() + { + _enabled_fs.value(_enabled_fs.value() ? "true\n" : "false\n"); + _trace_fs.trace(_enabled_fs.value()); + } + + void _buffer_size() + { + Number_of_bytes size = _buffer_size_fs.value(); + + if (_trace_fs.resize_buffer(size) == false) { + /* restore old value */ + _buffer_size_fs.value(_buffer_string); + return; + } + + _buffer_string = _buffer_size_fs.buffer(); + } + + public: + + Subject(Vfs::Env &env, Trace::Connection &trace, + Trace::Policy_id policy, Xml_node node) + : Subject_factory(env, trace, policy, node.attribute_value("id", 0u)), + Dir_file_system(env, Xml_node(_config(node).string()), *this) + { } + + + static char const *type_name() { return "trace_node"; } + char const *type() override { return type_name(); } +}; + + +struct Vfs_trace::Local_factory : File_system_factory +{ + Vfs::Env &_env; + + Trace::Connection _trace; + enum { MAX_SUBJECTS = 128 }; + Trace::Subject_id _subjects[MAX_SUBJECTS]; + unsigned _subject_count { 0 }; + Trace::Policy_id _policy_id { 0 }; + + Directory_tree _tree { _env.alloc() }; + + void _install_null_policy() + { + using namespace Genode; + Constructible null_policy; + + try { + null_policy.construct(_env.env(), "null"); + _policy_id = _trace.alloc_policy(null_policy->size()); + } + catch (Out_of_caps) { throw; } + catch (Out_of_ram) { throw; } + catch (...) { + error("failed to attach 'null' trace policy." + "Please make sure it is provided as a ROM module."); + throw; + } + + /* copy policy into trace session */ + void *dst = _env.env().rm().attach(_trace.policy(_policy_id)); + memcpy(dst, null_policy->local_addr(), null_policy->size()); + _env.env().rm().detach(dst); + } + + size_t _config_session_ram(Xml_node config) + { + try { + Genode::Number_of_bytes ram; + config.attribute("ram").value(&ram); + return ram; + } catch (...) { + Genode::error("mandatory 'ram' attribute missing"); + throw Genode::Exception(); + } + } + + Local_factory(Vfs::Env &env, Xml_node config) + : _env(env), _trace(env.env(), _config_session_ram(config), 512*1024, 0) + { + bool success = false; + while (!success) { + try { + _subject_count = _trace.subjects(_subjects, MAX_SUBJECTS); + success = true; + } catch(Genode::Out_of_ram) { + _trace.upgrade_ram(4096); + success = false; + } + } + + for (unsigned i = 0; i < _subject_count; i++) { + _tree.insert(_trace.subject_info(_subjects[i]), _subjects[i]); + } + + _install_null_policy(); + } + + Vfs::File_system *create(Vfs::Env&, Xml_node node) override + { + if (node.has_type(Subject::type_name())) + return new (_env.alloc()) Subject(_env, _trace, _policy_id, node); + + return nullptr; + } +}; + + +class Vfs_trace::File_system : private Local_factory, + public Vfs::Dir_file_system +{ + private: + + typedef String<512*1024> Config; + + static char const *_config(Vfs::Env &vfs_env, Directory_tree &tree) + { + char *buf = (char *)vfs_env.alloc().alloc(Config::capacity()); + Xml_generator xml(buf, Config::capacity(), "node", [&] () { + tree.xml(xml); + }); + + return buf; + } + + public: + + File_system(Vfs::Env &vfs_env, Genode::Xml_node node) + : Local_factory(vfs_env, node), + Vfs::Dir_file_system(vfs_env, Xml_node(_config(vfs_env, _tree)), *this) + { } + + char const *type() override { return "trace"; } +}; + + +/************************** + ** VFS plugin interface ** + **************************/ + +extern "C" Vfs::File_system_factory *vfs_file_system_factory(void) +{ + struct Factory : Vfs::File_system_factory + { + Vfs::File_system *create(Vfs::Env &vfs_env, + Genode::Xml_node node) override + { + try { return new (vfs_env.alloc()) + Vfs_trace::File_system(vfs_env, node); } + catch (...) { Genode::error("could not create 'trace_fs' "); } + return nullptr; + } + }; + + static Factory factory; + return &factory; +}