mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-19 05:37:54 +00:00
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
This commit is contained in:
parent
25484f870e
commit
18b3253cac
5
repos/gems/lib/mk/vfs_trace.mk
Normal file
5
repos/gems/lib/mk/vfs_trace.mk
Normal file
@ -0,0 +1,5 @@
|
||||
SRC_CC = vfs.cc
|
||||
|
||||
vpath %.cc $(REP_DIR)/src/lib/vfs/trace
|
||||
|
||||
SHARED_LIB = yes
|
34
repos/gems/src/lib/vfs/trace/README
Normal file
34
repos/gems/src/lib/vfs/trace/README
Normal file
@ -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 <vfs> of your component as
|
||||
follow:
|
||||
|
||||
! <vfs>
|
||||
! <trace ram="16M"/>
|
||||
! </vfs>
|
||||
|
||||
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.
|
150
repos/gems/src/lib/vfs/trace/directory_tree.h
Normal file
150
repos/gems/src/lib/vfs/trace/directory_tree.h
Normal file
@ -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 <util/avl_string.h>
|
||||
#include "session_label.h"
|
||||
|
||||
|
||||
namespace Vfs {
|
||||
class Directory_tree;
|
||||
class Trace_node;
|
||||
struct Label;
|
||||
}
|
||||
|
||||
|
||||
namespace Genode {
|
||||
template <typename> class Avl_node_tree;
|
||||
}
|
||||
|
||||
|
||||
template <typename NT>
|
||||
class Genode::Avl_node_tree : public NT
|
||||
{
|
||||
protected:
|
||||
|
||||
using Tree = Avl_tree<NT>;
|
||||
using Node = Avl_node<NT>;
|
||||
|
||||
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<Genode::Avl_string_base>
|
||||
{
|
||||
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<Trace_node *>(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<Trace_node const &>(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<Trace_node>
|
||||
{
|
||||
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_ */
|
69
repos/gems/src/lib/vfs/trace/session_label.h
Normal file
69
repos/gems/src/lib/vfs/trace/session_label.h
Normal file
@ -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 <base/snprintf.h>
|
||||
#include <util/arg_string.h>
|
||||
#include <util/string.h>
|
||||
|
||||
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_ */
|
1
repos/gems/src/lib/vfs/trace/target.mk
Normal file
1
repos/gems/src/lib/vfs/trace/target.mk
Normal file
@ -0,0 +1 @@
|
||||
LIBS = vfs_trace
|
79
repos/gems/src/lib/vfs/trace/trace_buffer.h
Normal file
79
repos/gems/src/lib/vfs/trace/trace_buffer.h
Normal file
@ -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 <base/trace/buffer.h>
|
||||
|
||||
|
||||
/**
|
||||
* Wrapper for Trace::Buffer that adds some convenient functionality
|
||||
*/
|
||||
class Trace_buffer
|
||||
{
|
||||
private:
|
||||
|
||||
Genode::Trace::Buffer &_buffer;
|
||||
Genode::Trace::Buffer::Entry _curr { _buffer.first() };
|
||||
unsigned _wrapped_count { 0 };
|
||||
|
||||
public:
|
||||
|
||||
Trace_buffer(Genode::Trace::Buffer &buffer) : _buffer(buffer) { }
|
||||
|
||||
/**
|
||||
* Call functor for each entry that wasn't yet processed
|
||||
*/
|
||||
template <typename FUNC>
|
||||
void for_each_new_entry(FUNC && functor, 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_ */
|
228
repos/gems/src/lib/vfs/trace/value_file_system.h
Normal file
228
repos/gems/src/lib/vfs/trace/value_file_system.h
Normal file
@ -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 <util/xml_generator.h>
|
||||
#include <vfs/single_file_system.h>
|
||||
|
||||
namespace Vfs {
|
||||
template <typename, unsigned BUF_SIZE = 64>
|
||||
class Value_file_system;
|
||||
}
|
||||
|
||||
|
||||
template <typename T, unsigned BUF_SIZE>
|
||||
class Vfs::Value_file_system : public Vfs::Single_file_system
|
||||
{
|
||||
public:
|
||||
|
||||
typedef Genode::String<64> Name;
|
||||
|
||||
private:
|
||||
|
||||
typedef Genode::String<BUF_SIZE + 1> 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<Watch_handle>;
|
||||
|
||||
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_ */
|
474
repos/gems/src/lib/vfs/trace/vfs.cc
Normal file
474
repos/gems/src/lib/vfs/trace/vfs.cc
Normal file
@ -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 <base/attached_rom_dataspace.h>
|
||||
#include <vfs/dir_file_system.h>
|
||||
#include <vfs/single_file_system.h>
|
||||
|
||||
|
||||
#include <gems/vfs.h>
|
||||
#include <util/xml_generator.h>
|
||||
|
||||
#include <trace_session/connection.h>
|
||||
|
||||
#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<Trace_buffer> _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 <typename FUNC>
|
||||
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<bool, 6> _enabled_fs
|
||||
{ "enable", "false\n"};
|
||||
|
||||
Value_file_system<Number_of_bytes, 16> _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<unsigned>::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<Subject> _enable_handler {
|
||||
_enabled_fs, "/enable",
|
||||
Subject_factory::_env.alloc(),
|
||||
*this, &Subject::_enable_subject };
|
||||
|
||||
Watch_handler<Subject> _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<Attached_rom_dataspace> 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<void*>(), 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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user