2019-04-01 03:04:11 +02:00
|
|
|
// 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
|
2019-04-02 20:09:37 +02:00
|
|
|
#define HAVE_IRCD_PROF_H
|
2019-04-01 03:04:11 +02:00
|
|
|
|
2019-04-02 20:09:37 +02:00
|
|
|
namespace ircd::prof
|
2019-04-01 03:04:11 +02:00
|
|
|
{
|
|
|
|
struct init;
|
2019-04-03 22:16:05 +02:00
|
|
|
struct type;
|
|
|
|
struct event;
|
2019-04-06 01:56:51 +02:00
|
|
|
struct times;
|
2019-04-03 22:16:05 +02:00
|
|
|
struct system;
|
2019-04-08 00:34:39 +02:00
|
|
|
struct resource;
|
2019-04-10 23:49:39 +02:00
|
|
|
struct syscall_timer;
|
2019-04-03 22:16:05 +02:00
|
|
|
enum dpl :uint8_t;
|
|
|
|
enum counter :uint8_t;
|
|
|
|
enum cacheop :uint8_t;
|
|
|
|
using group = std::vector<std::unique_ptr<event>>;
|
2019-04-08 00:33:36 +02:00
|
|
|
IRCD_OVERLOAD(sample)
|
2019-04-01 03:04:11 +02:00
|
|
|
IRCD_EXCEPTION(ircd::error, error)
|
|
|
|
|
2019-04-06 01:56:51 +02:00
|
|
|
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.
|
2019-04-06 23:18:08 +02:00
|
|
|
uint64_t time_proc(); ///< Nanoseconds of CPU time for process.
|
|
|
|
uint64_t time_thrd(); ///< Nanoseconds of CPU time for thread.
|
2019-04-03 22:16:05 +02:00
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
2019-04-08 00:34:39 +02:00
|
|
|
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);
|
|
|
|
|
2019-04-03 22:16:05 +02:00
|
|
|
// Control
|
|
|
|
void stop(group &);
|
|
|
|
void start(group &);
|
|
|
|
void reset(group &);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// X86 platform related
|
|
|
|
namespace ircd::prof::x86
|
|
|
|
{
|
2019-04-01 03:04:11 +02:00
|
|
|
unsigned long long rdpmc(const uint &);
|
|
|
|
unsigned long long rdtscp();
|
|
|
|
unsigned long long rdtsc();
|
|
|
|
}
|
|
|
|
|
2019-04-19 14:47:30 +02:00
|
|
|
/// Callgrind hypercall suite
|
|
|
|
namespace ircd::prof::vg
|
|
|
|
{
|
|
|
|
struct enable;
|
|
|
|
struct disable;
|
|
|
|
|
2019-04-26 05:49:04 +02:00
|
|
|
bool enabled();
|
2019-04-19 14:47:30 +02:00
|
|
|
void dump(const char *const reason = nullptr);
|
|
|
|
void toggle();
|
|
|
|
void reset();
|
|
|
|
void start() noexcept;
|
|
|
|
void stop() noexcept;
|
|
|
|
}
|
|
|
|
|
2019-04-26 05:49:04 +02:00
|
|
|
// Exports to ircd::
|
2019-04-01 03:04:11 +02:00
|
|
|
namespace ircd
|
|
|
|
{
|
2019-04-02 20:09:37 +02:00
|
|
|
using prof::cycles;
|
2019-04-01 03:04:11 +02:00
|
|
|
}
|
|
|
|
|
2019-04-26 05:49:04 +02:00
|
|
|
/// Enable callgrind profiling for the scope
|
2019-04-19 14:47:30 +02:00
|
|
|
struct ircd::prof::vg::enable
|
|
|
|
{
|
2019-04-26 05:49:04 +02:00
|
|
|
enable() noexcept;
|
|
|
|
~enable() noexcept;
|
2019-04-19 14:47:30 +02:00
|
|
|
};
|
|
|
|
|
2019-04-26 05:49:04 +02:00
|
|
|
/// Disable any enabled callgrind profiling for the scope; then restore.
|
2019-04-19 14:47:30 +02:00
|
|
|
struct ircd::prof::vg::disable
|
|
|
|
{
|
2019-04-26 05:49:04 +02:00
|
|
|
disable() noexcept;
|
|
|
|
~disable() noexcept;
|
2019-04-19 14:47:30 +02:00
|
|
|
};
|
|
|
|
|
2019-04-26 05:49:04 +02:00
|
|
|
/// This suite of devices is intended 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
|
|
|
|
/// though reasonably fast, and the result has poor resolution meaning
|
|
|
|
/// the result of at() is generally 0 unless the system call was very slow.
|
|
|
|
///
|
|
|
|
/// It is started on construction. The user must later call sample()
|
|
|
|
/// which returns the value of at() as well.
|
|
|
|
struct ircd::prof::syscall_timer
|
|
|
|
{
|
2019-04-26 06:09:50 +02:00
|
|
|
struct high_resolution;
|
|
|
|
|
2019-04-26 05:49:04 +02:00
|
|
|
uint64_t started, stopped;
|
|
|
|
|
|
|
|
public:
|
|
|
|
uint64_t at() const;
|
|
|
|
uint64_t sample();
|
|
|
|
|
|
|
|
syscall_timer() noexcept;
|
|
|
|
};
|
|
|
|
|
2019-04-26 06:09:50 +02:00
|
|
|
/// This is a higher resolution alternative. The sample may be conducted
|
|
|
|
/// with getrusage() or perf events; the exact method is TBD and may be
|
|
|
|
/// expensive/intrusive. This device should be used temporarily by developers
|
|
|
|
/// and not left in place in committed code.
|
|
|
|
struct ircd::prof::syscall_timer::high_resolution
|
|
|
|
{
|
|
|
|
uint64_t started, stopped;
|
|
|
|
|
|
|
|
public:
|
|
|
|
uint64_t at() const;
|
|
|
|
uint64_t sample();
|
|
|
|
|
|
|
|
high_resolution() noexcept;
|
|
|
|
};
|
|
|
|
|
2019-04-26 05:49:04 +02:00
|
|
|
/// Frontend to times(2). This has low resolution in practice, but it's
|
|
|
|
/// very cheap as far as syscalls go; x-platform implementation courtesy
|
|
|
|
/// of boost::chrono.
|
2019-04-06 01:56:51 +02:00
|
|
|
struct ircd::prof::times
|
|
|
|
{
|
|
|
|
uint64_t real {0};
|
|
|
|
uint64_t kern {0};
|
|
|
|
uint64_t user {0};
|
|
|
|
|
|
|
|
times(sample_t);
|
|
|
|
times() = default;
|
|
|
|
};
|
|
|
|
|
2019-04-26 05:49:04 +02:00
|
|
|
/// Frontend to getrusage(2). This has higher resolution than prof::times
|
|
|
|
/// in practice with slight added expense.
|
2019-04-08 00:34:39 +02:00
|
|
|
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}}
|
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
2019-04-26 05:49:04 +02:00
|
|
|
/// Frontend to perf_event_open(2). This has the highest resolution.
|
2019-04-03 22:16:05 +02:00
|
|
|
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,
|
|
|
|
|
2019-04-08 00:33:36 +02:00
|
|
|
system(sample_t) noexcept;
|
2019-04-03 22:16:05 +02:00
|
|
|
system()
|
|
|
|
:array_type{{0}}
|
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
2019-04-26 05:49:04 +02:00
|
|
|
/// Type descriptor for prof events. This structure is used to aggregate
|
|
|
|
/// information that describes a profiling event type, including whether
|
|
|
|
/// the kernel or the user is being profiled (dpl), the principal counter
|
|
|
|
/// type being profiled (counter) and any other contextual attributes.
|
2019-04-03 22:16:05 +02:00
|
|
|
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
|
|
|
|
{
|
2019-04-26 05:49:04 +02:00
|
|
|
KERNEL = 0,
|
|
|
|
USER = 1,
|
2019-04-03 22:16:05 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
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,
|
|
|
|
};
|
|
|
|
|
2019-04-02 20:09:37 +02:00
|
|
|
struct ircd::prof::init
|
2019-04-01 03:04:11 +02:00
|
|
|
{
|
|
|
|
init();
|
|
|
|
~init() noexcept;
|
|
|
|
};
|
|
|
|
|
2019-04-03 22:16:05 +02:00
|
|
|
#if defined(__x86_64__) || defined(__i386__)
|
2019-04-01 03:04:11 +02:00
|
|
|
inline uint64_t
|
|
|
|
__attribute__((flatten, always_inline, gnu_inline, artificial))
|
2019-04-02 20:09:37 +02:00
|
|
|
ircd::prof::cycles()
|
2019-04-01 03:04:11 +02:00
|
|
|
{
|
2019-04-03 22:16:05 +02:00
|
|
|
return x86::rdtsc();
|
2019-04-01 03:04:11 +02:00
|
|
|
}
|
2019-04-03 22:16:05 +02:00
|
|
|
#else
|
|
|
|
ircd::prof::cycles()
|
|
|
|
{
|
|
|
|
static_assert(false, "Select reference cycle counter for platform.");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
2019-04-01 03:04:11 +02:00
|
|
|
|
|
|
|
#if defined(__x86_64__) || defined(__i386__)
|
|
|
|
inline unsigned long long
|
|
|
|
__attribute__((always_inline, gnu_inline, artificial))
|
2019-04-03 22:16:05 +02:00
|
|
|
ircd::prof::x86::rdtsc()
|
2019-04-01 03:04:11 +02:00
|
|
|
{
|
|
|
|
return __builtin_ia32_rdtsc();
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
inline unsigned long long
|
2019-04-03 22:16:05 +02:00
|
|
|
ircd::prof::x86::rdtsc()
|
2019-04-01 03:04:11 +02:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(__x86_64__) || defined(__i386__)
|
|
|
|
inline unsigned long long
|
|
|
|
__attribute__((always_inline, gnu_inline, artificial))
|
2019-04-03 22:16:05 +02:00
|
|
|
ircd::prof::x86::rdtscp()
|
2019-04-01 03:04:11 +02:00
|
|
|
{
|
|
|
|
uint32_t ia32_tsc_aux;
|
|
|
|
return __builtin_ia32_rdtscp(&ia32_tsc_aux);
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
inline unsigned long long
|
2019-04-03 22:16:05 +02:00
|
|
|
ircd::prof::x86::rdtscp()
|
2019-04-01 03:04:11 +02:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(__x86_64__) || defined(__i386__)
|
|
|
|
inline unsigned long long
|
|
|
|
__attribute__((always_inline, gnu_inline, artificial))
|
2019-04-03 22:16:05 +02:00
|
|
|
ircd::prof::x86::rdpmc(const uint &c)
|
2019-04-01 03:04:11 +02:00
|
|
|
{
|
|
|
|
return __builtin_ia32_rdpmc(c);
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
inline unsigned long long
|
2019-04-03 22:16:05 +02:00
|
|
|
ircd::prof::x86::rdpmc(const uint &c)
|
2019-04-01 03:04:11 +02:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|