LOCAL_PATH := $(call my-dir)
simpleperf_common_cppflags := -std=c++11 -Wall -Wextra -Werror -Wunused
-simpleperf_use_bionic_perf_event_h_flag := -DUSE_BIONIC_PERF_EVENT_H -I bionic
-simpleperf_host_common_cppflags := $(simpleperf_common_cppflags) $(simpleperf_use_bionic_perf_event_h_flag)
+simpleperf_host_common_cppflags := $(simpleperf_common_cppflags) \
+ -DUSE_BIONIC_PERF_EVENT_H -I bionic \
+
+simpleperf_host_darwin_cppflags := $(simpleperf_host_common_cppflags) \
+ -I $(LOCAL_PATH)/darwin_support \
simpleperf_common_shared_libraries := \
libbase \
LLVM_ROOT_PATH := external/llvm
-libsimpleperf_src_files := \
+# libsimpleperf
+# =========================================================
+libsimpleperf_common_src_files := \
cmd_dumprecord.cpp \
cmd_help.cpp \
- cmd_list.cpp \
- cmd_record.cpp \
cmd_report.cpp \
- cmd_stat.cpp \
command.cpp \
dso.cpp \
- environment.cpp \
event_attr.cpp \
- event_fd.cpp \
- event_selection_set.cpp \
event_type.cpp \
read_elf.cpp \
record.cpp \
- record_file.cpp \
+ record_file_reader.cpp \
sample_tree.cpp \
utils.cpp \
+
+libsimpleperf_src_files := \
+ $(libsimpleperf_common_src_files) \
+ cmd_list.cpp \
+ cmd_record.cpp \
+ cmd_stat.cpp \
+ environment.cpp \
+ event_fd.cpp \
+ event_selection_set.cpp \
+ record_file_writer.cpp \
workload.cpp \
+libsimpleperf_darwin_src_files := \
+ $(libsimpleperf_common_src_files) \
+ environment_fake.cpp \
+
include $(CLEAR_VARS)
LOCAL_CLANG := true
LOCAL_CPPFLAGS := $(simpleperf_common_cppflags)
include $(BUILD_HOST_STATIC_LIBRARY)
endif
+ifeq ($(HOST_OS),darwin)
+include $(CLEAR_VARS)
+LOCAL_CLANG := true
+LOCAL_CPPFLAGS := $(simpleperf_host_darwin_cppflags)
+LOCAL_SRC_FILES := $(libsimpleperf_darwin_src_files)
+LOCAL_SHARED_LIBRARIES := $(simpleperf_common_shared_libraries)
+LOCAL_MODULE := libsimpleperf
+LOCAL_MODULE_TAGS := optional
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+include $(LLVM_ROOT_PATH)/llvm.mk
+include $(LLVM_HOST_BUILD_MK)
+include $(BUILD_HOST_SHARED_LIBRARY)
+endif
+
+# simpleperf
+# =========================================================
include $(CLEAR_VARS)
LOCAL_CLANG := true
LOCAL_CPPFLAGS := $(simpleperf_common_cppflags)
include $(BUILD_HOST_EXECUTABLE)
endif
+ifeq ($(HOST_OS),darwin)
+include $(CLEAR_VARS)
+LOCAL_CLANG := true
+LOCAL_CPPFLAGS := $(simpleperf_host_darwin_cppflags)
+LOCAL_SRC_FILES := main.cpp
+LOCAL_SHARED_LIBRARIES := libsimpleperf $(simpleperf_common_shared_libraries)
+LOCAL_MODULE := simpleperf
+LOCAL_MODULE_TAGS := optional
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+include $(BUILD_HOST_EXECUTABLE)
+endif
+
+# simpleperf_unit_test
+# =========================================================
+simpleperf_unit_test_common_src_files := \
+ command_test.cpp \
+ gtest_main.cpp \
+ record_test.cpp \
+ sample_tree_test.cpp \
+
simpleperf_unit_test_src_files := \
+ $(simpleperf_unit_test_common_src_files) \
cmd_dumprecord_test.cpp \
cmd_list_test.cpp \
cmd_record_test.cpp \
cmd_report_test.cpp \
cmd_stat_test.cpp \
- command_test.cpp \
cpu_offline_test.cpp \
environment_test.cpp \
- gtest_main.cpp \
read_elf_test.cpp \
record_file_test.cpp \
- record_test.cpp \
- sample_tree_test.cpp \
workload_test.cpp \
include $(CLEAR_VARS)
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
include $(BUILD_HOST_NATIVE_TEST)
endif
+
+ifeq ($(HOST_OS),darwin)
+include $(CLEAR_VARS)
+LOCAL_CLANG := true
+LOCAL_CPPFLAGS := $(simpleperf_host_darwin_cppflags)
+LOCAL_SRC_FILES := $(simpleperf_unit_test_common_src_files)
+LOCAL_SHARED_LIBRARIES := libsimpleperf $(simpleperf_common_shared_libraries)
+LOCAL_MODULE := simpleperf_unit_test
+LOCAL_MODULE_TAGS := optional
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+include $(BUILD_HOST_NATIVE_TEST)
+endif
#include <base/logging.h>
#include "command.h"
+#include "event_attr.h"
+#include "event_fd.h"
#include "event_type.h"
-#include "perf_event.h"
static void PrintEventTypesOfType(uint32_t type, const std::string& type_name,
const std::vector<EventType>& event_types) {
printf("List of %s:\n", type_name.c_str());
for (auto& event_type : event_types) {
- if (event_type.type == type && event_type.IsSupportedByKernel()) {
+ if (event_type.type == type &&
+ IsEventAttrSupportedByKernel(CreateDefaultPerfEventAttr(event_type))) {
printf(" %s\n", event_type.name.c_str());
}
}
}
bool RecordCommand::SetEventSelection() {
- event_selection_set_.AddEventType(*measured_event_type_modifier_);
+ if (!event_selection_set_.AddEventType(*measured_event_type_modifier_)) {
+ return false;
+ }
if (use_sample_freq_) {
event_selection_set_.SetSampleFreq(sample_freq_);
} else {
#include "command.h"
#include "environment.h"
+#include "event_attr.h"
+#include "event_fd.h"
#include "event_selection_set.h"
#include "event_type.h"
-#include "perf_event.h"
#include "utils.h"
#include "workload.h"
private:
bool ParseOptions(const std::vector<std::string>& args, std::vector<std::string>* non_option_args);
- bool AddMeasuredEventType(const std::string& event_type_name, bool report_unsupported_type = true);
+ bool AddMeasuredEventType(const std::string& event_type_name);
bool AddDefaultMeasuredEventTypes();
- void SetEventSelection();
+ bool SetEventSelection();
bool ShowCounters(const std::map<const EventType*, std::vector<PerfCounter>>& counters_map,
std::chrono::steady_clock::duration counting_duration);
return false;
}
}
- SetEventSelection();
+ if (!SetEventSelection()) {
+ return false;
+ }
// 2. Create workload.
std::unique_ptr<Workload> workload;
return true;
}
-bool StatCommand::AddMeasuredEventType(const std::string& event_type_name,
- bool report_unsupported_type) {
- std::unique_ptr<EventTypeAndModifier> event_type_modifier =
- ParseEventType(event_type_name, report_unsupported_type);
+bool StatCommand::AddMeasuredEventType(const std::string& event_type_name) {
+ std::unique_ptr<EventTypeAndModifier> event_type_modifier = ParseEventType(event_type_name);
if (event_type_modifier == nullptr) {
return false;
}
bool StatCommand::AddDefaultMeasuredEventTypes() {
for (auto& name : default_measured_event_types) {
// It is not an error when some event types in the default list are not supported by the kernel.
- AddMeasuredEventType(name, false);
+ const EventType* type = FindEventTypeByName(name);
+ if (type != nullptr && IsEventAttrSupportedByKernel(CreateDefaultPerfEventAttr(*type))) {
+ AddMeasuredEventType(name);
+ }
}
if (measured_event_types_.empty()) {
LOG(ERROR) << "Failed to add any supported default measured types";
return true;
}
-void StatCommand::SetEventSelection() {
+bool StatCommand::SetEventSelection() {
for (auto& pair : measured_event_types_) {
- event_selection_set_.AddEventType(pair.second);
+ if (!event_selection_set_.AddEventType(pair.second)) {
+ return false;
+ }
}
event_selection_set_.SetInherit(child_inherit_);
+ return true;
}
bool StatCommand::ShowCounters(
--- /dev/null
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * 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.
+ */
--- /dev/null
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#define __IO(type, nr)
+#define __IOR(type, nr, size)
+#define __IOW(type, nr, size)
--- /dev/null
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * 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 <stdint.h>
+
+typedef uint8_t __u8;
+typedef uint16_t __u16;
+typedef uint32_t __u32;
+typedef uint64_t __u64;
+typedef int64_t __s64;
const SymbolEntry* DsoEntry::FindSymbol(uint64_t offset_in_dso) {
std::unique_ptr<SymbolEntry> symbol(new SymbolEntry{
- .name = "", .addr = offset_in_dso, .len = 0,
+ "", // name
+ offset_in_dso, // addr
+ 0, // len
});
auto it = symbols.upper_bound(symbol);
static bool KernelSymbolCallback(const KernelSymbol& kernel_symbol, DsoEntry* dso) {
if (IsKernelFunctionSymbol(kernel_symbol)) {
SymbolEntry* symbol = new SymbolEntry{
- .name = kernel_symbol.name, .addr = kernel_symbol.addr, .len = 0,
+ kernel_symbol.name, // name
+ kernel_symbol.addr, // addr
+ 0, // len
};
dso->symbols.insert(std::unique_ptr<SymbolEntry>(symbol));
}
}
}
+// TODO: Fix the way to get kernel symbols. See b/22179177.
std::unique_ptr<DsoEntry> DsoFactory::LoadKernel() {
std::unique_ptr<DsoEntry> dso(new DsoEntry);
dso->path = "[kernel.kallsyms]";
bool (*filter)(const ElfFileSymbol&)) {
if (filter(elf_symbol)) {
SymbolEntry* symbol = new SymbolEntry{
- .name = elf_symbol.name, .addr = elf_symbol.start_in_file, .len = elf_symbol.len,
+ elf_symbol.name, // name
+ elf_symbol.start_in_file, // addr
+ elf_symbol.len, // len
};
dso->symbols.insert(std::unique_ptr<SymbolEntry>(symbol));
}
--- /dev/null
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * 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.
+ */
+
+// Add fake functions to build successfully on non-linux environments.
+#include "environment.h"
+
+bool ProcessKernelSymbols(const std::string&, std::function<bool(const KernelSymbol&)>) {
+ return false;
+}
poll_fd->fd = perf_event_fd_;
poll_fd->events = POLLIN;
}
+
+bool IsEventAttrSupportedByKernel(perf_event_attr attr) {
+ auto event_fd = EventFd::OpenEventFile(attr, getpid(), -1, false);
+ return event_fd != nullptr;
+}
DISALLOW_COPY_AND_ASSIGN(EventFd);
};
+bool IsEventAttrSupportedByKernel(perf_event_attr attr);
+
#endif // SIMPLE_PERF_EVENT_FD_H_
#include "event_type.h"
bool IsBranchSamplingSupported() {
- std::unique_ptr<EventTypeAndModifier> event_type_modifier = ParseEventType("cpu-cycles", false);
- if (event_type_modifier == nullptr) {
+ const EventType* type = FindEventTypeByName("cpu-cycles");
+ if (type == nullptr) {
return false;
}
- perf_event_attr attr = CreateDefaultPerfEventAttr(event_type_modifier->event_type);
+ perf_event_attr attr = CreateDefaultPerfEventAttr(*type);
attr.sample_type |= PERF_SAMPLE_BRANCH_STACK;
attr.branch_sample_type = PERF_SAMPLE_BRANCH_ANY;
- auto event_fd = EventFd::OpenEventFile(attr, getpid(), -1, false);
- return event_fd != nullptr;
+ return IsEventAttrSupportedByKernel(attr);
}
-void EventSelectionSet::AddEventType(const EventTypeAndModifier& event_type_modifier) {
+bool EventSelectionSet::AddEventType(const EventTypeAndModifier& event_type_modifier) {
EventSelection selection;
selection.event_type = event_type_modifier.event_type;
selection.event_attr = CreateDefaultPerfEventAttr(event_type_modifier.event_type);
selection.event_attr.exclude_host = event_type_modifier.exclude_host;
selection.event_attr.exclude_guest = event_type_modifier.exclude_guest;
selection.event_attr.precise_ip = event_type_modifier.precise_ip;
+ if (!IsEventAttrSupportedByKernel(selection.event_attr)) {
+ LOG(ERROR) << "Event type '" << selection.event_type.name << "' is not supported by the kernel";
+ return false;
+ }
selections_.push_back(std::move(selection));
+ return true;
}
void EventSelectionSet::SetEnableOnExec(bool enable) {
return selections_.empty();
}
- void AddEventType(const EventTypeAndModifier& event_type_modifier);
+ bool AddEventType(const EventTypeAndModifier& event_type_modifier);
void SetEnableOnExec(bool enable);
bool GetEnableOnExec();
#include "event_type_table.h"
};
-static bool IsEventTypeSupportedByKernel(const EventType& event_type) {
- auto event_fd =
- EventFd::OpenEventFile(CreateDefaultPerfEventAttr(event_type), getpid(), -1, false);
- return event_fd != nullptr;
-}
-
-bool EventType::IsSupportedByKernel() const {
- return IsEventTypeSupportedByKernel(*this);
-}
-
static const std::vector<EventType> GetTracepointEventTypes() {
std::vector<EventType> result;
const std::string tracepoint_dirname = "/sys/kernel/debug/tracing/events";
return nullptr;
}
-static const EventType* FindEventTypeByName(const std::string& name, bool report_unsupported_type) {
+const EventType* FindEventTypeByName(const std::string& name) {
const EventType* result = nullptr;
for (auto& event_type : GetAllEventTypes()) {
if (event_type.name == name) {
<< "', try `simpleperf list` to list all possible event type names";
return nullptr;
}
- if (!result->IsSupportedByKernel()) {
- (report_unsupported_type ? PLOG(ERROR) : PLOG(DEBUG)) << "Event type '" << result->name
- << "' is not supported by the kernel";
- return nullptr;
- }
return result;
}
-std::unique_ptr<EventTypeAndModifier> ParseEventType(const std::string& event_type_str,
- bool report_unsupported_type) {
+std::unique_ptr<EventTypeAndModifier> ParseEventType(const std::string& event_type_str) {
static std::string modifier_characters = "ukhGHp";
std::unique_ptr<EventTypeAndModifier> event_type_modifier(new EventTypeAndModifier);
std::string name = event_type_str;
modifier = event_type_str.substr(comm_pos + 1);
}
}
- const EventType* event_type = FindEventTypeByName(name, report_unsupported_type);
+ const EventType* event_type = FindEventTypeByName(name);
if (event_type == nullptr) {
// Try if the modifier belongs to the event type name, like some tracepoint events.
if (!modifier.empty()) {
name = event_type_str;
modifier.clear();
- event_type = FindEventTypeByName(name, report_unsupported_type);
+ event_type = FindEventTypeByName(name);
}
if (event_type == nullptr) {
return nullptr;
EventType() : type(0), config(0) {
}
- bool IsSupportedByKernel() const;
-
std::string name;
uint32_t type;
uint64_t config;
const std::vector<EventType>& GetAllEventTypes();
const EventType* FindEventTypeByConfig(uint32_t type, uint64_t config);
+const EventType* FindEventTypeByName(const std::string& name);
struct EventTypeAndModifier {
EventType event_type;
}
};
-std::unique_ptr<EventTypeAndModifier> ParseEventType(const std::string& event_type_str,
- bool report_unsupported_type = true);
+std::unique_ptr<EventTypeAndModifier> ParseEventType(const std::string& event_type_str);
#endif // SIMPLE_PERF_EVENT_H_
--- /dev/null
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * 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 "record_file.h"
+
+#include <fcntl.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <set>
+#include <vector>
+
+#include <base/logging.h>
+
+#include "perf_event.h"
+#include "record.h"
+#include "utils.h"
+
+using namespace PerfFileFormat;
+
+std::unique_ptr<RecordFileReader> RecordFileReader::CreateInstance(const std::string& filename) {
+ int fd = open(filename.c_str(), O_RDONLY | O_CLOEXEC);
+ if (fd == -1) {
+ PLOG(ERROR) << "failed to open record file '" << filename << "'";
+ return nullptr;
+ }
+ auto reader = std::unique_ptr<RecordFileReader>(new RecordFileReader(filename, fd));
+ if (!reader->MmapFile()) {
+ return nullptr;
+ }
+ return reader;
+}
+
+RecordFileReader::RecordFileReader(const std::string& filename, int fd)
+ : filename_(filename), record_fd_(fd), mmap_addr_(nullptr), mmap_len_(0) {
+}
+
+RecordFileReader::~RecordFileReader() {
+ if (record_fd_ != -1) {
+ Close();
+ }
+}
+
+bool RecordFileReader::Close() {
+ bool result = true;
+ if (munmap(const_cast<char*>(mmap_addr_), mmap_len_) == -1) {
+ PLOG(ERROR) << "failed to munmap() record file '" << filename_ << "'";
+ result = false;
+ }
+ if (close(record_fd_) == -1) {
+ PLOG(ERROR) << "failed to close record file '" << filename_ << "'";
+ result = false;
+ }
+ record_fd_ = -1;
+ return result;
+}
+
+bool RecordFileReader::MmapFile() {
+ off_t file_size = lseek(record_fd_, 0, SEEK_END);
+ if (file_size == -1) {
+ return false;
+ }
+ size_t mmap_len = file_size;
+ void* mmap_addr = mmap(nullptr, mmap_len, PROT_READ, MAP_SHARED, record_fd_, 0);
+ if (mmap_addr == MAP_FAILED) {
+ PLOG(ERROR) << "failed to mmap() record file '" << filename_ << "'";
+ return false;
+ }
+
+ mmap_addr_ = reinterpret_cast<const char*>(mmap_addr);
+ mmap_len_ = mmap_len;
+ return true;
+}
+
+const FileHeader* RecordFileReader::FileHeader() {
+ return reinterpret_cast<const struct FileHeader*>(mmap_addr_);
+}
+
+std::vector<const FileAttr*> RecordFileReader::AttrSection() {
+ std::vector<const FileAttr*> result;
+ const struct FileHeader* header = FileHeader();
+ size_t attr_count = header->attrs.size / header->attr_size;
+ const FileAttr* attr = reinterpret_cast<const FileAttr*>(mmap_addr_ + header->attrs.offset);
+ for (size_t i = 0; i < attr_count; ++i) {
+ result.push_back(attr++);
+ }
+ return result;
+}
+
+std::vector<uint64_t> RecordFileReader::IdsForAttr(const FileAttr* attr) {
+ std::vector<uint64_t> result;
+ size_t id_count = attr->ids.size / sizeof(uint64_t);
+ const uint64_t* id = reinterpret_cast<const uint64_t*>(mmap_addr_ + attr->ids.offset);
+ for (size_t i = 0; i < id_count; ++i) {
+ result.push_back(*id++);
+ }
+ return result;
+}
+
+static bool IsRecordHappensBefore(const std::unique_ptr<const Record>& r1,
+ const std::unique_ptr<const Record>& r2) {
+ bool is_r1_sample = (r1->header.type == PERF_RECORD_SAMPLE);
+ bool is_r2_sample = (r2->header.type == PERF_RECORD_SAMPLE);
+ uint64_t time1 = (is_r1_sample ? static_cast<const SampleRecord*>(r1.get())->time_data.time
+ : r1->sample_id.time_data.time);
+ uint64_t time2 = (is_r2_sample ? static_cast<const SampleRecord*>(r2.get())->time_data.time
+ : r2->sample_id.time_data.time);
+ // The record with smaller time happens first.
+ if (time1 != time2) {
+ return time1 < time2;
+ }
+ // If happening at the same time, make non-sample records before sample records,
+ // because non-sample records may contain useful information to parse sample records.
+ if (is_r1_sample != is_r2_sample) {
+ return is_r1_sample ? false : true;
+ }
+ // Otherwise, don't care of the order.
+ return false;
+}
+
+std::vector<std::unique_ptr<const Record>> RecordFileReader::DataSection() {
+ std::vector<std::unique_ptr<const Record>> result;
+ const struct FileHeader* header = FileHeader();
+ auto file_attrs = AttrSection();
+ CHECK(file_attrs.size() > 0);
+ perf_event_attr attr = file_attrs[0]->attr;
+
+ const char* end = mmap_addr_ + header->data.offset + header->data.size;
+ const char* p = mmap_addr_ + header->data.offset;
+ while (p < end) {
+ const perf_event_header* header = reinterpret_cast<const perf_event_header*>(p);
+ if (p + header->size <= end) {
+ result.push_back(std::move(ReadRecordFromBuffer(attr, header)));
+ }
+ p += header->size;
+ }
+ if ((attr.sample_type & PERF_SAMPLE_TIME) && attr.sample_id_all) {
+ std::sort(result.begin(), result.end(), IsRecordHappensBefore);
+ }
+ return result;
+}
+
+const std::map<int, SectionDesc>& RecordFileReader::FeatureSectionDescriptors() {
+ if (feature_sections_.empty()) {
+ std::vector<int> features;
+ const struct FileHeader* header = FileHeader();
+ for (size_t i = 0; i < sizeof(header->features); ++i) {
+ for (size_t j = 0; j < 8; ++j) {
+ if (header->features[i] & (1 << j)) {
+ features.push_back(i * 8 + j);
+ }
+ }
+ }
+ uint64_t feature_section_offset = header->data.offset + header->data.size;
+ const SectionDesc* p = reinterpret_cast<const SectionDesc*>(mmap_addr_ + feature_section_offset);
+ for (auto& feature : features) {
+ feature_sections_.insert(std::make_pair(feature, *p));
+ ++p;
+ }
+ }
+ return feature_sections_;
+}
+
+std::vector<std::string> RecordFileReader::ReadCmdlineFeature() {
+ const std::map<int, SectionDesc>& section_map = FeatureSectionDescriptors();
+ auto it = section_map.find(FEAT_CMDLINE);
+ if (it == section_map.end()) {
+ return std::vector<std::string>();
+ }
+ SectionDesc section = it->second;
+ const char* p = DataAtOffset(section.offset);
+ const char* end = DataAtOffset(section.offset + section.size);
+ std::vector<std::string> cmdline;
+ uint32_t arg_count;
+ MoveFromBinaryFormat(arg_count, p);
+ CHECK_LE(p, end);
+ for (size_t i = 0; i < arg_count; ++i) {
+ uint32_t len;
+ MoveFromBinaryFormat(len, p);
+ CHECK_LE(p + len, end);
+ cmdline.push_back(p);
+ p += len;
+ }
+ return cmdline;
+}
record_fp_ = nullptr;
return result;
}
-
-std::unique_ptr<RecordFileReader> RecordFileReader::CreateInstance(const std::string& filename) {
- int fd = open(filename.c_str(), O_RDONLY | O_CLOEXEC);
- if (fd == -1) {
- PLOG(ERROR) << "failed to open record file '" << filename << "'";
- return nullptr;
- }
- auto reader = std::unique_ptr<RecordFileReader>(new RecordFileReader(filename, fd));
- if (!reader->MmapFile()) {
- return nullptr;
- }
- return reader;
-}
-
-RecordFileReader::RecordFileReader(const std::string& filename, int fd)
- : filename_(filename), record_fd_(fd), mmap_addr_(nullptr), mmap_len_(0) {
-}
-
-RecordFileReader::~RecordFileReader() {
- if (record_fd_ != -1) {
- Close();
- }
-}
-
-bool RecordFileReader::Close() {
- bool result = true;
- if (munmap(const_cast<char*>(mmap_addr_), mmap_len_) == -1) {
- PLOG(ERROR) << "failed to munmap() record file '" << filename_ << "'";
- result = false;
- }
- if (close(record_fd_) == -1) {
- PLOG(ERROR) << "failed to close record file '" << filename_ << "'";
- result = false;
- }
- record_fd_ = -1;
- return result;
-}
-
-bool RecordFileReader::MmapFile() {
- off64_t file_size = lseek64(record_fd_, 0, SEEK_END);
- if (file_size == -1) {
- return false;
- }
- size_t mmap_len = file_size;
- void* mmap_addr = mmap(nullptr, mmap_len, PROT_READ, MAP_SHARED, record_fd_, 0);
- if (mmap_addr == MAP_FAILED) {
- PLOG(ERROR) << "failed to mmap() record file '" << filename_ << "'";
- return false;
- }
-
- mmap_addr_ = reinterpret_cast<const char*>(mmap_addr);
- mmap_len_ = mmap_len;
- return true;
-}
-
-const FileHeader* RecordFileReader::FileHeader() {
- return reinterpret_cast<const struct FileHeader*>(mmap_addr_);
-}
-
-std::vector<const FileAttr*> RecordFileReader::AttrSection() {
- std::vector<const FileAttr*> result;
- const struct FileHeader* header = FileHeader();
- size_t attr_count = header->attrs.size / header->attr_size;
- const FileAttr* attr = reinterpret_cast<const FileAttr*>(mmap_addr_ + header->attrs.offset);
- for (size_t i = 0; i < attr_count; ++i) {
- result.push_back(attr++);
- }
- return result;
-}
-
-std::vector<uint64_t> RecordFileReader::IdsForAttr(const FileAttr* attr) {
- std::vector<uint64_t> result;
- size_t id_count = attr->ids.size / sizeof(uint64_t);
- const uint64_t* id = reinterpret_cast<const uint64_t*>(mmap_addr_ + attr->ids.offset);
- for (size_t i = 0; i < id_count; ++i) {
- result.push_back(*id++);
- }
- return result;
-}
-
-static bool IsRecordHappensBefore(const std::unique_ptr<const Record>& r1,
- const std::unique_ptr<const Record>& r2) {
- bool is_r1_sample = (r1->header.type == PERF_RECORD_SAMPLE);
- bool is_r2_sample = (r2->header.type == PERF_RECORD_SAMPLE);
- uint64_t time1 = (is_r1_sample ? static_cast<const SampleRecord*>(r1.get())->time_data.time
- : r1->sample_id.time_data.time);
- uint64_t time2 = (is_r2_sample ? static_cast<const SampleRecord*>(r2.get())->time_data.time
- : r2->sample_id.time_data.time);
- // The record with smaller time happens first.
- if (time1 != time2) {
- return time1 < time2;
- }
- // If happening at the same time, make non-sample records before sample records,
- // because non-sample records may contain useful information to parse sample records.
- if (is_r1_sample != is_r2_sample) {
- return is_r1_sample ? false : true;
- }
- // Otherwise, don't care of the order.
- return false;
-}
-
-std::vector<std::unique_ptr<const Record>> RecordFileReader::DataSection() {
- std::vector<std::unique_ptr<const Record>> result;
- const struct FileHeader* header = FileHeader();
- auto file_attrs = AttrSection();
- CHECK(file_attrs.size() > 0);
- perf_event_attr attr = file_attrs[0]->attr;
-
- const char* end = mmap_addr_ + header->data.offset + header->data.size;
- const char* p = mmap_addr_ + header->data.offset;
- while (p < end) {
- const perf_event_header* header = reinterpret_cast<const perf_event_header*>(p);
- if (p + header->size <= end) {
- result.push_back(std::move(ReadRecordFromBuffer(attr, header)));
- }
- p += header->size;
- }
- if ((attr.sample_type & PERF_SAMPLE_TIME) && attr.sample_id_all) {
- std::sort(result.begin(), result.end(), IsRecordHappensBefore);
- }
- return result;
-}
-
-const std::map<int, SectionDesc>& RecordFileReader::FeatureSectionDescriptors() {
- if (feature_sections_.empty()) {
- std::vector<int> features;
- const struct FileHeader* header = FileHeader();
- for (size_t i = 0; i < sizeof(header->features); ++i) {
- for (size_t j = 0; j < 8; ++j) {
- if (header->features[i] & (1 << j)) {
- features.push_back(i * 8 + j);
- }
- }
- }
- uint64_t feature_section_offset = header->data.offset + header->data.size;
- const SectionDesc* p = reinterpret_cast<const SectionDesc*>(mmap_addr_ + feature_section_offset);
- for (auto& feature : features) {
- feature_sections_.insert(std::make_pair(feature, *p));
- ++p;
- }
- }
- return feature_sections_;
-}
-
-std::vector<std::string> RecordFileReader::ReadCmdlineFeature() {
- const std::map<int, SectionDesc>& section_map = FeatureSectionDescriptors();
- auto it = section_map.find(FEAT_CMDLINE);
- if (it == section_map.end()) {
- return std::vector<std::string>();
- }
- SectionDesc section = it->second;
- const char* p = DataAtOffset(section.offset);
- const char* end = DataAtOffset(section.offset + section.size);
- std::vector<std::string> cmdline;
- uint32_t arg_count;
- MoveFromBinaryFormat(arg_count, p);
- CHECK_LE(p, end);
- for (size_t i = 0; i < arg_count; ++i) {
- uint32_t len;
- MoveFromBinaryFormat(len, p);
- CHECK_LE(p + len, end);
- cmdline.push_back(p);
- p += len;
- }
- return cmdline;
-}
class RecordTest : public ::testing::Test {
protected:
virtual void SetUp() {
- std::unique_ptr<EventTypeAndModifier> event_type_modifier = ParseEventType("cpu-cycles");
- ASSERT_TRUE(event_type_modifier != nullptr);
- event_attr = CreateDefaultPerfEventAttr(event_type_modifier->event_type);
+ const EventType* type = FindEventTypeByName("cpu-cycles");
+ ASSERT_TRUE(type != nullptr);
+ event_attr = CreateDefaultPerfEventAttr(*type);
}
template <class RecordType>
public:
SignalHandlerRegister(const std::vector<int>& signums, void (*handler)(int)) {
for (auto& sig : signums) {
- sighandler_t old_handler = signal(sig, handler);
+ sig_t old_handler = signal(sig, handler);
saved_signal_handlers_.push_back(std::make_pair(sig, old_handler));
}
}
}
private:
- std::vector<std::pair<int, sighandler_t>> saved_signal_handlers_;
+ std::vector<std::pair<int, sig_t>> saved_signal_handlers_;
};
template <class T>