-
Notifications
You must be signed in to change notification settings - Fork 588
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Create
PerfCounterBuffers
to contain the logic to extract packets f…
…rom perf event buffers
- Loading branch information
1 parent
bdde224
commit cfad2e3
Showing
5 changed files
with
211 additions
and
68 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
/* -*- Mode: C++; tab-width: 8; c-basic-offset: 2; indent-tabs-mode: nil; -*- */ | ||
|
||
#include "PerfCounterBuffers.h" | ||
|
||
#include <sys/mman.h> | ||
|
||
#include "util.h" | ||
#include "log.h" | ||
|
||
using namespace std; | ||
|
||
namespace rr { | ||
|
||
void PerfCounterBuffers::destroy() { | ||
if (mmap_aux_buffer) { | ||
munmap(mmap_aux_buffer, mmap_header->aux_size); | ||
mmap_aux_buffer = nullptr; | ||
} | ||
if (mmap_header) { | ||
munmap(mmap_header, page_size() + buffer_size); | ||
mmap_header = nullptr; | ||
} | ||
} | ||
|
||
void PerfCounterBuffers::allocate(ScopedFd& perf_event_fd, | ||
uint64_t buffer_size, uint64_t aux_size) { | ||
this->buffer_size = buffer_size; | ||
|
||
void* base = mmap(NULL, page_size() + buffer_size, | ||
PROT_READ | PROT_WRITE, MAP_SHARED, perf_event_fd, 0); | ||
if (base == MAP_FAILED) { | ||
FATAL() << "Can't allocate memory for PT DATA area"; | ||
} | ||
mmap_header = static_cast<struct perf_event_mmap_page*>(base); | ||
|
||
if (aux_size > 0) { | ||
mmap_header->aux_offset = mmap_header->data_offset + mmap_header->data_size; | ||
mmap_header->aux_size = aux_size; | ||
|
||
void* aux = mmap(NULL, mmap_header->aux_size, PROT_READ | PROT_WRITE, MAP_SHARED, | ||
perf_event_fd, mmap_header->aux_offset); | ||
if (aux == MAP_FAILED) { | ||
FATAL() << "Can't allocate memory for PT AUX area"; | ||
} | ||
mmap_aux_buffer = static_cast<char*>(aux); | ||
} | ||
} | ||
|
||
optional<PerfCounterBuffers::Packet> PerfCounterBuffers::next_packet() { | ||
if (packet_in_use) { | ||
FATAL() << "Can't offer more than one packet at a time"; | ||
} | ||
|
||
uint64_t data_end = *reinterpret_cast<volatile uint64_t*>(mmap_header->data_head); | ||
if (mmap_header->data_tail >= data_end) { | ||
return nullopt; | ||
} | ||
__sync_synchronize(); | ||
|
||
char* data_buf = reinterpret_cast<char*>(mmap_header) + mmap_header->data_offset; | ||
uint64_t data_start = mmap_header->data_tail; | ||
size_t start_offset = data_start % mmap_header->data_size; | ||
auto header_ptr = reinterpret_cast<struct perf_event_header*>(data_buf + start_offset); | ||
struct perf_event_header header; | ||
memcpy(&header, header_ptr, sizeof(header)); | ||
|
||
size_t first_chunk_size = min<size_t>(header.size, | ||
mmap_header->data_size - start_offset); | ||
|
||
if (first_chunk_size < header.size) { | ||
packet_storage.resize(header.size); | ||
memcpy(packet_storage.data(), const_cast<perf_event_header*>(header_ptr), first_chunk_size); | ||
memcpy(packet_storage.data() + first_chunk_size, const_cast<char*>(data_buf), header.size - first_chunk_size); | ||
header_ptr = reinterpret_cast<struct perf_event_header*>(packet_storage.data()); | ||
} | ||
|
||
void* aux_ptr = nullptr; | ||
if (header.type == PERF_RECORD_AUX) { | ||
PerfEventAux aux_packet = *reinterpret_cast<PerfEventAux*>(header_ptr); | ||
size_t aux_start_offset = aux_packet.aux_offset % mmap_header->aux_size; | ||
aux_ptr = mmap_aux_buffer + aux_start_offset; | ||
first_chunk_size = min<size_t>(aux_packet.aux_size, mmap_header->aux_size - aux_start_offset); | ||
if (first_chunk_size < aux_packet.aux_size) { | ||
aux_packet_storage.resize(aux_packet.aux_size); | ||
memcpy(aux_packet_storage.data(), aux_ptr, first_chunk_size); | ||
memcpy(aux_packet_storage.data() + first_chunk_size, mmap_aux_buffer, | ||
aux_packet.aux_size - first_chunk_size); | ||
aux_ptr = aux_packet_storage.data(); | ||
} | ||
packet_data_aux_end = mmap_header->aux_tail + aux_packet.aux_size; | ||
} | ||
|
||
packet_in_use = true; | ||
packet_data_end = data_start + header.size; | ||
return Packet(*this, header_ptr, aux_ptr); | ||
} | ||
|
||
void PerfCounterBuffers::release_packet() { | ||
if (!packet_in_use) { | ||
FATAL() << "No packet!"; | ||
} | ||
mmap_header->data_tail = packet_data_end; | ||
mmap_header->aux_tail = packet_data_aux_end; | ||
packet_in_use = false; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
/* -*- Mode: C++; tab-width: 8; c-basic-offset: 2; indent-tabs-mode: nil; -*- */ | ||
|
||
#ifndef RR_PERF_COUNTER_BUFFERS_H_ | ||
#define RR_PERF_COUNTER_BUFFERS_H_ | ||
|
||
#include <stdint.h> | ||
|
||
#include <optional> | ||
|
||
#include <linux/perf_event.h> | ||
|
||
#include "ScopedFd.h" | ||
|
||
namespace rr { | ||
|
||
// I wish I knew why this type isn't defined in perf_event.h but is just | ||
// commented out there... | ||
struct PerfEventAux { | ||
struct perf_event_header header; | ||
uint64_t aux_offset; | ||
uint64_t aux_size; | ||
uint64_t flags; | ||
uint64_t sample_id; | ||
}; | ||
|
||
/** | ||
* Encapsulates the mmap buffers used for perf events. | ||
*/ | ||
class PerfCounterBuffers { | ||
public: | ||
PerfCounterBuffers() : mmap_header(nullptr), mmap_aux_buffer(nullptr), | ||
buffer_size(0), packet_in_use(false) {} | ||
~PerfCounterBuffers() { destroy(); } | ||
|
||
void allocate(ScopedFd& perf_event_fd, uint64_t buffer_size, uint64_t aux_size); | ||
void destroy(); | ||
|
||
bool allocated() const { return mmap_header != nullptr; } | ||
|
||
class Packet { | ||
public: | ||
Packet(Packet&& other) : buffers(other.buffers), data_(other.data_), | ||
aux_data_(other.aux_data_) { | ||
other.buffers = nullptr; | ||
} | ||
~Packet() { | ||
if (buffers) { | ||
buffers->release_packet(); | ||
} | ||
} | ||
struct perf_event_header* data() const { return data_; } | ||
void* aux_data() const { return aux_data_; } | ||
|
||
private: | ||
friend class PerfCounterBuffers; | ||
|
||
Packet(PerfCounterBuffers& buffers, struct perf_event_header* data, | ||
void* aux_data) | ||
: buffers(&buffers), data_(data), aux_data_(aux_data) {} | ||
|
||
PerfCounterBuffers* buffers; | ||
struct perf_event_header* data_; | ||
void* aux_data_; | ||
}; | ||
|
||
std::optional<Packet> next_packet(); | ||
|
||
private: | ||
friend class Packet; | ||
|
||
void release_packet(); | ||
|
||
perf_event_mmap_page* mmap_header; | ||
char* mmap_aux_buffer; | ||
uint64_t buffer_size; | ||
uint64_t packet_data_end; | ||
uint64_t packet_data_aux_end; | ||
std::vector<uint8_t> packet_storage; | ||
std::vector<uint8_t> aux_packet_storage; | ||
bool packet_in_use; | ||
}; | ||
|
||
} // namespace rr | ||
|
||
#endif /* RR_PERF_COUNTER_BUFFERS_H_ */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters