/* * \brief Registry containing possible tracing subjects * \author Norman Feske * \date 2013-08-09 * * Tracing subjects represent living or previously living tracing sources * that can have trace buffers attached. Each 'Trace::Subject' belongs to * a TRACE session and may point to a 'Trace::Source' (which is owned by * a CPU session). */ /* * Copyright (C) 2013 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU General Public License version 2. */ #ifndef _CORE__INCLUDE__TRACE__SUBJECT_REGISTRY_H_ #define _CORE__INCLUDE__TRACE__SUBJECT_REGISTRY_H_ /* Genode includes */ #include #include #include #include #include #include /* core includes */ #include #include /* base-internal include */ #include namespace Genode { namespace Trace { class Subject; class Subject_registry; } } /** * Subject of tracing data */ class Genode::Trace::Subject : public Genode::List::Element, public Genode::Trace::Source_owner { private: class Ram_dataspace { private: Ram_session *_ram; size_t _size; Ram_dataspace_capability _ds; void _reset() { _ram = 0; _size = 0; _ds = Ram_dataspace_capability(); } public: Ram_dataspace() { _reset(); } ~Ram_dataspace() { flush(); } /** * Allocate new dataspace * * \return true on success, false on the attempt to call setup * twice. */ bool setup(Ram_session &ram, size_t size) { if (_size) return false; _ram = &ram; _size = size; _ds = ram.alloc(size); return true; } /** * Clone dataspace into newly allocated dataspace */ bool setup(Ram_session &ram, Dataspace_capability &from_ds, size_t size) { if (!from_ds.valid()) return false; _ram = &ram; _size = size; _ds = ram.alloc(_size); /* copy content */ void *src = env()->rm_session()->attach(from_ds), *dst = env()->rm_session()->attach(_ds); memcpy(dst, src, _size); env()->rm_session()->detach(src); env()->rm_session()->detach(dst); return true; } /** * Release dataspace */ size_t flush() { if (_ram) _ram->free(_ds); _reset(); return 0; } Dataspace_capability dataspace() const { return _ds; } }; friend class Subject_registry; Subject_id const _id; unsigned const _source_id; Weak_ptr _source; Session_label const _label; Thread_name const _name; Ram_dataspace _buffer; Ram_dataspace _policy; Policy_id _policy_id; Subject_info::State _state() { Locked_ptr source(_source); /* source vanished */ if (!source.is_valid()) return Subject_info::DEAD; if (source->enabled()) return source->is_owned_by(this) ? Subject_info::TRACED : Subject_info::FOREIGN; if (source->error()) return Subject_info::ERROR; return Subject_info::UNTRACED; } public: /** * Constructor, called from 'Subject_registry' only */ Subject(Subject_id id, unsigned source_id, Weak_ptr &source, Session_label const &label, Thread_name const &name) : _id(id), _source_id(source_id), _source(source), _label(label), _name(name) { } /** * Return registry-local ID */ Subject_id id() const { return _id; } /** * Test if subject belongs to the specified unique source ID */ bool has_source_id(unsigned id) const { return id == _source_id; } /** * Start tracing * * \param size trace buffer size * * \throw Out_of_metadata * \throw Already_traced * \throw Source_is_dead * \throw Traced_by_other_session */ void trace(Policy_id policy_id, Dataspace_capability policy_ds, size_t policy_size, Ram_session &ram, size_t size) { _policy_id = policy_id; if (!_buffer.setup(ram, size) || !_policy.setup(ram, policy_ds, policy_size)) throw Already_traced(); /* inform trace source about the new buffer */ Locked_ptr source(_source); if (!source.is_valid()) throw Source_is_dead(); if (!source->try_acquire(this)) throw Traced_by_other_session(); source->trace(_policy.dataspace(), _buffer.dataspace()); } void pause() { /* inform trace source about the new buffer */ Locked_ptr source(_source); if (!source.is_valid()) source->disable(); } /** * Resume tracing of paused source * * \throw Source_is_dead */ void resume() { /* inform trace source about the new buffer */ Locked_ptr source(_source); if (!source.is_valid()) throw Source_is_dead(); source->enable(); } Subject_info info() { return Subject_info(_label, _name, _state(), _policy_id); } Dataspace_capability buffer() const { return _buffer.dataspace(); } size_t release() { /* inform trace source about the new buffer */ Locked_ptr source(_source); /* source vanished */ if (!source.is_valid()) return 0; return _buffer.flush() + _policy.flush(); } }; /** * Registry to tracing subjects * * There exists one instance for each TRACE session. */ class Genode::Trace::Subject_registry { private: typedef List Subjects; Allocator &_md_alloc; Ram_session &_ram; Source_registry &_sources; unsigned _id_cnt; Lock _lock; Subjects _entries; /** * Functor for testing the existance of subjects for a given source * * This functor is invoked by 'Source_registry::export'. */ struct Tester { Subjects &subjects; Tester(Subjects &subjects) : subjects(subjects) { } bool operator () (unsigned source_id) { for (Subject *s = subjects.first(); s; s = s->next()) if (s->has_source_id(source_id)) return true; return false; } } _tester; /** * Functor for inserting new subjects into the registry * * This functor is invoked by 'Source_registry::export'. */ struct Inserter { Subject_registry ®istry; Inserter(Subject_registry ®istry) : registry(registry) { } void operator () (unsigned source_id, Weak_ptr source, Session_label const &label, Thread_name const &name) { Subject *subject = new (®istry._md_alloc) Subject(Subject_id(registry._id_cnt++), source_id, source, label, name); registry._entries.insert(subject); } } _inserter; /** * Destroy subject, and release policy and trace buffers * * \return RAM resources released during destruction */ size_t _unsynchronized_destroy(Subject *s) { _entries.remove(s); size_t const released_ram = s->release(); destroy(&_md_alloc, s); return released_ram; }; /** * Obtain subject from given session-local ID * * \throw Nonexistent_subject */ Subject *_unsynchronized_lookup_by_id(Subject_id id) { for (Subject *s = _entries.first(); s; s = s->next()) if (s->id() == id) return s; throw Nonexistent_subject(); } public: /** * Constructor * * \param md_alloc meta-data allocator used for allocating 'Subject' * objects. * \param ram RAM session used for the allocation of trace * buffers and policy dataspaces. */ Subject_registry(Allocator &md_alloc, Ram_session &ram, Source_registry &sources) : _md_alloc(md_alloc), _ram(ram), _sources(sources), _id_cnt(0), _tester(_entries), _inserter(*this) { } /** * Destructor */ ~Subject_registry() { Lock guard(_lock); while (Subject *s = _entries.first()) _unsynchronized_destroy(s); } /** * \throw Ram_session::Quota_exceeded */ void import_new_sources(Source_registry &sources) { Lock guard(_lock); _sources.export_sources(_tester, _inserter); } /** * Retrieve existing subject IDs */ size_t subjects(Subject_id *dst, size_t dst_len) { Lock guard(_lock); unsigned i = 0; for (Subject *s = _entries.first(); s && i < dst_len; s = s->next()) dst[i++] = s->id(); return i; } /** * Remove subject and release resources * * \return RAM resources released as a side effect for removing the * subject (i.e., if the subject held a trace buffer or * policy dataspace). The value does not account for * memory allocated from the metadata allocator. */ size_t release(Subject_id subject_id) { Lock guard(_lock); Subject *subject = _unsynchronized_lookup_by_id(subject_id); if (subject) return _unsynchronized_destroy(subject); return 0; } Subject *lookup_by_id(Subject_id id) { Lock guard(_lock); return _unsynchronized_lookup_by_id(id); } }; #endif /* _CORE__INCLUDE__TRACE__SUBJECT_REGISTRY_H_ */