mirror of
https://github.com/plasma-umass/coz.git
synced 2024-10-26 07:39:17 +03:00
0401446561
In that case use the syscall interface directly https://stackoverflow.com/questions/30680550/c-gettid-was-not-declared-in-this-scope
241 lines
6.7 KiB
C++
241 lines
6.7 KiB
C++
/*
|
|
* Copyright (c) 2015, Charlie Curtsinger and Emery Berger,
|
|
* University of Massachusetts Amherst
|
|
* This file is part of the Coz project. See LICENSE.md file at the top-level
|
|
* directory of this distribution and at http://github.com/plasma-umass/coz.
|
|
*/
|
|
|
|
#if !defined(CAUSAL_RUNTIME_PERF_H)
|
|
#define CAUSAL_RUNTIME_PERF_H
|
|
|
|
#include <linux/perf_event.h>
|
|
#include <sys/types.h>
|
|
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <functional>
|
|
|
|
#include "ccutil/log.h"
|
|
#include "ccutil/wrapped_array.h"
|
|
|
|
#if __GLIBC__ == 2 && __GLIBC_MINOR__ < 30
|
|
#include <sys/syscall.h>
|
|
#define gettid() syscall(SYS_gettid)
|
|
#endif
|
|
|
|
// Workaround for missing hw_breakpoint.h include file:
|
|
// This include file just defines constants used to configure watchpoint registers.
|
|
// This will be constant across x86 systems.
|
|
enum { HW_BREAKPOINT_X = 4};
|
|
|
|
class perf_event {
|
|
public:
|
|
enum class record_type;
|
|
class record;
|
|
class sample_record;
|
|
|
|
/// Default constructor
|
|
perf_event();
|
|
/// Open a perf_event file using the given options structure
|
|
perf_event(struct perf_event_attr& pe, pid_t pid = 0, int cpu = -1);
|
|
/// Move constructor
|
|
perf_event(perf_event&& other);
|
|
|
|
/// Close the perf event file and unmap the ring buffer
|
|
~perf_event();
|
|
|
|
/// Move assignment is supported
|
|
void operator=(perf_event&& other);
|
|
|
|
/// Read event count
|
|
uint64_t get_count() const;
|
|
|
|
/// Start counting events and collecting samples
|
|
void start();
|
|
|
|
/// Stop counting events
|
|
void stop();
|
|
|
|
/// Close the perf_event file and unmap the ring buffer
|
|
void close();
|
|
|
|
/// Configure the perf_event file to deliver a signal when samples are ready to be processed
|
|
void set_ready_signal(int sig);
|
|
|
|
/// An enum class with all the available sampling data
|
|
enum class sample : uint64_t {
|
|
ip = PERF_SAMPLE_IP,
|
|
pid_tid = PERF_SAMPLE_TID,
|
|
time = PERF_SAMPLE_TIME,
|
|
addr = PERF_SAMPLE_ADDR,
|
|
id = PERF_SAMPLE_ID,
|
|
stream_id = PERF_SAMPLE_STREAM_ID,
|
|
cpu = PERF_SAMPLE_CPU,
|
|
period = PERF_SAMPLE_PERIOD,
|
|
|
|
#if defined(PREF_SAMPLE_READ)
|
|
read = PERF_SAMPLE_READ,
|
|
#else
|
|
read = 0,
|
|
#endif
|
|
|
|
callchain = PERF_SAMPLE_CALLCHAIN,
|
|
raw = PERF_SAMPLE_RAW,
|
|
|
|
#if defined(PERF_SAMPLE_BRANCH_STACK)
|
|
branch_stack = PERF_SAMPLE_BRANCH_STACK,
|
|
#else
|
|
branch_stack = 0,
|
|
#endif
|
|
|
|
#if defined(PERF_SAMPLE_REGS_USER)
|
|
regs = PERF_SAMPLE_REGS_USER,
|
|
#else
|
|
regs = 0,
|
|
#endif
|
|
|
|
#if defined(PERF_SAMPLE_STACK_USER)
|
|
stack = PERF_SAMPLE_STACK_USER,
|
|
#else
|
|
stack = 0,
|
|
#endif
|
|
|
|
_end = PERF_SAMPLE_MAX
|
|
};
|
|
|
|
/// Check if this perf_event was configured to collect a type of sample data
|
|
inline bool is_sampling(sample s) const {
|
|
return _sample_type & static_cast<uint64_t>(s);
|
|
}
|
|
|
|
/// Get the configuration for this perf_event's read format
|
|
inline uint64_t get_read_format() const {
|
|
return _read_format;
|
|
}
|
|
|
|
/// An enum to distinguish types of records in the mmapped ring buffer
|
|
enum class record_type {
|
|
mmap = PERF_RECORD_MMAP,
|
|
lost = PERF_RECORD_LOST,
|
|
comm = PERF_RECORD_COMM,
|
|
exit = PERF_RECORD_EXIT,
|
|
throttle = PERF_RECORD_THROTTLE,
|
|
unthrottle = PERF_RECORD_UNTHROTTLE,
|
|
fork = PERF_RECORD_FORK,
|
|
read = PERF_RECORD_READ,
|
|
sample = PERF_RECORD_SAMPLE,
|
|
|
|
#if defined(PERF_RECORD_MMAP2)
|
|
mmap2 = PERF_RECORD_MMAP2
|
|
#else
|
|
mmap2 = 0
|
|
#endif
|
|
};
|
|
|
|
class iterator;
|
|
|
|
/// A generic record type
|
|
struct record {
|
|
friend class perf_event::iterator;
|
|
public:
|
|
record_type get_type() const { return static_cast<record_type>(_header->type); }
|
|
|
|
inline bool is_mmap() const { return get_type() == record_type::mmap; }
|
|
inline bool is_lost() const { return get_type() == record_type::lost; }
|
|
inline bool is_comm() const { return get_type() == record_type::comm; }
|
|
inline bool is_exit() const { return get_type() == record_type::exit; }
|
|
inline bool is_throttle() const { return get_type() == record_type::throttle; }
|
|
inline bool is_unthrottle() const { return get_type() == record_type::unthrottle; }
|
|
inline bool is_fork() const { return get_type() == record_type::fork; }
|
|
inline bool is_read() const { return get_type() == record_type::read; }
|
|
inline bool is_sample() const { return get_type() == record_type::sample; }
|
|
inline bool is_mmap2() const { return get_type() == record_type::mmap2; }
|
|
|
|
uint64_t get_ip() const;
|
|
uint64_t get_pid() const;
|
|
uint64_t get_tid() const;
|
|
uint64_t get_time() const;
|
|
uint32_t get_cpu() const;
|
|
ccutil::wrapped_array<uint64_t> get_callchain() const;
|
|
|
|
private:
|
|
record(const perf_event& source, struct perf_event_header* header) :
|
|
_source(source), _header(header) {}
|
|
|
|
template<perf_event::sample s, typename T=void*> T locate_field() const;
|
|
|
|
const perf_event& _source;
|
|
struct perf_event_header* _header;
|
|
};
|
|
|
|
class iterator {
|
|
public:
|
|
iterator(perf_event& source, struct perf_event_mmap_page* mapping) :
|
|
_source(source), _mapping(mapping) {
|
|
if(mapping != nullptr) {
|
|
_index = mapping->data_tail;
|
|
_head = mapping->data_head;
|
|
//__atomic_thread_fence(__ATOMIC_SEQ_CST); // Not required for thread-local perf events
|
|
} else {
|
|
_index = 0;
|
|
_head = 0;
|
|
}
|
|
}
|
|
|
|
~iterator() {
|
|
if(_mapping != nullptr) {
|
|
_mapping->data_tail = _index;
|
|
}
|
|
}
|
|
|
|
void next();
|
|
record get();
|
|
bool has_data() const;
|
|
|
|
iterator& operator++() { next(); return *this; }
|
|
record operator*() { return get(); }
|
|
bool operator!=(const iterator& other) { return has_data() != other.has_data(); }
|
|
|
|
private:
|
|
perf_event& _source;
|
|
size_t _index;
|
|
size_t _head;
|
|
struct perf_event_mmap_page* _mapping;
|
|
|
|
// Buffer to hold the current record. Just a hack until records play nice with the ring buffer
|
|
uint8_t _buf[4096];
|
|
};
|
|
|
|
/// Get an iterator to the beginning of the memory mapped ring buffer
|
|
iterator begin() {
|
|
return iterator(*this, _mapping);
|
|
}
|
|
|
|
// Get an iterator to the end of the memory mapped ring buffer
|
|
iterator end() {
|
|
return iterator(*this, nullptr);
|
|
}
|
|
|
|
private:
|
|
// Disallow copy and assignment
|
|
perf_event(const perf_event&) = delete;
|
|
void operator=(const perf_event&) = delete;
|
|
|
|
// Copy data out of the mmap ring buffer
|
|
static void copy_from_ring_buffer(struct perf_event_mmap_page* mapping,
|
|
ptrdiff_t index, void* dest, size_t bytes);
|
|
|
|
/// File descriptor for the perf event
|
|
long _fd = -1;
|
|
|
|
/// Memory mapped perf event region
|
|
struct perf_event_mmap_page* _mapping = nullptr;
|
|
|
|
/// The sample type from this perf_event's configuration
|
|
uint64_t _sample_type = 0;
|
|
/// The read format from this perf event's configuration
|
|
uint64_t _read_format = 0;
|
|
};
|
|
|
|
#endif
|