genode/repos/os/src/server/trace_fs/directory.h

217 lines
4.8 KiB
C
Raw Normal View History

os: initial version of trace_fs The trace_fs server provides access to a Trace_session by using a File_system_session as frontend. Each trace subject is represented by a directory ('thread_name.subject') that contains specific files ('active', 'cleanup', 'enable', 'events', 'buffer_size' and 'policy'), which are used to control the tracing process of the thread as well as storing the content of its trace buffer. The tracing of a thread is only 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 deactived by writing a '0' to the 'enable' file. A policy may be changed by overwriting the currently used one. In this case the old policy is replaced by the new policy and is automatically utilize. Writing a value to the 'buffer_size' file changes the appointed size of the trace buffer. This value is only evaluted by reactivating the tracing process. The content of the trace buffer may be accessed by reading from the 'events' file. Throughout all tracing session new trace events are appended to this file. Nodes of UNTRACED subjects are kept as long as they do not change their tracing state to DEAD. In this case all nodes are removed from the file system. Subjects that were traced before and are now UNTRACED will only be removed by writing '1' to the 'cleanup' file - even if they are DEAD by now. To use the trace_fs a config 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="noux -> trace" interval="1000" subject_limit="512" trace_quota="64M" /> ! </config> ! </start> 'interval' sets the periode in which the Trace_session is polled. The time is given in milliseconds. 'subject_limit' speficies how many trace subject should by acquired at most when the Trace_session is polled. 'trace_quota' is the amount of quota the trace_fs should use for the Trace_session connection. The remaing 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 addiition there are 'buffer_size' and 'buffer_size_limit' that define the initial and the upper limit of the size of a trace buffer. Tracing of parent processes or rather threads may be enabled by setting 'parent_levels' to a value greater than '0' (though this attribute is available, the trace session component within core still lacks support for it). A ready-to-use runscript can by found in 'ports/run/noux_trace_fs.run'. Fixes #1049.
2014-01-17 16:04:04 +01:00
/*
* \brief File-system directory node
* \author Norman Feske
* \date 2012-04-11
*/
#ifndef _DIRECTORY_H_
#define _DIRECTORY_H_
/* Genode includes */
#include <file_system/util.h>
os: initial version of trace_fs The trace_fs server provides access to a Trace_session by using a File_system_session as frontend. Each trace subject is represented by a directory ('thread_name.subject') that contains specific files ('active', 'cleanup', 'enable', 'events', 'buffer_size' and 'policy'), which are used to control the tracing process of the thread as well as storing the content of its trace buffer. The tracing of a thread is only 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 deactived by writing a '0' to the 'enable' file. A policy may be changed by overwriting the currently used one. In this case the old policy is replaced by the new policy and is automatically utilize. Writing a value to the 'buffer_size' file changes the appointed size of the trace buffer. This value is only evaluted by reactivating the tracing process. The content of the trace buffer may be accessed by reading from the 'events' file. Throughout all tracing session new trace events are appended to this file. Nodes of UNTRACED subjects are kept as long as they do not change their tracing state to DEAD. In this case all nodes are removed from the file system. Subjects that were traced before and are now UNTRACED will only be removed by writing '1' to the 'cleanup' file - even if they are DEAD by now. To use the trace_fs a config 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="noux -> trace" interval="1000" subject_limit="512" trace_quota="64M" /> ! </config> ! </start> 'interval' sets the periode in which the Trace_session is polled. The time is given in milliseconds. 'subject_limit' speficies how many trace subject should by acquired at most when the Trace_session is polled. 'trace_quota' is the amount of quota the trace_fs should use for the Trace_session connection. The remaing 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 addiition there are 'buffer_size' and 'buffer_size_limit' that define the initial and the upper limit of the size of a trace buffer. Tracing of parent processes or rather threads may be enabled by setting 'parent_levels' to a value greater than '0' (though this attribute is available, the trace session component within core still lacks support for it). A ready-to-use runscript can by found in 'ports/run/noux_trace_fs.run'. Fixes #1049.
2014-01-17 16:04:04 +01:00
/* local includes */
#include <node.h>
#include <file.h>
#include <symlink.h>
namespace File_system {
class 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)
{
/* PWRN("discard node '%s' from '%s'", node->name(), Node::name()); */
_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)
{
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 (is_basename(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)) {
PERR("read buffer too small for directory entry");
return 0;
}
seek_off_t index = seek_offset / sizeof(Directory_entry);
if (seek_offset % sizeof(Directory_entry)) {
PERR("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_ */