0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-11-29 18:22:50 +01:00
construct/include/ircd/prof.h
2019-04-19 05:53:54 -07:00

303 lines
5.7 KiB
C++

// Matrix Construct
//
// Copyright (C) Matrix Construct Developers, Authors & Contributors
// Copyright (C) 2016-2019 Jason Volk <jason@zemos.net>
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice is present in all copies. The
// full license for this software is available in the LICENSE file.
#pragma once
#define HAVE_IRCD_PROF_H
namespace ircd::prof
{
struct init;
struct type;
struct event;
struct times;
struct system;
struct resource;
struct syscall_timer;
enum dpl :uint8_t;
enum counter :uint8_t;
enum cacheop :uint8_t;
using group = std::vector<std::unique_ptr<event>>;
IRCD_OVERLOAD(sample)
IRCD_EXCEPTION(ircd::error, error)
uint64_t cycles(); ///< Monotonic reference cycles (since system boot)
uint64_t time_user(); ///< Nanoseconds of CPU time in userspace.
uint64_t time_kern(); ///< Nanoseconds of CPU time in kernelland.
uint64_t time_real(); ///< Nanoseconds of CPU time real.
uint64_t time_proc(); ///< Nanoseconds of CPU time for process.
uint64_t time_thrd(); ///< Nanoseconds of CPU time for thread.
// Observe
system &hotsample(system &) noexcept;
system &operator+=(system &a, const system &b);
system &operator-=(system &a, const system &b);
system operator+(const system &a, const system &b);
system operator-(const system &a, const system &b);
resource &operator+=(resource &a, const resource &b);
resource &operator-=(resource &a, const resource &b);
resource operator+(const resource &a, const resource &b);
resource operator-(const resource &a, const resource &b);
// Control
void stop(group &);
void start(group &);
void reset(group &);
}
/// X86 platform related
namespace ircd::prof::x86
{
unsigned long long rdpmc(const uint &);
unsigned long long rdtscp();
unsigned long long rdtsc();
}
/// Callgrind hypercall suite
namespace ircd::prof::vg
{
struct enable;
struct disable;
void dump(const char *const reason = nullptr);
void toggle();
void reset();
void start() noexcept;
void stop() noexcept;
}
namespace ircd
{
using prof::cycles;
}
struct ircd::prof::vg::enable
{
enable() noexcept { start(); }
~enable() noexcept { stop(); }
};
struct ircd::prof::vg::disable
{
disable() noexcept { stop(); }
~disable() noexcept { start(); }
};
struct ircd::prof::times
{
uint64_t real {0};
uint64_t kern {0};
uint64_t user {0};
times(sample_t);
times() = default;
};
/// This device intends to figure out when a system call is really slow
/// or "blocking." The original use-case is for io_submit() in fs::aio.
/// The sample is conducted with times(2) which is itself a system call,
/// and the result has poor resolution meaning the result of at() is
/// generally 0 unless the system call was actually slow.
struct ircd::prof::syscall_timer
{
uint64_t started
{
time_kern()
};
uint64_t stopped
{
0
};
uint64_t at() const
{
return stopped - started;
}
uint64_t stop()
{
stopped = time_kern();
return at();
}
};
struct ircd::prof::resource
:std::array<uint64_t, 9>
{
enum
{
TIME_USER, // microseconds
TIME_KERN, // microseconds
RSS_MAX,
PF_MINOR,
PF_MAJOR,
BLOCK_IN,
BLOCK_OUT,
SCHED_YIELD,
SCHED_PREEMPT,
};
resource(sample_t);
resource()
:std::array<uint64_t, 9>{{0}}
{}
};
struct ircd::prof::system
:std::array<std::array<uint64_t, 2>, 7>
{
using array_type = std::array<std::array<uint64_t, 2>, 7>;
static prof::group group;
// [N][0] = KERNEL, [N][1] = USER
//
// 0: TIME_PROF,
// 1: TIME_CPU,
// 2: TIME_TASK,
// 3: PF_MINOR,
// 4: PF_MAJOR,
// 5: SWITCH_TASK,
// 6: SWITCH_CPU,
system(sample_t) noexcept;
system()
:array_type{{0}}
{}
};
struct ircd::prof::type
{
enum dpl dpl {0};
enum counter counter {0};
enum cacheop cacheop {0};
type(const event &);
type(const enum dpl & = (enum dpl)0,
const enum counter & = (enum counter)0,
const enum cacheop & = (enum cacheop)0);
};
enum ircd::prof::dpl
:std::underlying_type<ircd::prof::dpl>::type
{
KERNEL,
USER
};
enum ircd::prof::counter
:std::underlying_type<ircd::prof::counter>::type
{
TIME_PROF,
TIME_CPU,
TIME_TASK,
PF_MINOR,
PF_MAJOR,
SWITCH_TASK,
SWITCH_CPU,
CYCLES,
RETIRES,
BRANCHES,
BRANCHES_MISS,
CACHES,
CACHES_MISS,
STALLS_READ,
STALLS_RETIRE,
CACHE_L1D,
CACHE_L1I,
CACHE_LL,
CACHE_TLBD,
CACHE_TLBI,
CACHE_BPU,
CACHE_NODE,
_NUM
};
enum ircd::prof::cacheop
:std::underlying_type<ircd::prof::cacheop>::type
{
READ_ACCESS,
READ_MISS,
WRITE_ACCESS,
WRITE_MISS,
PREFETCH_ACCESS,
PREFETCH_MISS,
};
struct ircd::prof::init
{
init();
~init() noexcept;
};
#if defined(__x86_64__) || defined(__i386__)
inline uint64_t
__attribute__((flatten, always_inline, gnu_inline, artificial))
ircd::prof::cycles()
{
return x86::rdtsc();
}
#else
ircd::prof::cycles()
{
static_assert(false, "Select reference cycle counter for platform.");
return 0;
}
#endif
#if defined(__x86_64__) || defined(__i386__)
inline unsigned long long
__attribute__((always_inline, gnu_inline, artificial))
ircd::prof::x86::rdtsc()
{
return __builtin_ia32_rdtsc();
}
#else
inline unsigned long long
ircd::prof::x86::rdtsc()
{
return 0;
}
#endif
#if defined(__x86_64__) || defined(__i386__)
inline unsigned long long
__attribute__((always_inline, gnu_inline, artificial))
ircd::prof::x86::rdtscp()
{
uint32_t ia32_tsc_aux;
return __builtin_ia32_rdtscp(&ia32_tsc_aux);
}
#else
inline unsigned long long
ircd::prof::x86::rdtscp()
{
return 0;
}
#endif
#if defined(__x86_64__) || defined(__i386__)
inline unsigned long long
__attribute__((always_inline, gnu_inline, artificial))
ircd::prof::x86::rdpmc(const uint &c)
{
return __builtin_ia32_rdpmc(c);
}
#else
inline unsigned long long
ircd::prof::x86::rdpmc(const uint &c)
{
return 0;
}
#endif