mirror of
https://github.com/genodelabs/genode.git
synced 2025-01-31 08:25:38 +00:00
parent
0a1839e3e2
commit
451c08ac01
@ -7,6 +7,7 @@ set build_components {
|
||||
drivers/timer
|
||||
test/trace
|
||||
lib/trace/policy/null
|
||||
app/top
|
||||
}
|
||||
|
||||
build $build_components
|
||||
@ -44,6 +45,10 @@ append config {
|
||||
<trace_policy label="init -> test-trace" module="null" />
|
||||
</config>
|
||||
</start>
|
||||
<start name="top">
|
||||
<resource name="RAM" quantum="2M"/>
|
||||
<config period_ms="2000"/>
|
||||
</start>
|
||||
</config>}
|
||||
|
||||
install_config $config
|
||||
@ -56,12 +61,13 @@ install_config $config
|
||||
set boot_modules {
|
||||
core ld.lib.so init
|
||||
timer
|
||||
top
|
||||
test-trace
|
||||
null
|
||||
}
|
||||
|
||||
build_boot_image $boot_modules
|
||||
|
||||
append qemu_args " -nographic -serial mon:stdio -m 256 "
|
||||
append qemu_args " -nographic -serial mon:stdio -m 256 -smp 2 "
|
||||
|
||||
run_genode_until {.*child "test-trace" exited with exit value 0.*} 30
|
||||
|
13
repos/os/src/app/top/README
Normal file
13
repos/os/src/app/top/README
Normal file
@ -0,0 +1,13 @@
|
||||
This component obtains the information about the existing trace subjects from
|
||||
core's "TRACE" service and shows via the LOG session the highest CPU consumer
|
||||
per CPU in percentage.
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
The interval of output generation can be defined via the 'period_ms' attribute
|
||||
of the '<config>' node. The value is specified in milliseconds.
|
||||
|
||||
The following example shows the default values.
|
||||
|
||||
! <config period_ms="5000"/>
|
281
repos/os/src/app/top/main.cc
Normal file
281
repos/os/src/app/top/main.cc
Normal file
@ -0,0 +1,281 @@
|
||||
/*
|
||||
* \brief Application to show highest CPU consumer per CPU via LOG session
|
||||
* \author Norman Feske
|
||||
* Alexander Boettcher
|
||||
* \date 2015-06-15
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015-2017 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.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <trace_session/connection.h>
|
||||
#include <timer_session/connection.h>
|
||||
#include <base/component.h>
|
||||
#include <base/attached_rom_dataspace.h>
|
||||
#include <base/heap.h>
|
||||
#include <os/reporter.h>
|
||||
|
||||
|
||||
struct Trace_subject_registry
|
||||
{
|
||||
private:
|
||||
|
||||
struct Entry : Genode::List<Entry>::Element
|
||||
{
|
||||
Genode::Trace::Subject_id const id;
|
||||
|
||||
Genode::Trace::Subject_info info;
|
||||
|
||||
/**
|
||||
* Execution time during the last period
|
||||
*/
|
||||
unsigned long long recent_execution_time = 0;
|
||||
|
||||
Entry(Genode::Trace::Subject_id id) : id(id) { }
|
||||
|
||||
void update(Genode::Trace::Subject_info const &new_info)
|
||||
{
|
||||
if (new_info.execution_time().value < info.execution_time().value)
|
||||
recent_execution_time = 0;
|
||||
else
|
||||
recent_execution_time = new_info.execution_time().value -
|
||||
info.execution_time().value;
|
||||
|
||||
info = new_info;
|
||||
}
|
||||
};
|
||||
|
||||
Genode::List<Entry> _entries;
|
||||
|
||||
Entry *_lookup(Genode::Trace::Subject_id const id)
|
||||
{
|
||||
for (Entry *e = _entries.first(); e; e = e->next())
|
||||
if (e->id == id)
|
||||
return e;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
enum { MAX_SUBJECTS = 512 };
|
||||
Genode::Trace::Subject_id _subjects[MAX_SUBJECTS];
|
||||
|
||||
enum { MAX_CPUS_X = 16, MAX_CPUS_Y = 1, MAX_ELEMENTS_PER_CPU = 6};
|
||||
|
||||
/* accumulated execution time on all CPUs */
|
||||
unsigned long long total [MAX_CPUS_X][MAX_CPUS_Y];
|
||||
|
||||
/* most significant consumer per CPU */
|
||||
Entry const * load[MAX_CPUS_X][MAX_CPUS_Y][MAX_ELEMENTS_PER_CPU];
|
||||
|
||||
public:
|
||||
|
||||
void update(Genode::Trace::Connection &trace, Genode::Allocator &alloc)
|
||||
{
|
||||
unsigned const num_subjects = trace.subjects(_subjects, MAX_SUBJECTS);
|
||||
|
||||
/* add and update existing entries */
|
||||
for (unsigned i = 0; i < num_subjects; i++) {
|
||||
|
||||
Genode::Trace::Subject_id const id = _subjects[i];
|
||||
|
||||
Entry *e = _lookup(id);
|
||||
if (!e) {
|
||||
e = new (alloc) Entry(id);
|
||||
_entries.insert(e);
|
||||
}
|
||||
|
||||
e->update(trace.subject_info(id));
|
||||
|
||||
/* purge dead threads */
|
||||
if (e->info.state() == Genode::Trace::Subject_info::DEAD) {
|
||||
trace.free(e->id);
|
||||
_entries.remove(e);
|
||||
Genode::destroy(alloc, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void top()
|
||||
{
|
||||
/* clear old calculations */
|
||||
Genode::memset(total, 0, sizeof(total));
|
||||
Genode::memset(load, 0, sizeof(load));
|
||||
|
||||
for (Entry const *e = _entries.first(); e; e = e->next()) {
|
||||
|
||||
/* collect highest execution time per CPU */
|
||||
unsigned const x = e->info.affinity().xpos();
|
||||
unsigned const y = e->info.affinity().ypos();
|
||||
if (x >= MAX_CPUS_X || y >= MAX_CPUS_Y) {
|
||||
Genode::error("cpu ", e->info.affinity().xpos(), ".",
|
||||
e->info.affinity().ypos(), " is outside "
|
||||
"supported range ",
|
||||
(int)MAX_CPUS_X, ".", (int)MAX_CPUS_Y);
|
||||
continue;
|
||||
}
|
||||
|
||||
total[x][y] += e->recent_execution_time;
|
||||
|
||||
enum { NONE = ~0U };
|
||||
unsigned replace = NONE;
|
||||
|
||||
for (unsigned i = 0; i < MAX_ELEMENTS_PER_CPU; i++) {
|
||||
if (load[x][y][i])
|
||||
continue;
|
||||
|
||||
replace = i;
|
||||
break;
|
||||
}
|
||||
|
||||
if (replace != NONE) {
|
||||
load[x][y][replace] = e;
|
||||
continue;
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < MAX_ELEMENTS_PER_CPU; i++) {
|
||||
if (e->recent_execution_time
|
||||
<= load[x][y][i]->recent_execution_time)
|
||||
continue;
|
||||
|
||||
if (replace == NONE) {
|
||||
replace = i;
|
||||
continue;
|
||||
}
|
||||
if (load[x][y][replace]->recent_execution_time
|
||||
<= load[x][y][i]->recent_execution_time)
|
||||
replace = i;
|
||||
}
|
||||
|
||||
if (replace != NONE)
|
||||
load[x][y][replace] = e;
|
||||
}
|
||||
|
||||
/* sort */
|
||||
for (unsigned x = 0; x < MAX_CPUS_X; x++) {
|
||||
for (unsigned y = 0; y < MAX_CPUS_Y; y++) {
|
||||
for (unsigned k = 0; k < MAX_ELEMENTS_PER_CPU;) {
|
||||
if (!load[x][y][k])
|
||||
break;
|
||||
|
||||
unsigned i = k;
|
||||
for (unsigned j = i; j < MAX_ELEMENTS_PER_CPU; j++) {
|
||||
if (!load[x][y][j])
|
||||
break;
|
||||
|
||||
if (load[x][y][i]->recent_execution_time
|
||||
< load[x][y][j]->recent_execution_time) {
|
||||
|
||||
Entry const * tmp = load[x][y][j];
|
||||
load[x][y][j] = load[x][y][i];
|
||||
load[x][y][i] = tmp;
|
||||
|
||||
i++;
|
||||
if (i >= MAX_ELEMENTS_PER_CPU || !load[x][y][i])
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == k)
|
||||
k++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned x = 0; x < MAX_CPUS_X; x++) {
|
||||
for (unsigned y = 0; y < MAX_CPUS_Y; y++) {
|
||||
for (unsigned i = 0; i < MAX_ELEMENTS_PER_CPU; i++) {
|
||||
if (!load[x][y][i] || !total[x][y])
|
||||
continue;
|
||||
|
||||
unsigned percent = load[x][y][i]->recent_execution_time * 100 / total[x][y];
|
||||
unsigned rest = load[x][y][i]->recent_execution_time * 10000 / total[x][y] - (percent * 100);
|
||||
|
||||
using Genode::log;
|
||||
log("cpu=", load[x][y][i]->info.affinity().xpos(), ".",
|
||||
load[x][y][i]->info.affinity().ypos(), " ",
|
||||
percent < 10 ? " " : (percent < 100 ? " " : ""),
|
||||
percent, ".", rest < 10 ? "0" : "", rest, "% "
|
||||
"thread='", load[x][y][i]->info.thread_name(), "' "
|
||||
"label='", load[x][y][i]->info.session_label(), "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (load[0][0][0] && load[0][0][0]->recent_execution_time)
|
||||
Genode::log("");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
namespace App {
|
||||
|
||||
struct Main;
|
||||
using namespace Genode;
|
||||
}
|
||||
|
||||
|
||||
struct App::Main
|
||||
{
|
||||
Env &_env;
|
||||
|
||||
Trace::Connection _trace { _env, 512*1024, 32*1024, 0 };
|
||||
|
||||
static unsigned long _default_period_ms() { return 5000; }
|
||||
|
||||
unsigned long _period_ms = _default_period_ms();
|
||||
|
||||
Attached_rom_dataspace _config { _env, "config" };
|
||||
|
||||
Timer::Connection _timer { _env };
|
||||
|
||||
Heap _heap { _env.ram(), _env.rm() };
|
||||
|
||||
Trace_subject_registry _trace_subject_registry;
|
||||
|
||||
void _handle_config();
|
||||
|
||||
Signal_handler<Main> _config_handler = {
|
||||
_env.ep(), *this, &Main::_handle_config};
|
||||
|
||||
void _handle_period();
|
||||
|
||||
Signal_handler<Main> _periodic_handler = {
|
||||
_env.ep(), *this, &Main::_handle_period};
|
||||
|
||||
Main(Env &env) : _env(env)
|
||||
{
|
||||
_config.sigh(_config_handler);
|
||||
_handle_config();
|
||||
|
||||
_timer.sigh(_periodic_handler);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void App::Main::_handle_config()
|
||||
{
|
||||
_config.update();
|
||||
|
||||
_period_ms = _config.xml().attribute_value("period_ms", _default_period_ms());
|
||||
|
||||
log("period_ms=", _period_ms);
|
||||
|
||||
_timer.trigger_periodic(1000*_period_ms);
|
||||
}
|
||||
|
||||
|
||||
void App::Main::_handle_period()
|
||||
{
|
||||
/* update subject information */
|
||||
_trace_subject_registry.update(_trace, _heap);
|
||||
|
||||
/* show most significant consumers */
|
||||
_trace_subject_registry.top();
|
||||
}
|
||||
|
||||
|
||||
void Component::construct(Genode::Env &env) { static App::Main main(env); }
|
||||
|
3
repos/os/src/app/top/target.mk
Normal file
3
repos/os/src/app/top/target.mk
Normal file
@ -0,0 +1,3 @@
|
||||
TARGET = top
|
||||
SRC_CC = main.cc
|
||||
LIBS += base
|
Loading…
x
Reference in New Issue
Block a user