195 lines
7.8 KiB
C++
195 lines
7.8 KiB
C++
/*
|
|
* \brief Log information about trace subjects
|
|
* \author Martin Stein
|
|
* \date 2018-01-15
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2018 Genode Labs GmbH
|
|
*
|
|
* This file is part of the Genode OS framework, which is distributed
|
|
* under the terms of the GNU Affero General Public License version 3.
|
|
*/
|
|
|
|
/* local includes */
|
|
#include <policy.h>
|
|
#include <monitor.h>
|
|
#include <xml_node.h>
|
|
|
|
/* Genode includes */
|
|
#include <base/component.h>
|
|
#include <base/attached_rom_dataspace.h>
|
|
#include <base/heap.h>
|
|
#include <os/session_policy.h>
|
|
#include <timer_session/connection.h>
|
|
#include <util/construct_at.h>
|
|
|
|
using namespace Genode;
|
|
using Thread_name = String<40>;
|
|
|
|
|
|
class Main
|
|
{
|
|
private:
|
|
|
|
enum { MAX_SUBJECTS = 512 };
|
|
enum { DEFAULT_PERIOD_SEC = 5 };
|
|
enum { DEFAULT_BUFFER = 1024 * 4 };
|
|
enum { DEFAULT_SESSION_ARG_BUFFER = 1024 * 4 };
|
|
enum { DEFAULT_SESSION_RAM = 1024 * 1024 };
|
|
enum { DEFAULT_SESSION_PARENT_LEVELS = 0 };
|
|
|
|
Env &_env;
|
|
Timer::Connection _timer { _env };
|
|
Attached_rom_dataspace _config_rom { _env, "config" };
|
|
Xml_node const _config { _config_rom.xml() };
|
|
Trace::Connection _trace { _env,
|
|
_config.attribute_value("session_ram", Number_of_bytes(DEFAULT_SESSION_RAM)),
|
|
_config.attribute_value("session_arg_buffer", Number_of_bytes(DEFAULT_SESSION_ARG_BUFFER)),
|
|
_config.attribute_value("session_parent_levels", (unsigned)DEFAULT_SESSION_PARENT_LEVELS) };
|
|
bool const _affinity { _config.attribute_value("affinity", false) };
|
|
bool const _activity { _config.attribute_value("activity", false) };
|
|
bool const _verbose { _config.attribute_value("verbose", false) };
|
|
Microseconds const _period_us { read_sec_attr(_config, "period_sec", DEFAULT_PERIOD_SEC) };
|
|
Number_of_bytes const _default_buf_sz { _config.attribute_value("default_buffer", Number_of_bytes(DEFAULT_BUFFER)) };
|
|
Timer::Periodic_timeout<Main> _period { _timer, *this, &Main::_handle_period, _period_us };
|
|
Heap _heap { _env.ram(), _env.rm() };
|
|
Monitor_tree _monitors_0 { };
|
|
Monitor_tree _monitors_1 { };
|
|
bool _monitors_switch { false };
|
|
Policy_tree _policies { };
|
|
Policy_name _default_policy_name { _config.attribute_value("default_policy", Policy_name("null")) };
|
|
Policy _default_policy { _env, _trace, _default_policy_name };
|
|
unsigned long _report_id { 0 };
|
|
unsigned long _num_subjects { 0 };
|
|
unsigned long _num_monitors { 0 };
|
|
Trace::Subject_id _subjects[MAX_SUBJECTS];
|
|
|
|
void _handle_period(Duration)
|
|
{
|
|
/*
|
|
* Update monitors
|
|
*
|
|
* Which monitors are held and how they are configured depends on:
|
|
*
|
|
* 1) Which subjects are available at the Trace session,
|
|
* 2) which tracing state the subjects are currently in,
|
|
* 3) the configuration of this component about which subjects
|
|
* to monitor and how
|
|
*
|
|
* All this might have changed since the last call of this method.
|
|
* So, adapt the monitors and the monitor tree accordingly.
|
|
*/
|
|
|
|
/*
|
|
* Switch monitor trees so that the new tree is empty and the old
|
|
* tree contains all monitors.
|
|
*/
|
|
Monitor_tree &old_monitors = _monitors_switch ? _monitors_1 : _monitors_0;
|
|
Monitor_tree &new_monitors = _monitors_switch ? _monitors_0 : _monitors_1;
|
|
_monitors_switch = !_monitors_switch;
|
|
|
|
/* update available subject IDs and iterate over them */
|
|
try { _num_subjects = _trace.subjects(_subjects, MAX_SUBJECTS); }
|
|
catch (Out_of_ram ) { warning("Cannot list subjects: Out_of_ram" ); return; }
|
|
catch (Out_of_caps) { warning("Cannot list subjects: Out_of_caps"); return; }
|
|
for (unsigned i = 0; i < _num_subjects; i++) {
|
|
|
|
Trace::Subject_id const id = _subjects[i];
|
|
try {
|
|
/* skip dead subjects */
|
|
if (_trace.subject_info(id).state() == Trace::Subject_info::DEAD)
|
|
continue;
|
|
|
|
/* check if there is a matching policy in the XML config */
|
|
Session_policy session_policy = _session_policy(id);
|
|
try {
|
|
/* lookup monitor by subject ID */
|
|
Monitor &monitor = old_monitors.find_by_subject_id(id);
|
|
|
|
/* move monitor from old to new tree */
|
|
old_monitors.remove(&monitor);
|
|
new_monitors.insert(&monitor);
|
|
|
|
} catch (Monitor_tree::No_match) {
|
|
|
|
/* create monitor for subject in the new tree */
|
|
_new_monitor(new_monitors, id, session_policy);
|
|
}
|
|
}
|
|
catch (Trace::Nonexistent_subject ) { continue; }
|
|
catch (Session_policy::No_policy_defined) { continue; }
|
|
}
|
|
/* all monitors in the old tree are deprecated, destroy them */
|
|
while (Monitor *monitor = old_monitors.first())
|
|
_destroy_monitor(old_monitors, *monitor);
|
|
|
|
/* dump information of each monitor in the new tree */
|
|
log("");
|
|
log("--- Report ", _report_id++, " (", _num_monitors, "/", _num_subjects, " subjects) ---");
|
|
new_monitors.for_each([&] (Monitor &monitor) {
|
|
monitor.print(_activity, _affinity);
|
|
});
|
|
}
|
|
|
|
void _destroy_monitor(Monitor_tree &monitors, Monitor &monitor)
|
|
{
|
|
if (_verbose)
|
|
log("destroy monitor: subject ", monitor.subject_id().id);
|
|
|
|
try { _trace.free(monitor.subject_id()); }
|
|
catch (Trace::Nonexistent_subject) { }
|
|
monitors.remove(&monitor);
|
|
destroy(_heap, &monitor);
|
|
_num_monitors--;
|
|
}
|
|
|
|
void _new_monitor(Monitor_tree &monitors,
|
|
Trace::Subject_id id,
|
|
Session_policy &session_policy)
|
|
{
|
|
try {
|
|
Number_of_bytes const buffer_sz = session_policy.attribute_value("buffer", _default_buf_sz);
|
|
Policy_name const policy_name = session_policy.attribute_value("policy", _default_policy_name);
|
|
try {
|
|
_trace.trace(id.id, _policies.find_by_name(policy_name).id(), buffer_sz);
|
|
} catch (Policy_tree::No_match) {
|
|
Policy &policy = *new (_heap) Policy(_env, _trace, policy_name);
|
|
_policies.insert(policy);
|
|
_trace.trace(id.id, policy.id(), buffer_sz);
|
|
}
|
|
monitors.insert(new (_heap) Monitor(_trace, _env.rm(), id));
|
|
}
|
|
catch (Trace::Already_traced ) { warning("Cannot activate tracing: Already_traced" ); return; }
|
|
catch (Trace::Source_is_dead ) { warning("Cannot activate tracing: Source_is_dead" ); return; }
|
|
catch (Trace::Nonexistent_policy ) { warning("Cannot activate tracing: Nonexistent_policy" ); return; }
|
|
catch (Trace::Traced_by_other_session) { warning("Cannot activate tracing: Traced_by_other_session"); return; }
|
|
catch (Trace::Nonexistent_subject ) { warning("Cannot activate tracing: Nonexistent_subject" ); return; }
|
|
catch (Region_map::Invalid_dataspace ) { warning("Cannot activate tracing: Loading policy failed" ); return; }
|
|
|
|
|
|
_num_monitors++;
|
|
if (_verbose)
|
|
log("new monitor: subject ", id.id);
|
|
}
|
|
|
|
Session_policy _session_policy(Trace::Subject_id id)
|
|
{
|
|
Trace::Subject_info info = _trace.subject_info(id);
|
|
Session_label const label(info.session_label());
|
|
Session_policy policy(label, _config);
|
|
if (policy.has_attribute("thread"))
|
|
if (policy.attribute_value("thread", Thread_name()) != info.thread_name())
|
|
throw Session_policy::No_policy_defined();
|
|
|
|
return policy;
|
|
}
|
|
|
|
public:
|
|
|
|
Main(Env &env) : _env(env) { _policies.insert(_default_policy); }
|
|
};
|
|
|
|
|
|
void Component::construct(Env &env) { static Main main(env); }
|