/* * \brief Low-level test for TRACE service * \author Norman Feske * \author Josef Soentgen * \date 2013-08-12 */ /* * 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. */ /* Genode includes */ #include #include #include static char const *state_name(Genode::Trace::Subject_info::State state) { switch (state) { case Genode::Trace::Subject_info::INVALID: return "INVALID"; case Genode::Trace::Subject_info::UNTRACED: return "UNTRACED"; case Genode::Trace::Subject_info::TRACED: return "TRACED"; case Genode::Trace::Subject_info::FOREIGN: return "FOREIGN"; case Genode::Trace::Subject_info::ERROR: return "ERROR"; case Genode::Trace::Subject_info::DEAD: return "DEAD"; } return "undefined"; } struct Test_thread : Genode::Thread<1024 * sizeof (unsigned long)> { Timer::Connection _timer; void entry() { using namespace Genode; for (size_t i = 0; ; i++) { if (i & 0x3) { Ram_dataspace_capability ds_cap = env()->ram_session()->alloc(1024); env()->ram_session()->free(ds_cap); } _timer.msleep(250); } } Test_thread(const char *name) : Thread(name) { start(); } }; using namespace Genode; class Trace_buffer_monitor { private: enum { MAX_ENTRY_BUF = 256 }; Trace::Subject_id _id; Trace::Buffer *_buffer; addr_t _read_head; addr_t _write_head; size_t _overflow; char _entry_buf[MAX_ENTRY_BUF]; const char * _next_entry() { size_t len = *(addr_t*) _read_head; char *p = (char*) _read_head; char tmp[len + 1]; p += sizeof(size_t); if (len > 0) memcpy(tmp, (void *)(p), len); tmp[len] = '\0'; _read_head = (addr_t)(p + len); snprintf(_entry_buf, MAX_ENTRY_BUF, "0x%lx '%s'", _read_head, tmp); return _entry_buf; } void _update_heads() { _write_head = _buffer->entries(); _write_head += _buffer->head_offset(); if (_write_head < _read_head) { _overflow++; /* XXX read missing entries before resetting */ _read_head = _buffer->entries(); } } public: Trace_buffer_monitor(Trace::Subject_id id, Dataspace_capability ds_cap) : _id(id), _buffer(env()->rm_session()->attach(ds_cap)), _read_head(_buffer->entries()), _write_head(_buffer->entries() + _buffer->head_offset()), _overflow(0) { PLOG("monitor subject:%d buffer:0x%lx start:0x%lx", _id.id, (addr_t)_buffer, _buffer->entries()); } ~Trace_buffer_monitor() { if (_buffer) env()->rm_session()->detach(_buffer); } Trace::Subject_id id() { return _id; }; void dump(unsigned limit) { _update_heads(); PLOG("overflows: %zu", _overflow); if (limit) { PLOG("read up-to %u events", limit); for (unsigned i = 0; i < limit; i++) { const char *s = _next_entry(); if (s) PLOG("%s", s); } } else { PLOG("read all remaining events"); while (_read_head < _write_head) { const char *s = _next_entry(); if (s) PLOG("%s", s); } } } }; int main(int argc, char **argv) { using namespace Genode; printf("--- test-trace started ---\n"); static Genode::Trace::Connection trace(1024*1024, 64*1024, 0); static Timer::Connection timer; static Test_thread test("test-thread"); static Trace_buffer_monitor *test_monitor = 0; Genode::Trace::Policy_id policy_id; bool policy_set = false; char policy_label[64]; char policy_module[64]; Rom_dataspace_capability policy_module_rom_ds; try { Xml_node policy = config()->xml_node().sub_node("trace_policy"); for (;; policy = policy.next("trace_policy")) { try { policy.attribute("label").value(policy_label, sizeof (policy_label)); policy.attribute("module").value(policy_module, sizeof (policy_module)); static Rom_connection policy_rom(policy_module); policy_module_rom_ds = policy_rom.dataspace(); size_t rom_size = Dataspace_client(policy_module_rom_ds).size(); policy_id = trace.alloc_policy(rom_size); Dataspace_capability ds_cap = trace.policy(policy_id); if (ds_cap.valid()) { void *ram = env()->rm_session()->attach(ds_cap); void *rom = env()->rm_session()->attach(policy_module_rom_ds); memcpy(ram, rom, rom_size); env()->rm_session()->detach(ram); env()->rm_session()->detach(rom); } } catch (...) { PERR("could not load module '%s' for label '%s'", policy_module, policy_label); } PINF("load module: '%s' for label: '%s'", policy_module, policy_label); if (policy.is_last("trace_policy")) break; } } catch (...) { } for (size_t cnt = 0; cnt < 5; cnt++) { timer.msleep(3000); Trace::Subject_id subjects[32]; size_t num_subjects = trace.subjects(subjects, 32); printf("%zd tracing subjects present\n", num_subjects); for (size_t i = 0; i < num_subjects; i++) { Trace::Subject_info info = trace.subject_info(subjects[i]); printf("ID:%d label:\"%s\" name:\"%s\" state:%s policy:%d\n", subjects[i].id, info.session_label().string(), info.thread_name().string(), state_name(info.state()), info.policy_id().id); /* enable tracing */ if (!policy_set && strcmp(info.session_label().string(), policy_label) == 0 && strcmp(info.thread_name().string(), "test-thread") == 0) { try { PINF("enable tracing for thread:'%s' with policy:%d", info.thread_name().string(), policy_id.id); trace.trace(subjects[i].id, policy_id, 16384U); Dataspace_capability ds_cap = trace.buffer(subjects[i].id); test_monitor = new (env()->heap()) Trace_buffer_monitor(subjects[i].id, ds_cap); } catch (Trace::Source_is_dead) { PERR("source is dead"); } policy_set = true; } /* read events from trace buffer */ if (test_monitor) { if (subjects[i].id == test_monitor->id().id) test_monitor->dump(0); } } } if (test_monitor) destroy(env()->heap(), test_monitor); printf("--- test-trace finished ---\n"); return 0; }