From dc26910fc343b4208a54b25b49145d9a38cd0f70 Mon Sep 17 00:00:00 2001 From: Christian Prochaska Date: Wed, 24 Aug 2016 17:23:06 +0200 Subject: [PATCH] cpu_sampler component for statistical profiling Fixes #2075 --- repos/gems/run/cpu_sampler.run | 90 +++++++ repos/gems/src/server/cpu_sampler/README | 81 +++++++ repos/gems/src/server/cpu_sampler/cpu_root.h | 72 ++++++ .../cpu_sampler/cpu_session_component.cc | 152 ++++++++++++ .../cpu_sampler/cpu_session_component.h | 112 +++++++++ .../cpu_sampler/cpu_thread_component.cc | 219 ++++++++++++++++++ .../server/cpu_sampler/cpu_thread_component.h | 95 ++++++++ repos/gems/src/server/cpu_sampler/main.cc | 205 ++++++++++++++++ .../gems/src/server/cpu_sampler/native_cpu.cc | 26 +++ .../server/cpu_sampler/spec/foc/native_cpu.cc | 96 ++++++++ .../src/server/cpu_sampler/spec/foc/target.mk | 7 + .../src/server/cpu_sampler/spec/hw/target.mk | 7 + .../cpu_sampler/spec/nova/native_cpu.cc | 83 +++++++ .../server/cpu_sampler/spec/nova/target.mk | 7 + .../server/cpu_sampler/spec/okl4/target.mk | 7 + .../server/cpu_sampler/spec/sel4/target.mk | 7 + repos/gems/src/server/cpu_sampler/target.inc | 11 + .../cpu_sampler/thread_list_change_handler.h | 22 ++ repos/gems/src/test/cpu_sampler/main.cc | 28 +++ repos/gems/src/test/cpu_sampler/target.mk | 3 + tool/autopilot.list | 1 + 21 files changed, 1331 insertions(+) create mode 100644 repos/gems/run/cpu_sampler.run create mode 100644 repos/gems/src/server/cpu_sampler/README create mode 100644 repos/gems/src/server/cpu_sampler/cpu_root.h create mode 100644 repos/gems/src/server/cpu_sampler/cpu_session_component.cc create mode 100644 repos/gems/src/server/cpu_sampler/cpu_session_component.h create mode 100644 repos/gems/src/server/cpu_sampler/cpu_thread_component.cc create mode 100644 repos/gems/src/server/cpu_sampler/cpu_thread_component.h create mode 100644 repos/gems/src/server/cpu_sampler/main.cc create mode 100644 repos/gems/src/server/cpu_sampler/native_cpu.cc create mode 100644 repos/gems/src/server/cpu_sampler/spec/foc/native_cpu.cc create mode 100644 repos/gems/src/server/cpu_sampler/spec/foc/target.mk create mode 100644 repos/gems/src/server/cpu_sampler/spec/hw/target.mk create mode 100644 repos/gems/src/server/cpu_sampler/spec/nova/native_cpu.cc create mode 100644 repos/gems/src/server/cpu_sampler/spec/nova/target.mk create mode 100644 repos/gems/src/server/cpu_sampler/spec/okl4/target.mk create mode 100644 repos/gems/src/server/cpu_sampler/spec/sel4/target.mk create mode 100644 repos/gems/src/server/cpu_sampler/target.inc create mode 100644 repos/gems/src/server/cpu_sampler/thread_list_change_handler.h create mode 100644 repos/gems/src/test/cpu_sampler/main.cc create mode 100644 repos/gems/src/test/cpu_sampler/target.mk diff --git a/repos/gems/run/cpu_sampler.run b/repos/gems/run/cpu_sampler.run new file mode 100644 index 000000000..613fd3964 --- /dev/null +++ b/repos/gems/run/cpu_sampler.run @@ -0,0 +1,90 @@ +if { ![have_spec foc] && ![have_spec hw] && ![have_spec nova] && + ![have_spec okl4] && ![have_spec sel4] } { + puts "Run script is not supported on this platform" + exit 0 +} + +build { + core + init + drivers/timer + server/cpu_sampler + test/cpu_sampler +} + +create_boot_directory + +install_config { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +} + +build_boot_image { + core + init + timer + cpu_sampler + test-cpu_sampler +} + +append qemu_args "-nographic -m 128" + +set match_string "Test started. func: 0x(\[0-9a-f\]+).*\n" + +run_genode_until "$match_string" 10 + +regexp $match_string $output all func + +run_genode_until "\\\[init -> cpu_sampler -> samples -> init -> test-cpu_sampler -> ep\\\.1] \[0\]*$func" 2 [output_spawn_id] diff --git a/repos/gems/src/server/cpu_sampler/README b/repos/gems/src/server/cpu_sampler/README new file mode 100644 index 000000000..a85a49364 --- /dev/null +++ b/repos/gems/src/server/cpu_sampler/README @@ -0,0 +1,81 @@ +This component implements a CPU service which samples the instruction pointer +of the configured threads on a regular basis for the purpose of statistical +profiling. + +The collected samples are written to the LOG session with an individual label +for each thread. By using the 'fs_log' component, the sample data can be +written into separate files if desired. + +Configuration options +--------------------- + +! +! +! + +The 'sample_interval_ms' attribute configures the time between two samples in +milliseconds. + +The 'sample_duration_s' attribute configures the overall duration of the +sampling activity in seconds. + +The policy configures the threads to be sampled. + +The clients of the CPU sampler component must be at least grand children of the +initial init process to have their CPU sessions routed correctly. An example +configuration using a sub-init process can be found in the 'cpu_sampler.run' +script. + +Evaluation +---------- + +Currently, some basic tools for the evaluation of the sampled addresses are +available at + +[https://github.com/cproc/genode_stuff/tree/cpu_sampler-16.08] + +* Filtering the sampled addresses from the Genode log output + + ! filter_sampled_addresses_from_log + + This script extracts the sampled addresses from a file containing the Genode + log output and saves them in the file 'sampled_addresses.txt'. It is not + needed when the addresses have already been written into a separate file by + the 'fs_log' component. The match string (label) in the script might need to + be adapted for the specific scenario. + +* Filtering the shared library load addresses from the Genode log output + + ! filter_ldso_addresses_from_log + + This script extracts the shared library load addresses from a file containing + the Genode log output and saves them in the file 'ldso_addresses.txt'. To have + these addresses appear in the Genode log output, the sampled component should + be configured with the 'ld_verbose="yes"' XML attribute if it uses shared + libraries. If multiple components in a scenario are configured with this + attribute, the script needs to be adapted to match a specific label. + +* Generating statistics + + ! generate_statistics [] + + This script generates the files 'statistics_by_function.txt' and + 'statistics_by_address.txt'. + + The first argument is the name of the ELF image of the sampled component. + The second argument is the name of a file containing the sampled addresses. + The third argument is the name of a file containing the shared library load + addresses. It is only needed if the sampled component uses shared libraries. + + The 'statistics_by_function.txt' file lists the names of the sampled + functions, sorted by the highest sample count. Each line comprehends + all sampled addresses which belong to the particular function. + + The 'statistics_by_address.txt' file is more detailed than the + 'statistics_by_function.txt' file. It lists the sampled addresses, sorted by + the highest sample count, together with the name and file location of the + function the particular address belongs to. + +The 'generate_statistics' script uses the 'backtrace' script to determine the +function names and file locations. The best location to use the scripts is +the 'build/.../bin' directory, where all the shared libraries can be found. diff --git a/repos/gems/src/server/cpu_sampler/cpu_root.h b/repos/gems/src/server/cpu_sampler/cpu_root.h new file mode 100644 index 000000000..fba08f7e9 --- /dev/null +++ b/repos/gems/src/server/cpu_sampler/cpu_root.h @@ -0,0 +1,72 @@ +/* + * \brief CPU root interface + * \author Christian Prochaska + * \date 2016-01-19 + */ + +/* + * Copyright (C) 2016 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 _CPU_ROOT_H_ +#define _CPU_ROOT_H_ + +/* Genode includes */ +#include + +/* local includes */ +#include "cpu_session_component.h" +#include "cpu_thread_component.h" +#include "thread_list_change_handler.h" + +namespace Cpu_sampler { + using namespace Genode; + class Cpu_root; +} + +class Cpu_sampler::Cpu_root : public Root_component +{ + private: + + Rpc_entrypoint &_thread_ep; + Allocator &_md_alloc; + Thread_list &_thread_list; + Thread_list_change_handler &_thread_list_change_handler; + + protected: + + Cpu_session_component *_create_session(const char *args) override + { + Cpu_session_component *cpu_session_component = + new (md_alloc()) Cpu_session_component(_thread_ep, + _md_alloc, + _thread_list, + _thread_list_change_handler, + args); + return cpu_session_component; + } + + void _upgrade_session(Cpu_session_component *cpu, const char *args) override + { + env()->parent()->upgrade(cpu->parent_cpu_session(), args); + } + + public: + + Cpu_root(Rpc_entrypoint &session_ep, + Rpc_entrypoint &thread_ep, + Allocator &md_alloc, + Thread_list &thread_list, + Thread_list_change_handler &thread_list_change_handler) + : Root_component(&session_ep, &md_alloc), + _thread_ep(thread_ep), + _md_alloc(md_alloc), + _thread_list(thread_list), + _thread_list_change_handler(thread_list_change_handler) { } + +}; + +#endif /* _CPU_ROOT_H_ */ diff --git a/repos/gems/src/server/cpu_sampler/cpu_session_component.cc b/repos/gems/src/server/cpu_sampler/cpu_session_component.cc new file mode 100644 index 000000000..268869372 --- /dev/null +++ b/repos/gems/src/server/cpu_sampler/cpu_session_component.cc @@ -0,0 +1,152 @@ +/* + * \brief Implementation of the CPU session interface + * \author Christian Prochaska + * \date 2016-01-19 + */ + +/* + * Copyright (C) 2016 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 "cpu_session_component.h" +#include +#include + +using namespace Genode; +using namespace Cpu_sampler; + + +Thread_capability +Cpu_sampler::Cpu_session_component::create_thread(Pd_session_capability pd, + Name const &name, + Affinity::Location affinity, + Weight weight, + addr_t utcb) +{ + Cpu_thread_component *cpu_thread = new (_md_alloc) + Cpu_thread_component(*this, + _md_alloc, + pd, + name, + affinity, + weight, + utcb, + name.string(), + _next_thread_id); + + _thread_list.insert(new (_md_alloc) Thread_element(cpu_thread)); + + _thread_list_change_handler.thread_list_changed(); + + _next_thread_id++; + + return cpu_thread->cap(); +} + + +void Cpu_sampler::Cpu_session_component::kill_thread(Thread_capability thread_cap) +{ + auto lambda = [&] (Thread_element *cpu_thread_element) { + + Cpu_thread_component *cpu_thread = cpu_thread_element->object(); + + if (cpu_thread->cap() == thread_cap) { + _thread_list.remove(cpu_thread_element); + destroy(_md_alloc, cpu_thread_element); + destroy(_md_alloc, cpu_thread); + _thread_list_change_handler.thread_list_changed(); + } + }; + + for_each_thread(_thread_list, lambda); + + _parent_cpu_session.kill_thread(thread_cap); +} + + +void +Cpu_sampler::Cpu_session_component::exception_sigh(Signal_context_capability handler) +{ + _parent_cpu_session.exception_sigh(handler); +} + + +Affinity::Space Cpu_sampler::Cpu_session_component::affinity_space() const +{ + return _parent_cpu_session.affinity_space(); +} + + +Dataspace_capability +Cpu_sampler::Cpu_session_component::trace_control() +{ + return _parent_cpu_session.trace_control(); +} + + +Cpu_sampler::Cpu_session_component::Cpu_session_component( + Rpc_entrypoint &thread_ep, + Allocator &md_alloc, + Thread_list &thread_list, + Thread_list_change_handler &thread_list_change_handler, + char const *args) +: _thread_ep(thread_ep), + _parent_cpu_session(env()->parent()->session(args)), + _md_alloc(md_alloc), + _thread_list(thread_list), + _thread_list_change_handler(thread_list_change_handler), + _session_label(label_from_args(args)), + _native_cpu_cap(_setup_native_cpu()) +{ } + + +Cpu_sampler::Cpu_session_component::~Cpu_session_component() +{ + _cleanup_native_cpu(); + + auto lambda = [&] (Thread_element *cpu_thread_element) { + + Cpu_thread_component *cpu_thread = cpu_thread_element->object(); + + if (cpu_thread->cpu_session_component() == this) { + _thread_list.remove(cpu_thread_element); + destroy(_md_alloc, cpu_thread_element); + destroy(_md_alloc, cpu_thread); + } + }; + + for_each_thread(_thread_list, lambda); + + _thread_list_change_handler.thread_list_changed(); +} + + +int Cpu_sampler::Cpu_session_component::ref_account(Cpu_session_capability cap) +{ + return _parent_cpu_session.ref_account(cap); +} + + +int Cpu_sampler::Cpu_session_component::transfer_quota(Cpu_session_capability cap, + size_t size) +{ + return _parent_cpu_session.transfer_quota(cap, size); +} + + +Cpu_session::Quota Cpu_sampler::Cpu_session_component::quota() +{ + return _parent_cpu_session.quota(); +} + + +Capability +Cpu_sampler::Cpu_session_component::native_cpu() +{ + return _native_cpu_cap; +} diff --git a/repos/gems/src/server/cpu_sampler/cpu_session_component.h b/repos/gems/src/server/cpu_sampler/cpu_session_component.h new file mode 100644 index 000000000..fb6f90771 --- /dev/null +++ b/repos/gems/src/server/cpu_sampler/cpu_session_component.h @@ -0,0 +1,112 @@ +/* + * \brief CPU session component interface + * \author Christian Prochaska + * \date 2016-01-18 + */ + +/* + * Copyright (C) 2016 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 _CPU_SESSION_COMPONENT_H_ +#define _CPU_SESSION_COMPONENT_H_ + +/* Genode includes */ +#include +#include +#include +#include + +/* local includes */ +#include "cpu_thread_component.h" +#include "thread_list_change_handler.h" + +namespace Cpu_sampler { + using namespace Genode; + class Cpu_session_component; + typedef List> Thread_list; + typedef List_element Thread_element; + + template + void for_each_thread(Thread_list &thread_list, FN const &fn); +} + + +template +void Cpu_sampler::for_each_thread(Thread_list &thread_list, FN const &fn) +{ + Thread_element *next_cpu_thread_element = 0; + + for (Thread_element *cpu_thread_element = thread_list.first(); + cpu_thread_element; + cpu_thread_element = next_cpu_thread_element) { + + next_cpu_thread_element = cpu_thread_element->next(); + + fn(cpu_thread_element); + } +} + + +class Cpu_sampler::Cpu_session_component : public Rpc_object +{ + private: + + Rpc_entrypoint &_thread_ep; + + Cpu_session_client _parent_cpu_session; + Allocator &_md_alloc; + Thread_list &_thread_list; + Thread_list_change_handler &_thread_list_change_handler; + Session_label _session_label; + unsigned int _next_thread_id = 0; + + Capability _native_cpu_cap; + + Capability _setup_native_cpu(); + void _cleanup_native_cpu(); + + public: + + Session_label &session_label() { return _session_label; } + Cpu_session_client &parent_cpu_session() { return _parent_cpu_session; } + Rpc_entrypoint &thread_ep() { return _thread_ep; } + + /** + * Constructor + */ + Cpu_session_component(Rpc_entrypoint &thread_ep, + Allocator &md_alloc, + Thread_list &thread_list, + Thread_list_change_handler &thread_list_change_handler, + char const *args); + + /** + * Destructor + */ + ~Cpu_session_component(); + + + /*************************** + ** CPU session interface ** + ***************************/ + + Thread_capability create_thread(Pd_session_capability pd, + Name const &, + Affinity::Location, + Weight, + addr_t) override; + void kill_thread(Thread_capability) override; + void exception_sigh(Signal_context_capability handler) override; + Affinity::Space affinity_space() const override; + Dataspace_capability trace_control() override; + int ref_account(Cpu_session_capability c) override; + int transfer_quota(Cpu_session_capability c, size_t q) override; + Quota quota() override; + Capability native_cpu() override; +}; + +#endif /* _CPU_SESSION_COMPONENT_H_ */ diff --git a/repos/gems/src/server/cpu_sampler/cpu_thread_component.cc b/repos/gems/src/server/cpu_sampler/cpu_thread_component.cc new file mode 100644 index 000000000..8adccfca8 --- /dev/null +++ b/repos/gems/src/server/cpu_sampler/cpu_thread_component.cc @@ -0,0 +1,219 @@ +/* + * \brief Cpu_thread_component implementation + * \author Christian Prochaska + * \date 2016-01-19 + */ + +/* + * Copyright (C) 2016 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 + +/* local includes */ +#include "cpu_session_component.h" + +static constexpr bool verbose_take_sample = false; + +using namespace Genode; + +Cpu_sampler::Cpu_thread_component::Cpu_thread_component( + Cpu_session_component &cpu_session_component, + Allocator &md_alloc, + Pd_session_capability pd, + Cpu_session::Name const &name, + Affinity::Location affinity, + Cpu_session::Weight weight, + addr_t utcb, + char const *thread_name, + unsigned int thread_id) +: _cpu_session_component(cpu_session_component), + _md_alloc(md_alloc), + _parent_cpu_thread( + _cpu_session_component.parent_cpu_session().create_thread(pd, + name, + affinity, + weight, + utcb)) +{ + char label_buf[Session_label::size()]; + + snprintf(label_buf, sizeof(label_buf), "%s -> %s", + _cpu_session_component.session_label().string(), + thread_name); + + _label = Session_label(label_buf); + + snprintf(label_buf, sizeof(label_buf), "samples -> %s.%u", + _label.string(), thread_id); + + _log_session_label = Session_label(label_buf); + + _cpu_session_component.thread_ep().manage(this); +} + + +Cpu_sampler::Cpu_thread_component::~Cpu_thread_component() +{ + flush(); + + if (_log) + destroy(_md_alloc, _log); + + _cpu_session_component.thread_ep().dissolve(this); +} + + +void Cpu_sampler::Cpu_thread_component::take_sample() +{ + if (verbose_take_sample) + Genode::log("taking sample of thread ", _label.string()); + + if (!_started) { + if (verbose_take_sample) + Genode::log("cannot take sample, thread not started yet"); + return; + } + + try { + + _parent_cpu_thread.pause(); + + Thread_state thread_state = _parent_cpu_thread.state(); + + _parent_cpu_thread.resume(); + + _sample_buf[_sample_buf_index++] = thread_state.ip; + + if (_sample_buf_index == SAMPLE_BUF_SIZE) + flush(); + + } catch (Cpu_thread::State_access_failed) { + + Genode::log("thread state access failed"); + + } +} + + +void Cpu_sampler::Cpu_thread_component::reset() +{ + _sample_buf_index = 0; +} + + +void Cpu_sampler::Cpu_thread_component::flush() +{ + if (_sample_buf_index == 0) + return; + + if (!_log) + _log = new (_md_alloc) Log_connection(_log_session_label); + + /* number of hex characters + newline + '\0' */ + enum { SAMPLE_STRING_SIZE = 2 * sizeof(addr_t) + 1 + 1 }; + + char sample_string[SAMPLE_STRING_SIZE]; + + char const *format_string; + + if (sizeof(addr_t) == 8) + format_string = "%16lX\n"; + else + format_string = "%8X\n"; + + for (unsigned int i = 0; i < _sample_buf_index; i++) { + snprintf(sample_string, SAMPLE_STRING_SIZE, format_string, + _sample_buf[i]); + _log->write(sample_string); + } + + _sample_buf_index = 0; +} + + +Dataspace_capability +Cpu_sampler::Cpu_thread_component::utcb() +{ + return _parent_cpu_thread.utcb(); +} + + +void Cpu_sampler::Cpu_thread_component::start(addr_t ip, addr_t sp) +{ + _parent_cpu_thread.start(ip, sp); + _started = true; +} + + +void Cpu_sampler::Cpu_thread_component::pause() +{ + _parent_cpu_thread.pause(); +} + + +void Cpu_sampler::Cpu_thread_component::resume() +{ + _parent_cpu_thread.resume(); +} + + +void Cpu_sampler::Cpu_thread_component::single_step(bool enable) +{ + _parent_cpu_thread.single_step(enable); +} + + +void Cpu_sampler::Cpu_thread_component::cancel_blocking() +{ + _parent_cpu_thread.cancel_blocking(); +} + + +Thread_state Cpu_sampler::Cpu_thread_component::state() +{ + return _parent_cpu_thread.state(); +} + + +void Cpu_sampler::Cpu_thread_component::state(Thread_state const &state) +{ + _parent_cpu_thread.state(state); +} + + +void +Cpu_sampler::Cpu_thread_component::exception_sigh(Signal_context_capability sigh_cap) +{ + _parent_cpu_thread.exception_sigh(sigh_cap); +} + + +void Cpu_sampler::Cpu_thread_component::affinity(Affinity::Location location) +{ + _parent_cpu_thread.affinity(location); +} + + +unsigned Cpu_sampler::Cpu_thread_component::trace_control_index() +{ + return _parent_cpu_thread.trace_control_index(); +} + + +Dataspace_capability +Cpu_sampler::Cpu_thread_component::trace_buffer() +{ + return _parent_cpu_thread.trace_buffer(); +} + + +Dataspace_capability +Cpu_sampler::Cpu_thread_component::trace_policy() +{ + return _parent_cpu_thread.trace_policy(); +} diff --git a/repos/gems/src/server/cpu_sampler/cpu_thread_component.h b/repos/gems/src/server/cpu_sampler/cpu_thread_component.h new file mode 100644 index 000000000..41b03f68b --- /dev/null +++ b/repos/gems/src/server/cpu_sampler/cpu_thread_component.h @@ -0,0 +1,95 @@ +/* + * \brief Cpu_thread_component interface + * \author Christian Prochaska + * \date 2016-01-19 + */ + +/* + * Copyright (C) 2016 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 _CPU_THREAD_COMPONENT_H_ +#define _CPU_THREAD_COMPONENT_H_ + +/* Genode includes */ +#include +#include +#include + +/* local includes */ +#include "cpu_session_component.h" + +namespace Cpu_sampler { + using namespace Genode; + class Cpu_thread_component; + class Cpu_session_component; +} + +class Cpu_sampler::Cpu_thread_component : public Rpc_object +{ + private: + + enum { SAMPLE_BUF_SIZE = 1024 }; + + Cpu_session_component &_cpu_session_component; + + Allocator &_md_alloc; + + Cpu_thread_client _parent_cpu_thread; + + bool _started = false; + + Session_label _label; + Session_label _log_session_label; + + Genode::addr_t _sample_buf[SAMPLE_BUF_SIZE]; + unsigned int _sample_buf_index = 0; + + Log_connection *_log = 0; + + public: + + Cpu_thread_component(Cpu_session_component &cpu_session_component, + Allocator &md_alloc, + Pd_session_capability pd, + Cpu_session::Name const &name, + Affinity::Location affinity, + Cpu_session::Weight weight, + addr_t utcb, + char const *thread_name, + unsigned int thread_id); + ~Cpu_thread_component(); + + Cpu_session_component const *cpu_session_component() const + { return &_cpu_session_component; } + + Thread_capability parent_thread() { return _parent_cpu_thread; } + Session_label &label() { return _label; } + + void take_sample(); + void reset(); + void flush(); + + /************************** + ** CPU thread interface ** + *************************/ + + Dataspace_capability utcb() override; + void start(addr_t, addr_t) override; + void pause() override; + void resume() override; + void single_step(bool) override; + void cancel_blocking() override; + Thread_state state() override; + void state(Thread_state const &) override; + void exception_sigh(Signal_context_capability) override; + void affinity(Affinity::Location) override; + unsigned trace_control_index() override; + Dataspace_capability trace_buffer() override; + Dataspace_capability trace_policy() override; +}; + +#endif /* _CPU_THREAD_COMPONENT_H_ */ diff --git a/repos/gems/src/server/cpu_sampler/main.cc b/repos/gems/src/server/cpu_sampler/main.cc new file mode 100644 index 000000000..195011910 --- /dev/null +++ b/repos/gems/src/server/cpu_sampler/main.cc @@ -0,0 +1,205 @@ +/* + * \brief CPU sampler + * \author Christian Prochaska + * \date 2016-01-15 + */ + +/* + * Copyright (C) 2016 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 +#include +#include +#include +#include +#include +#include +#include + +/* local includes */ +#include "cpu_root.h" +#include "cpu_session_component.h" +#include "cpu_thread_component.h" +#include "thread_list_change_handler.h" + +namespace Cpu_sampler { struct Main; } + +static constexpr bool verbose = false; +static constexpr bool verbose_missed_timeouts = false; +static constexpr bool verbose_sample_duration = true; + + +/****************** + ** Main program ** + ******************/ + +struct Cpu_sampler::Main : Thread_list_change_handler +{ + Genode::Env &env; + Genode::Heap alloc; + Cpu_root cpu_root; + Attached_rom_dataspace config; + Timer::Connection timer; + Thread_list thread_list; + Thread_list selected_thread_list; + + unsigned int sample_index; + unsigned int max_sample_index; + unsigned int timeout_us; + + + void handle_timeout(unsigned int num) + { + if (verbose_missed_timeouts && (num > 1)) + Genode::log("missed ", num - 1, " timeouts"); + + auto lambda = [&] (Thread_element *cpu_thread_element) { + + Cpu_thread_component *cpu_thread = cpu_thread_element->object(); + + cpu_thread->take_sample(); + + if (sample_index == max_sample_index) + cpu_thread->flush(); + }; + + for_each_thread(selected_thread_list, lambda); + + if (verbose_sample_duration && (sample_index == max_sample_index)) + Genode::log("sample period finished"); + + sample_index++; + + if (sample_index == max_sample_index) + timer.trigger_once(timeout_us); + } + + + Signal_rpc_member
timeout_dispatcher = + { env.ep(), *this, &Main::handle_timeout }; + + + void handle_config_update(unsigned) + { + config.update(); + + sample_index = 0; + + unsigned int sample_interval_ms = + config.xml().attribute_value("sample_interval_ms", 1000); + + unsigned int sample_duration_s = + config.xml().attribute_value("sample_duration_s", 10); + + max_sample_index = ((sample_duration_s * 1000) / sample_interval_ms) - 1; + + timeout_us = sample_interval_ms * 1000; + + thread_list_changed(); + + if (verbose_sample_duration) + Genode::log("starting a new sample period"); + + timer.trigger_periodic(timeout_us); + } + + + Signal_rpc_member
config_update_dispatcher = + { env.ep(), *this, &Main::handle_config_update}; + + + void thread_list_changed() override + { + /* clear selected_thread_list */ + + auto remove_lambda = [&] (Thread_element *cpu_thread_element) { + + if (verbose) + Genode::log("removing thread ", + cpu_thread_element->object()->label().string(), + " from selection"); + + selected_thread_list.remove(cpu_thread_element); + destroy(&alloc, cpu_thread_element); + }; + + for_each_thread(selected_thread_list, remove_lambda); + + + /* generate new selected_thread_list */ + + auto insert_lambda = [&] (Thread_element *cpu_thread_element) { + + Cpu_thread_component *cpu_thread = cpu_thread_element->object(); + + if (verbose) + Genode::log("evaluating thread ", cpu_thread->label().string()); + + try { + + Session_policy policy(cpu_thread->label(), config.xml()); + cpu_thread->reset(); + selected_thread_list.insert(new (&alloc) + Thread_element(cpu_thread)); + + if (verbose) + Genode::log("added thread ", + cpu_thread->label().string(), + " to selection"); + + } catch (Session_policy::No_policy_defined) { + + if (verbose) + Genode::log("no session policy defined for thread ", + cpu_thread->label().string()); + } + }; + + for_each_thread(thread_list, insert_lambda); + } + + + /** + * Constructor + */ + Main(Genode::Env &env) + : env(env), + alloc(env.ram(), env.rm()), + cpu_root(env.ep().rpc_ep(), env.ep().rpc_ep(), alloc, thread_list, *this), + config(env, "config") + { + /* + * Register signal handlers + */ + config.sigh(config_update_dispatcher); + timer.sigh(timeout_dispatcher); + + /* + * Apply initial configuration + */ + handle_config_update(0); + + /* + * Announce service + */ + env.parent().announce(env.ep().manage(cpu_root)); + } + +}; + + +/*************** + ** Component ** + ***************/ + +namespace Component { + Genode::size_t stack_size() { return 4*1024*sizeof(Genode::addr_t); } + void construct(Genode::Env &env) { static Cpu_sampler::Main inst(env); } +} diff --git a/repos/gems/src/server/cpu_sampler/native_cpu.cc b/repos/gems/src/server/cpu_sampler/native_cpu.cc new file mode 100644 index 000000000..2a525b26e --- /dev/null +++ b/repos/gems/src/server/cpu_sampler/native_cpu.cc @@ -0,0 +1,26 @@ +/* + * \brief Generic 'Native_cpu' setup + * \author Christian Prochaska + * \date 2016-05-13 + */ + +/* + * Copyright (C) 2016 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. + */ + + +/* Cpu_sampler includes */ +#include "cpu_session_component.h" + + +Genode::Capability +Cpu_sampler::Cpu_session_component::_setup_native_cpu() +{ + return parent_cpu_session().native_cpu(); +} + + +void Cpu_sampler::Cpu_session_component::_cleanup_native_cpu() { } diff --git a/repos/gems/src/server/cpu_sampler/spec/foc/native_cpu.cc b/repos/gems/src/server/cpu_sampler/spec/foc/native_cpu.cc new file mode 100644 index 000000000..2869e516d --- /dev/null +++ b/repos/gems/src/server/cpu_sampler/spec/foc/native_cpu.cc @@ -0,0 +1,96 @@ +/* + * \brief Fiasco.OC-specific 'Native_cpu' setup + * \author Christian Prochaska + * \date 2016-05-13 + */ + +/* + * Copyright (C) 2016 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 + +/* GDB monitor includes */ +#include "cpu_session_component.h" +#include "cpu_thread_component.h" + + +namespace Cpu_sampler { + class Native_cpu_component; +} + + +using namespace Genode; + + +class Cpu_sampler::Native_cpu_component : public Rpc_object +{ + private: + + Cpu_session_component &_cpu_session_component; + Foc_native_cpu_client _foc_native_cpu; + + public: + + Native_cpu_component(Cpu_session_component &cpu_session_component) + : _cpu_session_component(cpu_session_component), + _foc_native_cpu(_cpu_session_component.parent_cpu_session().native_cpu()) + { + _cpu_session_component.thread_ep().manage(this); + } + + ~Native_cpu_component() + { + _cpu_session_component.thread_ep().dissolve(this); + } + + void enable_vcpu(Thread_capability thread_cap, addr_t vcpu_state) override + { + auto lambda = [&] (Cpu_sampler::Cpu_thread_component *cpu_thread) { + _foc_native_cpu.enable_vcpu(cpu_thread->parent_thread(), vcpu_state); + }; + + _cpu_session_component.thread_ep().apply(thread_cap, lambda); + } + + Native_capability native_cap(Thread_capability thread_cap) override + { + auto lambda = [&] (Cpu_sampler::Cpu_thread_component *cpu_thread) { + return _foc_native_cpu.native_cap(cpu_thread->parent_thread()); + }; + + return _cpu_session_component.thread_ep().apply(thread_cap, lambda); + } + + Native_capability alloc_irq() override + { + return _foc_native_cpu.alloc_irq(); + } +}; + + +Capability +Cpu_sampler::Cpu_session_component::_setup_native_cpu() +{ + Native_cpu_component *native_cpu_component = + new (_md_alloc) Native_cpu_component(*this); + + return native_cpu_component->cap(); +} + + +void Cpu_sampler::Cpu_session_component::_cleanup_native_cpu() +{ + Native_cpu_component *native_cpu_component = nullptr; + _thread_ep.apply(_native_cpu_cap, [&] (Native_cpu_component *c) { native_cpu_component = c; }); + + if (!native_cpu_component) return; + + destroy(_md_alloc, native_cpu_component); +} diff --git a/repos/gems/src/server/cpu_sampler/spec/foc/target.mk b/repos/gems/src/server/cpu_sampler/spec/foc/target.mk new file mode 100644 index 000000000..ade4af62d --- /dev/null +++ b/repos/gems/src/server/cpu_sampler/spec/foc/target.mk @@ -0,0 +1,7 @@ +REQUIRES += foc + +SRC_CC += native_cpu.cc + +vpath native_cpu.cc $(PRG_DIR) + +include $(PRG_DIR)/../../target.inc diff --git a/repos/gems/src/server/cpu_sampler/spec/hw/target.mk b/repos/gems/src/server/cpu_sampler/spec/hw/target.mk new file mode 100644 index 000000000..a167e50b4 --- /dev/null +++ b/repos/gems/src/server/cpu_sampler/spec/hw/target.mk @@ -0,0 +1,7 @@ +REQUIRES += hw + +SRC_CC += native_cpu.cc + +vpath native_cpu.cc $(PRG_DIR)/../.. + +include $(PRG_DIR)/../../target.inc diff --git a/repos/gems/src/server/cpu_sampler/spec/nova/native_cpu.cc b/repos/gems/src/server/cpu_sampler/spec/nova/native_cpu.cc new file mode 100644 index 000000000..e71303b96 --- /dev/null +++ b/repos/gems/src/server/cpu_sampler/spec/nova/native_cpu.cc @@ -0,0 +1,83 @@ +/* + * \brief NOVA-specific 'Native_cpu' setup + * \author Christian Prochaska + * \date 2016-05-13 + */ + +/* + * Copyright (C) 2016 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 + +/* Cpu_sampler includes */ +#include "cpu_session_component.h" +#include "cpu_thread_component.h" + + +namespace Cpu_sampler { + class Native_cpu_component; +} + + +using namespace Genode; + + +class Cpu_sampler::Native_cpu_component : public Rpc_object +{ + private: + + Cpu_session_component &_cpu_session_component; + Nova_native_cpu_client _nova_native_cpu; + + public: + + Native_cpu_component(Cpu_session_component &cpu_session_component) + : _cpu_session_component(cpu_session_component), + _nova_native_cpu(_cpu_session_component.parent_cpu_session().native_cpu()) + { + _cpu_session_component.thread_ep().manage(this); + } + + ~Native_cpu_component() + { + _cpu_session_component.thread_ep().dissolve(this); + } + + Native_capability pager_cap(Thread_capability thread_cap) override + { + auto lambda = [&] (Cpu_sampler::Cpu_thread_component *cpu_thread) { + return _nova_native_cpu.pager_cap(cpu_thread->parent_thread()); + }; + + return _cpu_session_component.thread_ep().apply(thread_cap, lambda); + } +}; + + +Capability +Cpu_sampler::Cpu_session_component::_setup_native_cpu() +{ + Native_cpu_component *native_cpu_component = + new (_md_alloc) Native_cpu_component(*this); + + return native_cpu_component->cap(); +} + + +void Cpu_sampler::Cpu_session_component::_cleanup_native_cpu() +{ + Native_cpu_component *native_cpu_component = nullptr; + _thread_ep.apply(_native_cpu_cap, + [&] (Native_cpu_component *c) { native_cpu_component = c; }); + + if (!native_cpu_component) return; + + destroy(_md_alloc, native_cpu_component); +} diff --git a/repos/gems/src/server/cpu_sampler/spec/nova/target.mk b/repos/gems/src/server/cpu_sampler/spec/nova/target.mk new file mode 100644 index 000000000..215848b54 --- /dev/null +++ b/repos/gems/src/server/cpu_sampler/spec/nova/target.mk @@ -0,0 +1,7 @@ +REQUIRES += nova + +SRC_CC += native_cpu.cc + +vpath native_cpu.cc $(PRG_DIR) + +include $(PRG_DIR)/../../target.inc diff --git a/repos/gems/src/server/cpu_sampler/spec/okl4/target.mk b/repos/gems/src/server/cpu_sampler/spec/okl4/target.mk new file mode 100644 index 000000000..8c8aa47d0 --- /dev/null +++ b/repos/gems/src/server/cpu_sampler/spec/okl4/target.mk @@ -0,0 +1,7 @@ +REQUIRES += okl4 + +SRC_CC += native_cpu.cc + +vpath native_cpu.cc $(PRG_DIR)/../.. + +include $(PRG_DIR)/../../target.inc diff --git a/repos/gems/src/server/cpu_sampler/spec/sel4/target.mk b/repos/gems/src/server/cpu_sampler/spec/sel4/target.mk new file mode 100644 index 000000000..e070cd029 --- /dev/null +++ b/repos/gems/src/server/cpu_sampler/spec/sel4/target.mk @@ -0,0 +1,7 @@ +REQUIRES += sel4 + +SRC_CC += native_cpu.cc + +vpath native_cpu.cc $(PRG_DIR)/../.. + +include $(PRG_DIR)/../../target.inc diff --git a/repos/gems/src/server/cpu_sampler/target.inc b/repos/gems/src/server/cpu_sampler/target.inc new file mode 100644 index 000000000..15fbe920a --- /dev/null +++ b/repos/gems/src/server/cpu_sampler/target.inc @@ -0,0 +1,11 @@ +TARGET = cpu_sampler + +SRC_CC += main.cc \ + cpu_session_component.cc \ + cpu_thread_component.cc + +INC_DIR = $(REP_DIR)/src/server/cpu_sampler + +LIBS = base + +vpath %.cc $(REP_DIR)/src/server/cpu_sampler diff --git a/repos/gems/src/server/cpu_sampler/thread_list_change_handler.h b/repos/gems/src/server/cpu_sampler/thread_list_change_handler.h new file mode 100644 index 000000000..64aaeaf46 --- /dev/null +++ b/repos/gems/src/server/cpu_sampler/thread_list_change_handler.h @@ -0,0 +1,22 @@ +/* + * \brief Thread list change handler interface + * \author Christian Prochaska + * \date 2016-01-19 + */ + +/* + * Copyright (C) 2016 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 _THREAD_LIST_CHANGE_HANDLER_H_ +#define _THREAD_LIST_CHANGE_HANDLER_H_ + +struct Thread_list_change_handler +{ + virtual void thread_list_changed() = 0; +}; + +#endif /* _THREAD_LIST_CHANGE_HANDLER_H_ */ diff --git a/repos/gems/src/test/cpu_sampler/main.cc b/repos/gems/src/test/cpu_sampler/main.cc new file mode 100644 index 000000000..e0f1dfcdb --- /dev/null +++ b/repos/gems/src/test/cpu_sampler/main.cc @@ -0,0 +1,28 @@ +/* + * \brief Test for the CPU sampler component + * \author Christian Prochaska + * \date 2016-01-18 + */ + +/* + * Copyright (C) 2016 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. + */ + +#include + +void __attribute((noinline)) func() +{ + for (;;) { } +} + +int main(int argc, char *argv[]) +{ + Genode::log("Test started. func: ", func); + + func(); + + return 0; +} diff --git a/repos/gems/src/test/cpu_sampler/target.mk b/repos/gems/src/test/cpu_sampler/target.mk new file mode 100644 index 000000000..dca3336fd --- /dev/null +++ b/repos/gems/src/test/cpu_sampler/target.mk @@ -0,0 +1,3 @@ +TARGET = test-cpu_sampler +SRC_CC = main.cc +LIBS = base diff --git a/tool/autopilot.list b/tool/autopilot.list index 4f5309685..472044a5c 100644 --- a/tool/autopilot.list +++ b/tool/autopilot.list @@ -68,3 +68,4 @@ xml_node fpu ds_ownership fs_log +cpu_sampler