mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-19 13:47:56 +00:00
os: remove trace_fs
This component is superseded by the 'vfs_trace' plugin. Issue #3294.
This commit is contained in:
parent
57e650ef2c
commit
d32e8390c2
@ -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'.
|
@ -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_ */
|
@ -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_ */
|
@ -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_ */
|
@ -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_ */
|
@ -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
@ -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_ */
|
@ -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_ */
|
@ -1,6 +0,0 @@
|
||||
TARGET = trace_fs
|
||||
SRC_CC = main.cc
|
||||
LIBS = base
|
||||
INC_DIR += $(PRG_DIR)
|
||||
|
||||
CC_CXX_WARN_STRICT =
|
@ -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_ */
|
@ -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 :
|
Loading…
Reference in New Issue
Block a user