Added simple example for using the performance counter syscalls in EalánOS.

This commit is contained in:
Michael Mueller
2023-03-27 17:22:00 +02:00
parent 561a2ddbf9
commit 412cf94f59
4 changed files with 279 additions and 0 deletions

View File

@@ -0,0 +1,80 @@
set build_components {
core init timer app/hpc_test
}
source ${genode_dir}/repos/base/run/platform_drv.inc
append_platform_drv_build_components
build $build_components
create_boot_directory
set config {
<config>
<parent-provides>
<service name="LOG"/>
<service name="PD"/>
<service name="CPU"/>
<service name="ROM"/>
<service name="RAM"/>
<service name="IRQ"/>
<service name="IO_MEM"/>
<service name="IO_PORT"/>
<service name="CAP"/>
<service name="RM"/>
<service name="SIGNAL"/>
<service name="TOPO"/>
</parent-provides>
<default-route>
<any-service><parent/><any-child/></any-service>
</default-route>
<default caps="200"/>
<start name="timer">
<resource name="RAM" quantum="16M"/>
<provides><service name="Timer"/></provides>
<route>
<any-service><parent/><any-child/></any-service>
</route>
</start>
}
append config {
<start name="hpc_test1">
<binary name="hpc_test"/>
<resource name="RAM" quantum="64M"/>
<config>
<vfs> <dir name="dev"> <log/> <inline name="rtc">2022-07-20 14:30</inline> </dir> </vfs>
<libc stdout="/dev/log" stderr="/dev/log" rtc="/dev/rtc"/>
</config>
<route>
<service name="Timer"><child name="timer"/></service>
<any-service><parent/><any-child/></any-service>
</route>
</start>
<start name="hpc_test2">
<binary name="hpc_test"/>
<resource name="RAM" quantum="64M"/>
<config>
<vfs> <dir name="dev"> <log/> <inline name="rtc">2022-07-20 14:30</inline> </dir> </vfs>
<libc stdout="/dev/log" stderr="/dev/log" rtc="/dev/rtc"/>
</config>
<route>
<service name="Timer"><child name="timer"/></service>
<any-service><parent/><any-child/></any-service>
</route>
</start>
</config>
}
install_config $config
set boot_modules {
core init timer vfs.lib.so ld.lib.so posix.lib.so libc.lib.so libm.lib.so stdcxx.lib.so hpc_test
}
append_platform_drv_boot_modules
build_boot_image $boot_modules
append qemu_args "-nographic "
run_genode_until forever

View File

@@ -0,0 +1,89 @@
/**
* @file main.cc
* @author Michael Müller (michael.mueller@uos.de)
* @brief Some test for programing hardware performance counters in NOVA
* @version 0.1
* @date 2022-12-14
*
* @copyright Copyright (c) 2022
*
*/
#include <nova/syscall-generic.h>
#include <nova/syscalls.h>
#include <iostream>
#include <chrono>
#include <thread>
#include <x86intrin.h>
int main(void)
{
Nova::mword_t event = 0x26;
Nova::mword_t mask = 0x00;
Nova::mword_t flags = 0x70000;
Nova::uint8_t rc;
if ((rc = Nova::hpc_ctrl(Nova::HPC_SETUP, 0, 1, event, mask, flags)) != Nova::NOVA_OK) {
std::cerr << "Failed to setup performance counter 0" << std::endl;
return -1;
}
std::cout << "Counter 0 setup" << std::endl;
event = 0x60;
mask = 0xfe;
if ((rc = Nova::hpc_ctrl(Nova::HPC_SETUP, 1, 1, event, mask, flags)) != Nova::NOVA_OK)
{
std::cerr << "Failed to setup performance counter 1, rc = " << static_cast<Nova::uint32_t>(rc) << std::endl;
return -1;
}
event = 0x62;
mask = 0x1;
if ((rc = Nova::hpc_ctrl(Nova::HPC_SETUP, 2, 1, event, mask, flags)) != Nova::NOVA_OK)
{
std::cerr << "Failed to setup performance counter 2, rc = " << static_cast<Nova::uint32_t>(rc) << std::endl;
return -1;
}
if ((rc = Nova::hpc_start(0, 1)) != Nova::NOVA_OK) {
std::cerr << "Failed to start counter 0" << std::endl;
return -2;
}
if ((rc = Nova::hpc_start(1, 1)) != Nova::NOVA_OK) {
std::cerr << "Failed to start counter 0" << std::endl;
return -2;
}
if ((rc = Nova::hpc_start(2, 1)) != Nova::NOVA_OK) {
std::cerr << "Failed to start counter 0" << std::endl;
return -2;
}
for (;;) {
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
Nova::mword_t count = 0;
_mm_clflush(&count);
if ((rc = Nova::hpc_read(0, 1, count)) != Nova::NOVA_OK)
{
std::cerr << "Failed to read counter 0" << std::endl;
}
std::cout << count << " cache line flushes" << std::endl;
Nova::mword_t latency = 0;
if ((rc = Nova::hpc_read(2, 1, latency)) != Nova::NOVA_OK)
{
std::cerr << "Failed to read counter 1" << std::endl;
}
Nova::mword_t l2_requests = 0;
if ((rc = Nova::hpc_read(1, 1, l2_requests)) != Nova::NOVA_OK)
{
std::cerr << "Failed to read counter 1" << std::endl;
}
count = (latency * 4) / l2_requests;
std::cout << "L2 latency:" << count << " cycles" << std::endl;
}
return 0;
}

View File

@@ -0,0 +1,5 @@
TARGET = hpc_test
SRC_CC = trace_pfc.cc
LIBS += base posix libm libc stdcxx
CC_OPT += -Wno-error -Wno-permissive -fpermissive -Wno-error=conversion

View File

@@ -0,0 +1,105 @@
/**
* @file trace_pfc.cc
* @author Michael Müller (michael.mueller@uos.de)
* @brief Tests for Genode wrappers around Performance counter syscalls in NOVA
* @version 0.1
* @date 2022-12-15
*
* @copyright Copyright (c) 2022
*
*/
#include <base/trace/perf.h>
#include <iostream>
#include <chrono>
#include <thread>
#include <x86intrin.h>
using namespace Genode;
int main(void)
{
Trace::Performance_counter::Counter ctr_clflush, ctr_l2_latency, ctr_l2_requests, /*ctr_l3_miss,*/ ctr_l2_prefetch;
try {
ctr_clflush = Trace::Performance_counter::alloc_core();
ctr_l2_latency = Trace::Performance_counter::alloc_core();
ctr_l2_requests = Trace::Performance_counter::alloc_core();
ctr_l2_prefetch = Trace::Performance_counter::acquire(Trace::Performance_counter::Type::CORE);
// ctr_l3_miss = Trace::Performance_counter::alloc_cbo();
}
catch (Trace::Pfc_no_avail)
{
std::cout << "Unable to allocate performance counters." << std::endl;
return -1;
}
std::cout << "Performance counter allocation successful." << std::endl;
try {
Trace::Performance_counter::setup(ctr_clflush, 0x26, 0x00, 0x70000);
Trace::Performance_counter::setup(ctr_l2_latency, 0x62, 0x01, 0x30000);
Trace::Performance_counter::setup(ctr_l2_requests, 0x60, 0xfe, 0x30000);
Trace::Performance_counter::setup(ctr_l2_prefetch, 0xc0, 0x00, 0x30000);
//Trace::Performance_counter::setup(ctr_l3_miss, 0x6, 0xff, 0x550f000000000000);
} catch (Trace::Pfc_access_error &e) {
std::cerr << "PFC access failed. rc=" << e.error_code() << std::endl;
return -1;
}
std::cout << "Performance counters successfully set up." << std::endl;
try {
Trace::Performance_counter::start(ctr_clflush);
Trace::Performance_counter::start(ctr_l2_latency);
Trace::Performance_counter::start(ctr_l2_requests);
Trace::Performance_counter::start(ctr_l2_prefetch);
//Trace::Performance_counter::start(ctr_l3_miss);
} catch (Trace::Pfc_access_error &e) {
std::cerr << "PFC access failed. rc=" << e.error_code() << std::endl;
return -1;
}
std::cout << "Performance counters started." << std::endl;
for (;;) {
Genode::uint64_t clflushes, latency, requests, /*l3_misses,*/ l2_prefetches;
clflushes = latency = requests = l2_prefetches = 0;
std::this_thread::sleep_for(std::chrono::seconds(2));
_mm_clflush(&clflushes);
_mm_clflush(&clflushes);
try {
clflushes = Trace::Performance_counter::read(ctr_clflush);
latency = Trace::Performance_counter::read(ctr_l2_latency);
requests = Trace::Performance_counter::read(ctr_l2_requests);
l2_prefetches = Trace::Performance_counter::read(ctr_l2_prefetch);
//l3_misses = Trace::Performance_counter::read(ctr_l3_miss);
} catch (Trace::Pfc_access_error &e) {
std::cerr << "PFC access failed. rc=" << e.error_code() << std::endl;
return 1;
}
std::cout << clflushes << " cache line flushes." << std::endl;
//std::cout << "L2 latency: " << (latency * 4) / requests << " cycles." << std::endl;
std::cout << l2_prefetches << " L2 prefetch requests." << std::endl;
/*
try {
Trace::Performance_counter::stop(ctr_l2_prefetch);
Trace::Performance_counter::reset(ctr_l2_prefetch, 0xdeadbeef);
Trace::Performance_counter::start(ctr_l2_prefetch);
std::cout << Trace::Performance_counter::read(ctr_l2_prefetch) << " L2 prefetches after context-switch" << std::endl;
Trace::Performance_counter::stop(ctr_l2_prefetch);
Trace::Performance_counter::reset(ctr_l2_prefetch, l2_prefetches);
Trace::Performance_counter::start(ctr_l2_prefetch);
} catch (Trace::Pfc_access_error &e) {
std::cerr << "PFC access failed. rc=" << e.error_code() << std::endl;
}
*/
// std::cout << l3_misses << " L3 misses" << std::endl;
}
return 0;
}