os: remove trace_fs

This component is superseded by the 'vfs_trace' plugin.

Issue #3294.
This commit is contained in:
Josef Söntgen 2019-06-18 14:38:09 +02:00 committed by Christian Helmuth
parent 57e650ef2c
commit d32e8390c2
12 changed files with 0 additions and 3231 deletions

View File

@ -1,61 +0,0 @@
The new _trace_fs_ server provides access to a trace session by providing a
file-system session as front end. Combined with Noux, it allows for the
interactive exploration and tracing of Genode's process tree using
traditional Unix tools.
Each trace subject is represented by a directory ('thread_name.subject') that
contains specific files, which are used to control the tracing process of the
thread as well as storing the content of its trace buffer:
:'enable': The tracing of a thread is activated if there is a valid policy
installed and the intend to trace the subject was made clear by writing '1'
to the 'enable' file. The tracing of a thread may be deactivated by writing a
'0' to this file.
:'policy': A policy may be changed by overwriting the currently used one in the
'policy' file. In this case, the old policy is replaced by the new one and
automatically used by the framework.
:'buffer_size': Writing a value to the 'buffer_size' file changes the size of
the trace buffer. This value is evaluated only when reactivating the tracing
of the thread.
:'events': The trace-buffer contents may be accessed by reading from the
'events' file. New trace events are appended to this file.
:'active': Reading the file will return whether the tracing is active (1) or
not (0).
:'cleanup': Nodes of untraced subjects are kept as long as they do not change
their tracing state to dead. Dead untraced nodes are automatically removed
from the file system. Subjects that were traced before and are now untraced
can be removed by writing '1' to the 'cleanup' file.
To use the trace_fs, a configuration similar to the following may be used:
! <start name="trace_fs">
! <resource name="RAM" quantum="128M"/>
! <provides><service name="File_system"/></provides>
! <config>
! <policy label_prefix="noux -> trace"
! interval="1000"
! subject_limit="512"
! trace_quota="64M" />
! </config>
! </start>
:'interval': sets the period the Trace_session is polled. The
time is given in milliseconds.
:'subject_limit': specifies how many trace subjects should by acquired at
max when the Trace_session is polled.
:'trace_quota': is the amount of quota the trace_fs should use for the
Trace_session connection. The remaining amount of RAM quota will be used
for the actual nodes of the file system and the 'policy' as well as the
'events' files.
In addition, there are 'buffer_size' and 'buffer_size_limit' that define
the initial and the upper limit of the size of a trace buffer.
A ready-to-use run script can by found in 'ports/run/noux_trace_fs.run'.

View File

@ -1,134 +0,0 @@
/*
* \brief Buffer class
* \author Josef Soentgen
* \date 2014-01-15
*/
/*
* Copyright (C) 2014-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _BUFFER_H_
#define _BUFFER_H_
/* Genode includes */
#include <util/string.h>
namespace Util {
typedef Genode::size_t size_t;
/**
* Buffer merely wrapps a simple char array
*/
template <size_t CAPACITY>
class Buffer
{
public:
class Out_of_range { };
private:
char _buf[CAPACITY];
size_t _length;
public:
static size_t capacity() { return CAPACITY; }
Buffer() : _length(0) { }
Buffer(char const *s)
:
_length(Genode::min(Genode::strlen(s) + 1, CAPACITY))
{
Genode::strncpy(_buf, s, _length);
}
char const *data() const { return (_buf[_length -1 ] == '\0') ? _buf : ""; }
char *data() { return _buf; }
size_t length() const { return _length; }
char & operator[](size_t i)
{
if (i >= CAPACITY)
throw Out_of_range();
return _buf[i];
}
void replace(char p, char c)
{
char *s = _buf;
for (; *s; s++) {
if (*s == p)
*s = c;
}
}
};
/**
* This class walks along a label and returns the next element on request
*/
class Label_walker
{
private:
Buffer<64> _buffer;
char const *_label;
char const *_next()
{
size_t i = 0;
for (; *_label && (i < _buffer.capacity()); _label++, i++) {
/* check seperator */
if ((*_label == ' ') &&
(*(_label + 1) == '-') &&
(*(_label + 2) == '>') &&
(*(_label + 3) == ' '))
break;
_buffer[i] = *_label;
}
_buffer[i] = '\0';
/* sanatize the element */
_buffer.replace('/', '_');
/* omit seperator */
if (*_label)
_label += 4;
return _label;
}
public:
Label_walker(char const *label) : _label(label) { }
/**
* Walk to the next element of the label
*
* \return pointer to the remaing part of the label
*/
char const *next() { return _next(); }
/**
* Get current element of the label
*
* \return pointer to current element
*/
char const *element() { return _buffer.data(); }
};
}
#endif /* _BUFFER_H_ */

View File

@ -1,441 +0,0 @@
/*
* \brief Data structure for storing sparse files in RAM
* \author Norman Feske
* \date 2012-04-18
*/
/*
* Copyright (C) 2012-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _CHUNK_H_
#define _CHUNK_H_
/* Genode includes */
#include <util/noncopyable.h>
#include <base/allocator.h>
#include <util/string.h>
#include <file_system_session/file_system_session.h>
namespace File_system {
using namespace Genode;
/**
* Common base class of both 'Chunk' and 'Chunk_index'
*/
class Chunk_base : Noncopyable
{
public:
class Index_out_of_range { };
protected:
seek_off_t const _base_offset;
size_t _num_entries; /* corresponds to last used entry */
/**
* Test if specified range lies within the chunk
*/
void assert_valid_range(seek_off_t start, size_t len,
file_size_t chunk_size) const
{
if (zero()) return;
if (start < _base_offset)
throw Index_out_of_range();
if (start + len > _base_offset + chunk_size)
throw Index_out_of_range();
}
Chunk_base(seek_off_t base_offset)
: _base_offset(base_offset), _num_entries(0) { }
/**
* Construct zero chunk
*
* A zero chunk is a chunk that cannot be written to. When reading
* from it, it returns zeros. Because there is a single zero chunk
* for each chunk type, the base offset is meaningless. We use a
* base offset of ~0 as marker to identify zero chunks.
*/
Chunk_base() : _base_offset(~0L), _num_entries(0) { }
public:
/**
* Return absolute base offset of chunk in bytes
*/
seek_off_t base_offset() const { return _base_offset; }
/**
* Return true if chunk is a read-only zero chunk
*/
bool zero() const { return _base_offset == (seek_off_t)(~0L); }
/**
* Return true if chunk has no allocated sub chunks
*/
bool empty() const { return _num_entries == 0; }
};
/**
* Chunk of bytes used as leaf in hierarchy of chunk indices
*/
template <unsigned CHUNK_SIZE>
class Chunk : public Chunk_base
{
private:
char _data[CHUNK_SIZE];
public:
enum { SIZE = CHUNK_SIZE };
/**
* Construct byte chunk
*
* \param base_offset absolute offset of chunk in bytes
*
* The first argument is unused. Its mere purpose is to make the
* signature of the constructor compatible to the constructor
* of 'Chunk_index'.
*/
Chunk(Allocator &, seek_off_t base_offset)
:
Chunk_base(base_offset)
{
memset(_data, 0, CHUNK_SIZE);
}
/**
* Construct zero chunk
*/
Chunk() { }
/**
* Return number of used entries
*
* The returned value corresponds to the index of the last used
* entry + 1. It does not correlate to the number of actually
* allocated entries (there may be ranges of zero blocks).
*/
file_size_t used_size() const { return _num_entries; }
void write(char const *src, size_t len, seek_off_t seek_offset)
{
assert_valid_range(seek_offset, len, SIZE);
/* offset relative to this chunk */
seek_off_t const local_offset = seek_offset - base_offset();
memcpy(&_data[local_offset], src, len);
_num_entries = max(_num_entries, local_offset + len);
}
void read(char *dst, size_t len, seek_off_t seek_offset) const
{
assert_valid_range(seek_offset, len, SIZE);
memcpy(dst, &_data[seek_offset - base_offset()], len);
}
void truncate(file_size_t size)
{
assert_valid_range(size, 0, SIZE);
/*
* Offset of the first free position (relative to the beginning
* this chunk).
*/
seek_off_t const local_offset = size - base_offset();
if (local_offset >= _num_entries)
return;
memset(&_data[local_offset], 0, _num_entries - local_offset);
_num_entries = local_offset;
}
};
template <unsigned NUM_ENTRIES, typename ENTRY_TYPE>
class Chunk_index : public Chunk_base
{
public:
typedef ENTRY_TYPE Entry;
enum { ENTRY_SIZE = ENTRY_TYPE::SIZE,
SIZE = ENTRY_SIZE*NUM_ENTRIES };
private:
Allocator &_alloc;
Entry * _entries[NUM_ENTRIES];
/**
* Return instance of a zero sub chunk
*/
static Entry const &_zero_chunk()
{
static Entry zero_chunk;
return zero_chunk;
}
/**
* Return sub chunk at given index
*
* If there is no sub chunk at the specified index, this function
* transparently allocates one. Hence, the returned sub chunk
* is ready to be written to.
*/
Entry &_entry_for_writing(unsigned index)
{
if (index >= NUM_ENTRIES)
throw Index_out_of_range();
if (_entries[index])
return *_entries[index];
seek_off_t entry_offset = base_offset() + index*ENTRY_SIZE;
_entries[index] = new (&_alloc) Entry(_alloc, entry_offset);
_num_entries = max(_num_entries, index + 1);
return *_entries[index];
}
/**
* Return sub chunk at given index (for reading only)
*
* This function transparently provides a zero sub chunk for any
* index that is not populated by a real chunk.
*/
Entry const &_entry_for_reading(unsigned index) const
{
if (index >= NUM_ENTRIES)
throw Index_out_of_range();
if (_entries[index])
return *_entries[index];
return _zero_chunk();
}
/**
* Return index of entry located at specified byte offset
*
* The caller of this function must make sure that the offset
* parameter is within the bounds of the chunk.
*/
unsigned _index_by_offset(seek_off_t offset) const
{
return (offset - base_offset()) / ENTRY_SIZE;
}
/**
* Apply operation 'func' to a range of entries
*/
template <typename THIS, typename DATA, typename FUNC>
static void _range_op(THIS &obj, DATA *data, size_t len,
seek_off_t seek_offset, FUNC const &func)
{
/*
* Depending on whether this function is called for reading
* (const function) or writing (non-const function), the
* operand type is const or non-const Entry. The correct type
* is embedded as a trait in the 'FUNC' functor type.
*/
typedef typename FUNC::Entry Const_qualified_entry;
obj.assert_valid_range(seek_offset, len, SIZE);
while (len > 0) {
unsigned const index = obj._index_by_offset(seek_offset);
Const_qualified_entry &entry = FUNC::lookup(obj, index);
/*
* Calculate byte offset relative to the chunk
*
* We cannot use 'entry.base_offset()' for this calculation
* because in the const case, the lookup might return a
* zero chunk, which has no defined base offset. Therefore,
* we calculate the base offset via index*ENTRY_SIZE.
*/
seek_off_t const local_seek_offset =
seek_offset - obj.base_offset() - index*ENTRY_SIZE;
/* available capacity at 'entry' starting at seek offset */
seek_off_t const capacity = ENTRY_SIZE - local_seek_offset;
seek_off_t const curr_len = min(len, capacity);
/* apply functor (read or write) to entry */
func(entry, data, curr_len, seek_offset);
/* advance to next entry */
len -= curr_len;
data += curr_len;
seek_offset += curr_len;
}
}
struct Write_func
{
typedef ENTRY_TYPE Entry;
static Entry &lookup(Chunk_index &chunk, unsigned i) {
return chunk._entry_for_writing(i); }
void operator () (Entry &entry, char const *src, size_t len,
seek_off_t seek_offset) const
{
entry.write(src, len, seek_offset);
}
};
struct Read_func
{
typedef ENTRY_TYPE const Entry;
static Entry &lookup(Chunk_index const &chunk, unsigned i) {
return chunk._entry_for_reading(i); }
void operator () (Entry &entry, char *dst, size_t len,
seek_off_t seek_offset) const
{
if (entry.zero())
memset(dst, 0, len);
else
entry.read(dst, len, seek_offset);
}
};
void _init_entries()
{
for (unsigned i = 0; i < NUM_ENTRIES; i++)
_entries[i] = 0;
}
void _destroy_entry(unsigned i)
{
if (_entries[i] && (i < _num_entries)) {
destroy(&_alloc, _entries[i]);
_entries[i] = 0;
}
}
public:
/**
* Constructor
*
* \param alloc allocator to use for allocating sub-chunk
* indices and chunks
* \param base_offset absolute offset of the chunk in bytes
*/
Chunk_index(Allocator &alloc, seek_off_t base_offset)
: Chunk_base(base_offset), _alloc(alloc) { _init_entries(); }
/**
* Construct zero chunk
*/
Chunk_index() : _alloc(*(Allocator *)0) { }
/**
* Destructor
*/
~Chunk_index()
{
for (unsigned i = 0; i < NUM_ENTRIES; i++)
_destroy_entry(i);
}
/**
* Return size of chunk in bytes
*
* The returned value corresponds to the position after the highest
* offset that was written to.
*/
file_size_t used_size() const
{
if (_num_entries == 0)
return 0;
/* size of entries that lie completely within the used range */
file_size_t const size_whole_entries = ENTRY_SIZE*(_num_entries - 1);
Entry *last_entry = _entries[_num_entries - 1];
if (!last_entry)
return size_whole_entries;
return size_whole_entries + last_entry->used_size();
}
/**
* Write data to chunk
*/
void write(char const *src, size_t len, seek_off_t seek_offset)
{
_range_op(*this, src, len, seek_offset, Write_func());
}
/**
* Read data from chunk
*/
void read(char *dst, size_t len, seek_off_t seek_offset) const
{
_range_op(*this, dst, len, seek_offset, Read_func());
}
/**
* Truncate chunk to specified size in bytes
*
* This function can be used to shrink a chunk only. Specifying a
* 'size' larger than 'used_size' has no effect. The value returned
* by 'used_size' refers always to the position of the last byte
* written to the chunk.
*/
void truncate(file_size_t size)
{
unsigned const trunc_index = _index_by_offset(size);
if (trunc_index >= _num_entries)
return;
for (unsigned i = trunc_index + 1; i < _num_entries; i++)
_destroy_entry(i);
/* traverse into sub chunks */
if (_entries[trunc_index])
_entries[trunc_index]->truncate(size);
_num_entries = trunc_index + 1;
/*
* If the truncated at a chunk boundary, we can release the
* empty trailing chunk at 'trunc_index'.
*/
if (_entries[trunc_index] && _entries[trunc_index]->empty()) {
_destroy_entry(trunc_index);
_num_entries--;
}
}
};
};
#endif /* _CHUNK_H_ */

View File

@ -1,222 +0,0 @@
/*
* \brief File-system directory node
* \author Norman Feske
* \date 2012-04-11
*/
/*
* Copyright (C) 2012-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _DIRECTORY_H_
#define _DIRECTORY_H_
/* Genode includes */
#include <file_system/util.h>
/* local includes */
#include <node.h>
#include <file.h>
#include <symlink.h>
namespace Trace_fs {
class Directory;
}
class Trace_fs::Directory : public Node
{
private:
List<Node> _entries;
size_t _num_entries;
public:
Directory(char const *name) : _num_entries(0) { Node::name(name); }
/**
* Check if the directory has the specified subnode
*
* \param name name of the searched subnode
*
* \return true if the subnode was found, either false
*/
bool has_sub_node_unsynchronized(char const *name) const
{
Node const *sub_node = _entries.first();
for (; sub_node; sub_node = sub_node->next())
if (strcmp(sub_node->name(), name) == 0)
return true;
return false;
}
/**
* Add node to the list of subnodes
*
* \param pointer to node
*/
void adopt_unsynchronized(Node *node)
{
/*
* XXX inc ref counter
*/
_entries.insert(node);
_num_entries++;
mark_as_updated();
}
/**
* Remove the node from the list of subnodes
*
* \param node pointer to node
*/
void discard_unsynchronized(Node *node)
{
_entries.remove(node);
_num_entries--;
mark_as_updated();
}
/**
* Lookup node which belongs to the specified path
*
* \param path path to lookup
* \param return_parent if true return parent node, otherwise
* actual path node
*
* \return node node founc
* \throws Lookup_failed
*/
Node *lookup(char const *path, bool return_parent = false) override
{
if (strcmp(path, "") == 0) {
return this;
}
if (!path || path[0] == '/')
throw Lookup_failed();
/* find first path delimiter */
unsigned i = 0;
for (; path[i] && path[i] != '/'; i++);
/*
* If no path delimiter was found, we are the parent of the
* specified path.
*/
if (path[i] == 0 && return_parent) {
return this;
}
/*
* The offset 'i' corresponds to the end of the first path
* element, which can be either the end of the string or the
* first '/' character.
*/
/* try to find entry that matches the first path element */
Node *sub_node = _entries.first();
for (; sub_node; sub_node = sub_node->next())
if ((strlen(sub_node->name()) == i) &&
(strcmp(sub_node->name(), path, i) == 0))
break;
if (!sub_node)
throw Lookup_failed();
if (!contains_path_delimiter(path)) {
/*
* Because 'path' is a basename that corresponds to an
* existing sub_node, we have found what we were looking
* for.
*/
return sub_node;
}
/*
* As 'path' contains one or more path delimiters, traverse
* into the sub directory names after the first path element.
*/
/*
* We cannot traverse into anything other than a directory.
*
* XXX we might follow symlinks here
*/
Directory *sub_dir = dynamic_cast<Directory *>(sub_node);
if (!sub_dir)
throw Lookup_failed();
return sub_dir->lookup(path + i + 1, return_parent);
}
/**
* Return number of subnodes
*/
size_t num_entries() const { return _num_entries; }
/********************
** Node interface **
********************/
size_t read(char *dst, size_t len, seek_off_t seek_offset)
{
if (len < sizeof(Directory_entry)) {
Genode::error("read buffer too small for directory entry");
return 0;
}
seek_off_t index = seek_offset / sizeof(Directory_entry);
if (seek_offset % sizeof(Directory_entry)) {
Genode::error("seek offset not alighed to sizeof(Directory_entry)");
return 0;
}
/* find list element */
Node *node = _entries.first();
for (unsigned i = 0; i < index && node; node = node->next(), i++);
/* index out of range */
if (!node)
return 0;
Directory_entry *e = (Directory_entry *)(dst);
if (dynamic_cast<File *>(node)) e->type = Directory_entry::TYPE_FILE;
if (dynamic_cast<Directory *>(node)) e->type = Directory_entry::TYPE_DIRECTORY;
if (dynamic_cast<Symlink *>(node)) e->type = Directory_entry::TYPE_SYMLINK;
strncpy(e->name, node->name(), sizeof(e->name));
return sizeof(Directory_entry);
}
size_t write(char const *src, size_t len, seek_off_t seek_offset)
{
/* writing to directory nodes is not supported */
return 0;
}
Status status() const
{
Status s;
s.inode = inode();
s.size = _num_entries * sizeof (Directory_entry);
s.mode = File_system::Status::MODE_DIRECTORY;
return s;
}
};
#endif /* _DIRECTORY_H_ */

View File

@ -1,220 +0,0 @@
/*
* \brief File node
* \author Norman Feske
* \author Josef Soentgen
* \date 2012-04-11
*/
/*
* Copyright (C) 2012-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _FILE_H_
#define _FILE_H_
/* Genode includes */
#include <base/allocator.h>
#include <base/trace/types.h>
/* local includes */
#include <node.h>
#include <chunk.h>
namespace Trace_fs {
class Changeable_content;
class File;
class Buffered_file;
}
/**
*
*
*/
class Trace_fs::Changeable_content
{
protected:
/**
* This member is used to communicate the state and
* must be set true by classes using this class in case
* the content has changed.
*/
bool _changed;
/**
* This method is called when the content change is
* acknowledged. It may be overriden by any class using
* this particular class.
*/
virtual void _refresh_content() { }
public:
Changeable_content() : _changed(false) { }
/**
* Check if the content was changed
*
* This evaluation has to be made by classes using this
* particular class.
*
* \return true if changed, otherwise false
*/
bool changed() const { return _changed; }
/**
* Acknowledge the content has changed
*/
void acknowledge_change()
{
_changed = false;
_refresh_content();
}
};
/**
* File interface
*/
class Trace_fs::File : public Node
{
public:
File(char const *name)
{
Node::name(name);
}
virtual ~File() { }
/********************
** Node interface **
********************/
virtual size_t read(char *dst, size_t len, seek_off_t seek_offset) = 0;
virtual size_t write(char const *src, size_t len, seek_off_t seek_offset) = 0;
virtual Status status() const = 0;
/********************
** File interface **
********************/
virtual file_size_t length() const = 0;
virtual void truncate(file_size_t size) = 0;
};
/**
* Memory buffered file
*
* This file merely exists in memory and grows automatically.
*/
class Trace_fs::Buffered_file : public File
{
private:
typedef Chunk<4096> Chunk_level_3;
typedef Chunk_index<128, Chunk_level_3> Chunk_level_2;
typedef Chunk_index<64, Chunk_level_2> Chunk_level_1;
typedef Chunk_index<64, Chunk_level_1> Chunk_level_0;
Chunk_level_0 _chunk;
file_size_t _length;
public:
Buffered_file(Allocator &alloc, char const *name)
: File(name), _chunk(alloc, 0), _length(0) { }
virtual size_t read(char *dst, size_t len, seek_off_t seek_offset)
{
file_size_t const chunk_used_size = _chunk.used_size();
if (seek_offset >= _length)
return 0;
/*
* Constrain read transaction to available chunk data
*
* Note that 'chunk_used_size' may be lower than '_length'
* because 'Chunk' may have truncated tailing zeros.
*/
if (seek_offset + len >= _length)
len = _length - seek_offset;
file_size_t read_len = len;
if (seek_offset + read_len > chunk_used_size) {
if (chunk_used_size >= seek_offset)
read_len = chunk_used_size - seek_offset;
else
read_len = 0;
}
_chunk.read(dst, read_len, seek_offset);
/* add zero padding if needed */
if (read_len < len)
memset(dst + read_len, 0, len - read_len);
return len;
}
virtual size_t write(char const *src, size_t len, seek_off_t seek_offset)
{
if (seek_offset == (seek_off_t)(~0))
seek_offset = _chunk.used_size();
if (seek_offset + len >= Chunk_level_0::SIZE) {
len = (Chunk_level_0::SIZE-1) - seek_offset;
Genode::error(name(), ": size limit ", (long)Chunk_level_0::SIZE, " reached");
}
_chunk.write(src, len, (size_t)seek_offset);
/*
* Keep track of file length. We cannot use 'chunk.used_size()'
* as file length because trailing zeros may by represented
* by zero chunks, which do not contribute to 'used_size()'.
*/
_length = max(_length, seek_offset + len);
mark_as_updated();
return len;
}
virtual Status status() const
{
Status s;
s.inode = inode();
s.size = _length;
s.mode = File_system::Status::MODE_FILE;
return s;
}
virtual file_size_t length() const { return _length; }
void truncate(file_size_t size) override
{
if (size < _chunk.used_size())
_chunk.truncate(size);
_length = size;
mark_as_updated();
}
};
#endif /* _FILE_H_ */

View File

@ -1,283 +0,0 @@
/*
* \brief Facility for managing the trace subjects
* \author Josef Soentgen
* \date 2014-01-22
*/
/*
* Copyright (C) 2014-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _SUBJECT_REGISTRY_H_
#define _SUBJECT_REGISTRY_H_
#include <base/allocator.h>
#include <base/lock.h>
#include <base/trace/types.h>
#include <directory.h>
#include <trace_files.h>
namespace Trace_fs {
typedef Genode::size_t size_t;
class Followed_subject;
class Followed_subject_registry;
}
class Trace_fs::Followed_subject : public Directory
{
public:
/**
* This class manages the access to the trace subject's trace buffer
*/
class Trace_buffer_manager
{
public:
class Already_managed { };
class Not_managed { };
struct Process_entry
{
virtual size_t operator()(Genode::Trace::Buffer::Entry&) = 0;
};
private:
Genode::Trace::Buffer *buffer;
Genode::Trace::Buffer::Entry current_entry;
public:
Trace_buffer_manager(Genode::Region_map &rm,
Genode::Dataspace_capability ds_cap)
:
buffer(rm.attach(ds_cap)),
current_entry(buffer->first())
{ }
size_t dump_entry(Process_entry &process)
{
size_t len = process(current_entry);
current_entry = buffer->next(current_entry);
return len;
}
bool last_entry() const
{
return current_entry.last();
}
void rewind() { current_entry = buffer->first(); }
};
private:
Genode::Allocator &_md_alloc;
Genode::Region_map &_rm;
int _handle;
Genode::Trace::Subject_id _id;
Genode::Trace::Policy_id _policy_id;
bool _was_traced;
Trace_buffer_manager *_buffer_manager;
public:
Active_file active_file;
Buffer_size_file buffer_size_file;
Cleanup_file cleanup_file;
Enable_file enable_file;
Events_file events_file;
Policy_file policy_file;
Followed_subject(Genode::Allocator &md_alloc, char const *name,
Genode::Region_map &rm,
Genode::Trace::Subject_id &id, int handle)
:
Directory(name),
_md_alloc(md_alloc),
_rm(rm),
_handle(handle),
_id(id),
_was_traced(false),
_buffer_manager(0),
active_file(_id),
buffer_size_file(),
cleanup_file(_id),
enable_file(_id),
events_file(_id, _md_alloc),
policy_file(_id, _md_alloc)
{
adopt_unsynchronized(&active_file);
adopt_unsynchronized(&cleanup_file);
adopt_unsynchronized(&enable_file);
adopt_unsynchronized(&events_file);
adopt_unsynchronized(&buffer_size_file);
adopt_unsynchronized(&policy_file);
}
~Followed_subject()
{
discard_unsynchronized(&active_file);
discard_unsynchronized(&cleanup_file);
discard_unsynchronized(&enable_file);
discard_unsynchronized(&events_file);
discard_unsynchronized(&buffer_size_file);
discard_unsynchronized(&policy_file);
}
bool marked_for_cleanup() const { return cleanup_file.cleanup(); }
bool was_traced() const { return _was_traced; }
Trace_buffer_manager* trace_buffer_manager() { return _buffer_manager; }
void manage_trace_buffer(Genode::Dataspace_capability ds_cap)
{
if (_buffer_manager != 0)
throw Trace_buffer_manager::Already_managed();
_buffer_manager = new (&_md_alloc) Trace_buffer_manager(_rm, ds_cap);
}
void unmanage_trace_buffer()
{
if (_buffer_manager == 0)
throw Trace_buffer_manager::Not_managed();
destroy(&_md_alloc, _buffer_manager);
_buffer_manager = 0;
}
const Genode::Trace::Subject_id id() const { return _id; }
const Genode::Trace::Policy_id policy_id() const { return _policy_id; }
void policy_id(Genode::Trace::Policy_id &id) { _policy_id.id = id.id; }
bool policy_valid() const { return (_policy_id.id != 0); }
void invalidate_policy() { _policy_id = Genode::Trace::Policy_id(); }
int handle() const { return _handle; }
};
/**
* This registry contains all current followed trace subjects
*/
class Trace_fs::Followed_subject_registry
{
public:
class Invalid_subject { };
class Out_of_subject_handles { };
private:
/* XXX abitrary limit - needs to be revisited when highly
* dynamic scenarios are executed */
enum { MAX_SUBJECTS = 1024U };
Followed_subject *_subjects[MAX_SUBJECTS];
Genode::Allocator &_md_alloc;
/**
* Find free subject handle
*
* \throw Out_of_subject_handles
*/
int _find_free_handle()
{
for (unsigned i = 0; i < MAX_SUBJECTS; i++)
if (!_subjects[i]) {
return i;
}
throw Out_of_subject_handles();
}
bool _in_range(int handle) const
{
return ((handle >= 0) && (handle < MAX_SUBJECTS));
}
public:
Followed_subject_registry(Genode::Allocator &md_alloc)
:
_md_alloc(md_alloc)
{
for (unsigned i = 0; i < MAX_SUBJECTS; i++)
_subjects[i] = 0;
}
/**
* Return maximal number of subject that can be stored in the registry
*
* \return maximal number of subjects
*/
unsigned max_subjects() const { return MAX_SUBJECTS; }
/**
* Allocate new subject
*
* \param name name of subject
* \param id subject id of tracre subject
*/
Followed_subject *alloc(char const *name, Genode::Trace::Subject_id &id,
Genode::Region_map &rm)
{
int handle = _find_free_handle();
_subjects[handle] = new (&_md_alloc) Followed_subject(_md_alloc, name, rm, id, handle);
return _subjects[handle];
}
/**
* Free subject
*
* \param subject pointer to subject
*/
void free(Followed_subject *subject)
{
int handle = subject->handle();
if (!_in_range(handle))
return;
if(!_subjects[handle])
return;
_subjects[handle] = 0;
destroy(&_md_alloc, subject);
}
/**
* Lookup subject by using the id
*
* \throw Invalid_subject();
*/
Followed_subject *lookup(Genode::Trace::Subject_id const &sid)
{
for (unsigned i = 0; i < MAX_SUBJECTS; i++)
if (_subjects[i]) {
if (_subjects[i]->id().id == sid.id)
return _subjects[i];
}
throw Invalid_subject();
}
};
#endif /* _SUBJECT_REGISTRY_H_ */

File diff suppressed because it is too large Load Diff

View File

@ -1,89 +0,0 @@
/*
* \brief File-system node
* \author Norman Feske
* \date 2012-04-11
*/
/*
* Copyright (C) 2012-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _NODE_H_
#define _NODE_H_
/* Genode includes */
#include <file_system/node.h>
#include <util/list.h>
#include <base/lock.h>
#include <base/signal.h>
namespace Trace_fs {
using namespace File_system;
using namespace Genode;
class Node;
}
class Trace_fs::Node : public Node_base, public Weak_object<Node>,
public List<Node>::Element
{
public:
typedef char Name[128];
private:
Name _name;
unsigned long const _inode;
/**
* Generate unique inode number
*/
static unsigned long _unique_inode()
{
static unsigned long inode_count;
return ++inode_count;
}
public:
Node()
: _inode(_unique_inode())
{ _name[0] = 0; }
virtual ~Node() { lock_for_destruction(); }
unsigned long inode() const { return _inode; }
char const *name() const { return _name; }
/**
* Assign name
*/
void name(char const *name) { strncpy(_name, name, sizeof(_name)); }
virtual Status status() const = 0;
virtual size_t read(char *dst, size_t len, seek_off_t) = 0;
virtual size_t write(char const *src, size_t len, seek_off_t) = 0;
/*
* Directory functionality
*/
virtual Node *lookup(char const *path, bool return_parent = false)
{
Genode::error(__PRETTY_FUNCTION__, " called on a non-directory node");
return nullptr;
}
/*
* File functionality
*/
virtual void truncate(file_size_t size)
{
Genode::error(__PRETTY_FUNCTION__, " called on a non-file node");
}
};
#endif /* _NODE_H_ */

View File

@ -1,41 +0,0 @@
/*
* \brief Symlink file-system node
* \author Norman Feske
* \date 2012-04-11
*/
/*
* Copyright (C) 2012-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _SYMLINK_H_
#define _SYMLINK_H_
/* local includes */
#include <file.h>
namespace Trace_fs {
class Symlink;
}
class Trace_fs::Symlink : public File
{
public:
Symlink(char const *name) : File(name) { }
size_t read(char *dst, size_t len, seek_off_t seek_offset) {
return 0; }
size_t write(char const *src, size_t len, seek_off_t seek_offset) {
return 0; }
file_size_t length() const { return 0; }
void truncate(file_size_t) { }
};
#endif /* _SYMLINK_H_ */

View File

@ -1,6 +0,0 @@
TARGET = trace_fs
SRC_CC = main.cc
LIBS = base
INC_DIR += $(PRG_DIR)
CC_CXX_WARN_STRICT =

View File

@ -1,438 +0,0 @@
/*
* \brief Trace files
* \author Josef Soentgen
* \date 2014-01-22
*/
/*
* Copyright (C) 2014-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _TRACE_FILES_H_
#define _TRACE_FILES_H_
/* Genode includes */
#include <util/string.h>
/* local includes */
#include <file.h>
namespace Trace_fs {
class State_file;
class Active_file;
class Cleanup_file;
class Enable_file;
class Events_file;
class Buffer_size_file;
class Policy_file;
}
/**
* The State_file is a stateful file that is used to implement
* files in the file system, which may trigger a action in the
* file system backend.
*/
class Trace_fs::State_file : public File,
public Changeable_content
{
protected:
bool _state;
public:
State_file(char const *name)
: File(name), _state(false) { }
bool state() const { return _state; }
/********************
** Node interface **
********************/
virtual size_t read(char *dst, size_t len, seek_off_t seek_offset)
{
/* limit len */
if (len > 2)
len = 2;
switch (len) {
case 2:
dst[1] = '\n';
case 1:
dst[0] = 0x30 + (char)_state;
break;
default:
/* zero length is useless */
break;
}
return len;
}
virtual size_t write(char const *src, size_t len, seek_off_t seek_offset)
{
char buf[32];
if (len >= sizeof buf)
return 0;
using namespace Genode;
strncpy(buf, src, min(len + 1, sizeof (buf)));
/**
* For now, we only check the leading digit and do not care
* about the rest.
*/
if (!strcmp(buf, "1", 1)) {
_state = true;
}
else if (!strcmp(buf, "0", 1)) {
_state = false;
} else {
/* silently ignore bogus writes */
return 0;
}
Changeable_content::_changed = true;
return len;
}
Status status() const
{
Status s;
s.inode = inode();
s.size = 2;
s.mode = File_system::Status::MODE_FILE;
return s;
}
/********************
** File interface **
********************/
file_size_t length() const { return 2; }
void truncate(file_size_t size) { }
};
/**
* The Active_file node shows the state of the tracing
*/
class Trace_fs::Active_file : public State_file
{
private:
Genode::Trace::Subject_id &_id;
bool _active;
public:
Active_file(Genode::Trace::Subject_id &id)
: State_file("active"), _id(id) { }
Genode::Trace::Subject_id& id() const { return _id; }
bool active() const { return State_file::state(); }
void set_active() { _state = true; }
void set_inactive() { _state = false; }
};
/**
* The Cleanup_file is used to trigger the removal of files used by
* the traced subject and to free utilized memory.
*/
class Trace_fs::Cleanup_file : public State_file
{
private:
Genode::Trace::Subject_id &_id;
public:
Cleanup_file(Genode::Trace::Subject_id &id)
: State_file("cleanup"), _id(id) { }
Genode::Trace::Subject_id& id() const { return _id; }
bool cleanup() const { return State_file::state(); }
};
/**
* The Enable_file is used to initiate the tracing process
*/
class Trace_fs::Enable_file : public State_file
{
private:
Genode::Trace::Subject_id &_id;
public:
Enable_file(Genode::Trace::Subject_id &id)
: State_file("enable"), _id(id) { }
Genode::Trace::Subject_id& id() const { return _id; }
bool enabled() const { return State_file::state(); }
};
/**
* The Events_file encapsulates the trace buffer of traced thread
*/
class Trace_fs::Events_file : public Buffered_file
{
private:
Genode::Trace::Subject_id &_id;
public:
Events_file(Genode::Trace::Subject_id &id,
Allocator &md_alloc)
: Buffered_file(md_alloc, "events"), _id(id) { }
Genode::Trace::Subject_id id() const { return _id; }
size_t append(char const *src, size_t len)
{
Buffered_file::write(src, len, length());
return len;
}
/********************
** File interface **
********************/
/* override to prevent the user from overriding the file */
size_t write(char const *src, size_t len, seek_off_t seek_offset) { return 0; }
void truncate(file_size_t size) { }
};
/**
* This file contains the size of the trace buffer
*/
class Trace_fs::Buffer_size_file : public File,
public Changeable_content
{
private:
file_size_t _length;
unsigned long _size_limit;
unsigned long _size;
char _content[32];
Genode::size_t _content_filled;
/**
* Check if new size honors the size limit
*
* \param size new size of the buffer
* \return size limit if new size is greater, otherwise new size
*/
size_t _check_size_limit(size_t size)
{
if (size > _size_limit)
return _size_limit;
else
return size;
}
/**
* Evalute the current content of the buffer
*/
void _refresh_content()
{
unsigned long tmp = 0;
_content[_content_filled - 1] = '\0';
_content_filled = 0;
_length = Genode::strlen(_content);
/* account for \n when reading from the file */
_length += 1;
ascii_to(_content, tmp);
_size = _check_size_limit(tmp);
}
public:
/**
* Constructor
*/
Buffer_size_file() : File("buffer_size"), _size_limit(0), _size(0) { }
/**
* Return current size of the trace buffer
*/
unsigned long size() const { return _size; }
/**
* Set current size of the trace buffer
*/
void size(unsigned long size)
{
_size = _check_size_limit(size);
/* update file content */
_length = Genode::snprintf(_content, sizeof (_content), "%lu", _size);
/* account for \n when reading from the file */
_length += 1;
}
/**
* Set max size of a trace buffer
*/
void size_limit(unsigned long limit) { _size_limit = limit; }
/**
* Return maximal size of the trace buffer
*/
unsigned long size_limit() const { return _size_limit; }
/********************
** Node interface **
********************/
/**
* Read current maximal size of the trace buffer
*/
size_t read(char *dst, size_t len, seek_off_t seek_offset)
{
if (len > 32) {
Genode::error("len:'", len, "' to small");
return 0;
}
char buf[32];
Genode::snprintf(buf, sizeof (buf), "%lu\n", _size);
memcpy(dst, buf, len);
return len;
}
/**
* Write new current maximal size of the trace buffer
*/
size_t write(char const *src, size_t len, seek_off_t seek_offset)
{
if ((_content_filled + len) > sizeof (_content))
return 0;
Genode::memcpy(_content + _content_filled, src, len);
_content_filled += len;
Changeable_content::_changed = true;
return len;
}
Status status() const
{
Status s;
s.inode = inode();
s.size = _length;
s.mode = File_system::Status::MODE_FILE;
return s;
}
/********************
** File interface **
********************/
file_size_t length() const { return _length; }
void truncate(file_size_t size) { }
};
/**
* Policy file
*/
class Trace_fs::Policy_file : public Buffered_file,
public Changeable_content
{
private:
Genode::Trace::Subject_id &_id;
file_size_t _length;
public:
Policy_file(Genode::Trace::Subject_id &id,
Genode::Allocator &md_alloc)
: Buffered_file(md_alloc, "policy"), _id(id) { }
Genode::Trace::Subject_id& id() const { return _id; }
/********************
** Node interface **
********************/
size_t read(char *dst, size_t len, seek_off_t seek_offset)
{
return Buffered_file::read(dst, len, seek_offset);
}
size_t write(char const *src, size_t len, seek_off_t seek_offset)
{
size_t written = Buffered_file::write(src, len, seek_offset);
if (written > 0)
_changed = true;
return written;
}
/********************
** File interface **
********************/
void truncate(file_size_t size)
{
Buffered_file::truncate(size);
_changed = true;
}
};
#endif /* _TRACE_FILES_H_ */

View File

@ -1,160 +0,0 @@
#
# The Linux version of Noux lacks the support for the fork system call. Hence,
# the run script is expected to fail.
#
if {[have_spec linux]} {
puts "Linux is unsupported."
exit 0
}
create_boot_directory
import_from_depot [depot_user]/src/[base_src] \
[depot_user]/pkg/[drivers_interactive_pkg] \
[depot_user]/pkg/terminal \
[depot_user]/src/init \
[depot_user]/src/libc \
[depot_user]/src/noux \
[depot_user]/src/posix \
[depot_user]/src/ncurses \
[depot_user]/src/vim \
[depot_user]/src/bash \
[depot_user]/src/grep \
[depot_user]/src/coreutils \
[depot_user]/src/ram_fs
# write default vimrc file
set vimrc_fd [open "bin/vimrc" w]
puts $vimrc_fd {
set noloadplugins
set hls
set nocompatible
set laststatus=2
set noswapfile
set viminfo=}
close $vimrc_fd
install_config {
<config verbose="yes">
<parent-provides>
<service name="ROM"/>
<service name="LOG"/>
<service name="RM"/>
<service name="CPU"/>
<service name="PD"/>
<service name="IRQ"/>
<service name="IO_PORT"/>
<service name="IO_MEM"/>
<service name="TRACE"/>
</parent-provides>
<default-route>
<any-service> <parent/> <any-child/> </any-service>
</default-route>
<default caps="100"/>
<start name="timer">
<resource name="RAM" quantum="1M"/>
<provides><service name="Timer"/></provides>
</start>
<start name="drivers" caps="1000">
<resource name="RAM" quantum="32M" constrain_phys="yes"/>
<binary name="init"/>
<route>
<service name="ROM" label="config"> <parent label="drivers.config"/> </service>
<service name="Timer"> <child name="timer"/> </service>
<any-service> <parent/> </any-service>
</route>
<provides>
<service name="Input"/> <service name="Framebuffer"/>
</provides>
</start>
<start name="terminal">
<resource name="RAM" quantum="2M"/>
<provides><service name="Terminal"/></provides>
<route>
<service name="ROM" label="config"> <parent label="terminal.config"/> </service>
<any-service> <parent/> <any-child/> </any-service>
</route>
</start>
<start name="trace_fs" caps="200">
<resource name="RAM" quantum="128M"/>
<provides><service name="File_system"/></provides>
<config>
<policy label="noux -> trace" root="/" interval="1000"
subject_limit="512" trace_quota="64M" parent_levels="0" />
</config>
</start>
<start name="ram_fs">
<resource name="RAM" quantum="32M"/>
<provides><service name="File_system"/></provides>
<config>
<content>
<dir name="home">
<inline name=".bash_profile">
</inline>
</dir>
<dir name="tmp">
<dir name="policies">
<rom name="rpc_name" />
</dir>
</dir>
</content>
<!-- constrain sessions according to their labels -->
<policy label="noux -> root" root="/" />
<policy label="noux -> home" root="/home" writeable="yes" />
<policy label="noux -> tmp" root="/tmp" writeable="yes" />
</config>
</start>
<start name="noux" caps="2000">
<resource name="RAM" quantum="256M" />
<config>
<fstab>
<tar name="coreutils.tar" />
<tar name="vim.tar" />
<tar name="bash.tar" />
<tar name="grep.tar" />
<dir name="home"> <fs label="home" /> </dir>
<dir name="ram"> <fs label="root" /> </dir>
<dir name="tmp"> <fs label="tmp" /> </dir>
<dir name="trace"> <fs label="trace" /> </dir>
<dir name="dev"> <null /> <zero /> </dir>
<dir name="share"> <dir name="vim"> <rom name="vimrc"/> </dir> </dir>
</fstab>
<start name="/bin/bash">
<env name="TERM" value="screen" />
<env name="HOME" value="/home" />
<!-- <arg value="-login" /> -->
</start>
</config>
<route>
<service name="File_system" label="home">
<child name="ram_fs" /> </service>
<service name="File_system" label="root">
<child name="ram_fs" /> </service>
<service name="File_system" label="tmp">
<child name="ram_fs" /> </service>
<service name="File_system" label="trace">
<child name="trace_fs"/> </service>
<any-service> <parent /> <any-child /> </any-service>
</route>
</start>
</config>}
build { server/trace_fs lib/trace/policy/rpc_name }
build_boot_image { vimrc trace_fs rpc_name }
run_genode_until forever
# vi: set ft=tcl :