cpp/perfevents/Records.cpp (195 lines of code) (raw):
/**
* Copyright 2004-present, Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <profilo/perfevents/Event.h>
#include <profilo/perfevents/Records.h>
#include <profilo/perfevents/detail/FileBackedMappingsList.h>
#include <cstring>
namespace facebook {
namespace perfevents {
bool RecordMmap::isAnonymous() const {
// Purely anonymous entries have //anon as the filename.
static constexpr char kAnonPath[] = "//anon";
if (strncmp(filename, kAnonPath, strlen(kAnonPath)) == 0) {
return true;
}
// There are other named entries that are also anonymous.
// (e.g., [stack:1000])
if (detail::FileBackedMappingsList::isAnonymous(filename)) {
return true;
}
return false;
}
RecordSample::RecordSample(void* data, size_t len)
: data_((uint8_t*)data), len_(len) {}
uint64_t RecordSample::ip() const {
return *(reinterpret_cast<uint64_t*>(data_ + offsetForField(PERF_SAMPLE_IP)));
}
uint32_t RecordSample::pid() const {
return *(
reinterpret_cast<uint32_t*>(data_ + offsetForField(PERF_SAMPLE_TID)));
}
uint32_t RecordSample::tid() const {
return *(reinterpret_cast<uint32_t*>(
data_ + offsetForField(PERF_SAMPLE_TID) + 4 /* skip pid */));
}
uint64_t RecordSample::time() const {
return *(
reinterpret_cast<uint64_t*>(data_ + offsetForField(PERF_SAMPLE_TIME)));
}
uint64_t RecordSample::addr() const {
return *(
reinterpret_cast<uint64_t*>(data_ + offsetForField(PERF_SAMPLE_ADDR)));
}
uint64_t RecordSample::groupLeaderId() const {
return *(reinterpret_cast<uint64_t*>(data_ + offsetForField(PERF_SAMPLE_ID)));
}
uint64_t RecordSample::id() const {
return *(reinterpret_cast<uint64_t*>(
data_ + offsetForField(PERF_SAMPLE_STREAM_ID)));
}
uint32_t RecordSample::cpu() const {
return *(
reinterpret_cast<uint32_t*>(data_ + offsetForField(PERF_SAMPLE_CPU)));
}
uint64_t RecordSample::timeRunning() const {
return *(reinterpret_cast<uint64_t*>(
data_ + offsetForField(PERF_FORMAT_TOTAL_TIME_RUNNING)));
}
uint64_t RecordSample::timeEnabled() const {
return *(reinterpret_cast<uint64_t*>(
data_ + offsetForField(PERF_FORMAT_TOTAL_TIME_ENABLED)));
}
size_t RecordSample::size() const {
return len_;
}
namespace {
// Offset calculation routines for arbitrary sample_type and read_format
// values. Used to generate constexpr constants for the sample_type and
// read_format we actually use. Otherwise, it's not at all obvious how you
// compute the offsets or what needs to change if you add something new to
// kSampleType.
//
constexpr static size_t genericOffsetForReadFormat(
uint64_t read_format,
uint64_t field) {
size_t offset = sizeof(uint64_t); // skip initial `value` field, no `field`
// value corresponds to it.
if ((read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) != 0) {
if (field == PERF_FORMAT_TOTAL_TIME_ENABLED) {
return offset;
}
offset += sizeof(uint64_t);
}
if ((read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) != 0) {
if (field == PERF_FORMAT_TOTAL_TIME_RUNNING) {
return offset;
}
offset += sizeof(uint64_t);
}
if ((read_format & PERF_FORMAT_ID) != 0) {
if (field == PERF_FORMAT_ID) {
return offset;
}
offset += sizeof(uint64_t);
}
return offset;
}
constexpr static size_t genericOffsetForField(
uint64_t sample_type,
uint64_t read_format,
uint64_t field) {
size_t offset = 0;
if ((sample_type & PERF_SAMPLE_IDENTIFIER) != 0) {
if (field == PERF_SAMPLE_IDENTIFIER) {
return offset;
}
offset += sizeof(uint64_t);
}
if ((sample_type & PERF_SAMPLE_IP) != 0) {
if (field == PERF_SAMPLE_IP) {
return offset;
}
offset += sizeof(uint64_t);
}
if ((sample_type & PERF_SAMPLE_TID) != 0) {
if (field == PERF_SAMPLE_TID) {
return offset;
}
offset += 2 * sizeof(uint32_t); // uint32_t tid, pid
}
if ((sample_type & PERF_SAMPLE_TIME) != 0) {
if (field == PERF_SAMPLE_TIME) {
return offset;
}
offset += sizeof(uint64_t);
}
if ((sample_type & PERF_SAMPLE_ADDR) != 0) {
if (field == PERF_SAMPLE_ADDR) {
return offset;
}
offset += sizeof(uint64_t);
}
if ((sample_type & PERF_SAMPLE_ID) != 0) {
if (field == PERF_SAMPLE_ID) {
return offset;
}
offset += sizeof(uint64_t);
}
if ((sample_type & PERF_SAMPLE_STREAM_ID) != 0) {
if (field == PERF_SAMPLE_STREAM_ID) {
return offset;
}
offset += sizeof(uint64_t);
}
if ((sample_type & PERF_SAMPLE_CPU) != 0) {
if (field == PERF_SAMPLE_CPU) {
return offset;
}
offset += 2 * sizeof(uint32_t); // uint32_t cpu, __res;
}
if ((sample_type & PERF_SAMPLE_PERIOD) != 0) {
if (field == PERF_SAMPLE_PERIOD) {
return offset;
}
offset += sizeof(uint64_t);
}
bool field_in_read_format = field == PERF_FORMAT_ID ||
field == PERF_FORMAT_TOTAL_TIME_RUNNING ||
field == PERF_FORMAT_TOTAL_TIME_ENABLED;
if ((sample_type & PERF_SAMPLE_READ) != 0) {
if (field_in_read_format) {
return offset + genericOffsetForReadFormat(read_format, field);
}
// Skip the entire read_format struct.
// Every field in read_format is a u64, +1 because `value` is always
// present.
size_t bytes_in_read_format =
(__builtin_popcountll(read_format) + 1) * sizeof(uint64_t);
offset += bytes_in_read_format;
} else if (field_in_read_format) {
// If we hit this branch, we will de-constexpr-ify the function anyway.
throw std::logic_error(
"Attempting to access field in read_format without PERF_SAMPLE_READ in sample_type");
}
return offset;
}
} // namespace
size_t RecordSample::offsetForField(uint64_t field) const {
static constexpr uint64_t kTidOffset =
genericOffsetForField(kSampleType, kReadFormat, PERF_SAMPLE_TID);
static constexpr uint64_t kTimeOffset =
genericOffsetForField(kSampleType, kReadFormat, PERF_SAMPLE_TIME);
static constexpr uint64_t kAddrOffset =
genericOffsetForField(kSampleType, kReadFormat, PERF_SAMPLE_ADDR);
static constexpr uint64_t kIdOffset =
genericOffsetForField(kSampleType, kReadFormat, PERF_SAMPLE_ID);
static constexpr uint64_t kStreamIdOffset =
genericOffsetForField(kSampleType, kReadFormat, PERF_SAMPLE_STREAM_ID);
static constexpr uint64_t kCpuOffset =
genericOffsetForField(kSampleType, kReadFormat, PERF_SAMPLE_CPU);
static constexpr uint64_t kReadOffset =
genericOffsetForField(kSampleType, kReadFormat, PERF_SAMPLE_READ);
switch (field) {
case PERF_SAMPLE_TID:
return kTidOffset;
case PERF_SAMPLE_TIME:
return kTimeOffset;
case PERF_SAMPLE_ADDR:
return kAddrOffset;
case PERF_SAMPLE_ID:
return kIdOffset;
case PERF_SAMPLE_STREAM_ID:
return kStreamIdOffset;
case PERF_SAMPLE_CPU:
return kCpuOffset;
case PERF_SAMPLE_READ:
return kReadOffset;
}
throw std::invalid_argument("Requested field not in kSampleType");
}
} // namespace perfevents
} // namespace facebook