summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYabin Cui <yabinc@google.com>2015-05-13 23:33:54 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2015-05-13 23:35:38 +0000
commitc49491f2f0cc3e5d3e4c57bd755226c3b93490a0 (patch)
tree71ab094fc6fc165652ae5e5b85e3969fc8e18f49
parentf044a21af13472bed9d74b96829a0e5597af0a7a (diff)
parent8f6225147c5b6cb2159a7f5cb0dab952ee0759df (diff)
downloadextras-c49491f2f0cc3e5d3e4c57bd755226c3b93490a0.tar.gz
Merge "Dump build_id feature in `simpleperf record`."
-rw-r--r--simpleperf/Android.mk24
-rw-r--r--simpleperf/build_id.h26
-rw-r--r--simpleperf/cmd_dumprecord.cpp25
-rw-r--r--simpleperf/cmd_record.cpp63
-rw-r--r--simpleperf/cmd_record_test.cpp12
-rw-r--r--simpleperf/environment.cpp10
-rw-r--r--simpleperf/environment.h6
-rw-r--r--simpleperf/event_type.cpp4
-rw-r--r--simpleperf/read_elf.cpp116
-rw-r--r--simpleperf/read_elf.h26
-rw-r--r--simpleperf/record.cpp46
-rw-r--r--simpleperf/record.h31
-rw-r--r--simpleperf/record_equal_test.h9
-rw-r--r--simpleperf/record_file.cpp175
-rw-r--r--simpleperf/record_file.h19
-rw-r--r--simpleperf/record_file_test.cpp22
16 files changed, 597 insertions, 17 deletions
diff --git a/simpleperf/Android.mk b/simpleperf/Android.mk
index 2635da0a..f37c4b0b 100644
--- a/simpleperf/Android.mk
+++ b/simpleperf/Android.mk
@@ -18,10 +18,11 @@ LOCAL_PATH := $(call my-dir)
simpleperf_common_cppflags := -std=c++11 -Wall -Wextra -Werror -Wunused
-simpleperf_common_static_libraries := \
+simpleperf_common_shared_libraries := \
libbase \
- libcutils \
- liblog \
+ libLLVM \
+
+LLVM_ROOT_PATH := external/llvm
libsimpleperf_src_files := \
cmd_dumprecord.cpp \
@@ -35,6 +36,7 @@ libsimpleperf_src_files := \
event_fd.cpp \
event_selection_set.cpp \
event_type.cpp \
+ read_elf.cpp \
record.cpp \
record_file.cpp \
utils.cpp \
@@ -44,11 +46,13 @@ include $(CLEAR_VARS)
LOCAL_CLANG := true
LOCAL_CPPFLAGS := $(simpleperf_common_cppflags)
LOCAL_SRC_FILES := $(libsimpleperf_src_files)
-LOCAL_STATIC_LIBRARIES := $(simpleperf_common_static_libraries)
+LOCAL_SHARED_LIBRARIES := $(simpleperf_common_shared_libraries)
LOCAL_MODULE := libsimpleperf
LOCAL_MODULE_TAGS := debug
LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+include $(LLVM_ROOT_PATH)/llvm.mk
+include $(LLVM_DEVICE_BUILD_MK)
include $(BUILD_STATIC_LIBRARY)
ifeq ($(HOST_OS),linux)
@@ -56,11 +60,13 @@ include $(CLEAR_VARS)
LOCAL_CLANG := true
LOCAL_CPPFLAGS := $(simpleperf_common_cppflags)
LOCAL_SRC_FILES := $(libsimpleperf_src_files)
-LOCAL_STATIC_LIBRARIES := $(simpleperf_common_static_libraries)
+LOCAL_SHARED_LIBRARIES := $(simpleperf_common_shared_libraries)
LOCAL_LDLIBS := -lrt
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_STATIC_LIBRARY)
endif
@@ -69,7 +75,7 @@ LOCAL_CLANG := true
LOCAL_CPPFLAGS := $(simpleperf_common_cppflags)
LOCAL_SRC_FILES := main.cpp
LOCAL_WHOLE_STATIC_LIBRARIES := libsimpleperf
-LOCAL_STATIC_LIBRARIES := $(simpleperf_common_static_libraries)
+LOCAL_SHARED_LIBRARIES := $(simpleperf_common_shared_libraries)
LOCAL_MODULE := simpleperf
LOCAL_MODULE_TAGS := debug
LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
@@ -82,7 +88,7 @@ LOCAL_CLANG := true
LOCAL_CPPFLAGS := $(simpleperf_common_cppflags)
LOCAL_SRC_FILES := main.cpp
LOCAL_WHOLE_STATIC_LIBRARIES := libsimpleperf
-LOCAL_STATIC_LIBRARIES := $(simpleperf_common_static_libraries)
+LOCAL_SHARED_LIBRARIES := $(simpleperf_common_shared_libraries)
LOCAL_LDLIBS := -lrt
LOCAL_MODULE := simpleperf
LOCAL_MODULE_TAGS := optional
@@ -107,7 +113,7 @@ LOCAL_CLANG := true
LOCAL_CPPFLAGS := $(simpleperf_common_cppflags)
LOCAL_SRC_FILES := $(simpleperf_unit_test_src_files)
LOCAL_WHOLE_STATIC_LIBRARIES := libsimpleperf
-LOCAL_STATIC_LIBRARIES := $(simpleperf_common_static_libraries)
+LOCAL_SHARED_LIBRARIES := $(simpleperf_common_shared_libraries)
LOCAL_MODULE := simpleperf_unit_test
LOCAL_MODULE_TAGS := optional
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
@@ -119,7 +125,7 @@ LOCAL_CLANG := true
LOCAL_CPPFLAGS := $(simpleperf_common_cppflags)
LOCAL_SRC_FILES := $(simpleperf_unit_test_src_files)
LOCAL_WHOLE_STATIC_LIBRARIES := libsimpleperf
-LOCAL_STATIC_LIBRARIES := $(simpleperf_common_static_libraries)
+LOCAL_SHARED_LIBRARIES := $(simpleperf_common_shared_libraries)
LOCAL_MODULE := simpleperf_unit_test
LOCAL_MODULE_TAGS := optional
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
diff --git a/simpleperf/build_id.h b/simpleperf/build_id.h
new file mode 100644
index 00000000..5a4b12cb
--- /dev/null
+++ b/simpleperf/build_id.h
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+#ifndef SIMPLE_PERF_BUILD_ID_H_
+#define SIMPLE_PERF_BUILD_ID_H_
+
+#include <array>
+
+static constexpr int BUILD_ID_SIZE = 20;
+
+typedef std::array<unsigned char, BUILD_ID_SIZE> BuildId;
+
+#endif // SIMPLE_PERF_BUILD_ID_H_
diff --git a/simpleperf/cmd_dumprecord.cpp b/simpleperf/cmd_dumprecord.cpp
index 4ee93942..57eec1f6 100644
--- a/simpleperf/cmd_dumprecord.cpp
+++ b/simpleperf/cmd_dumprecord.cpp
@@ -42,6 +42,7 @@ class DumpRecordCommandImpl {
void DumpFileHeader();
void DumpAttrSection();
void DumpDataSection();
+ void DumpFeatureSection();
std::string record_filename_;
std::unique_ptr<RecordFileReader> record_file_reader_;
@@ -60,6 +61,7 @@ bool DumpRecordCommandImpl::Run(const std::vector<std::string>& args) {
DumpFileHeader();
DumpAttrSection();
DumpDataSection();
+ DumpFeatureSection();
return true;
}
@@ -162,6 +164,29 @@ void DumpRecordCommandImpl::DumpDataSection() {
}
}
+void DumpRecordCommandImpl::DumpFeatureSection() {
+ std::vector<SectionDesc> sections = record_file_reader_->FeatureSectionDescriptors();
+ CHECK_EQ(sections.size(), features_.size());
+ for (size_t i = 0; i < features_.size(); ++i) {
+ int feature = features_[i];
+ SectionDesc& section = sections[i];
+ printf("feature section for %s: offset %" PRId64 ", size %" PRId64 "\n",
+ GetFeatureName(feature).c_str(), section.offset, section.size);
+ if (feature == FEAT_BUILD_ID) {
+ const char* p = record_file_reader_->DataAtOffset(section.offset);
+ const char* end = p + section.size;
+ while (p < end) {
+ const perf_event_header* header = reinterpret_cast<const perf_event_header*>(p);
+ CHECK_LE(p + header->size, end);
+ CHECK_EQ(PERF_RECORD_BUILD_ID, header->type);
+ BuildIdRecord record(header);
+ record.Dump(1);
+ p += header->size;
+ }
+ }
+ }
+}
+
class DumpRecordCommand : public Command {
public:
DumpRecordCommand()
diff --git a/simpleperf/cmd_record.cpp b/simpleperf/cmd_record.cpp
index e27b6e4b..98a0cd55 100644
--- a/simpleperf/cmd_record.cpp
+++ b/simpleperf/cmd_record.cpp
@@ -14,17 +14,20 @@
* limitations under the License.
*/
+#include <libgen.h>
#include <poll.h>
#include <signal.h>
#include <string>
#include <vector>
#include <base/logging.h>
+#include <base/strings.h>
#include "command.h"
#include "environment.h"
#include "event_selection_set.h"
#include "event_type.h"
+#include "read_elf.h"
#include "record.h"
#include "record_file.h"
#include "utils.h"
@@ -60,6 +63,8 @@ class RecordCommandImpl {
bool WriteData(const char* data, size_t size);
bool DumpKernelAndModuleMmaps();
bool DumpThreadCommAndMmaps();
+ bool DumpAdditionalFeatures();
+ bool DumpBuildIdFeature();
bool use_sample_freq_; // Use sample_freq_ when true, otherwise using sample_period_.
uint64_t sample_freq_; // Sample 'sample_freq_' times per second.
@@ -157,7 +162,10 @@ bool RecordCommandImpl::Run(const std::vector<std::string>& args) {
poll(&pollfds[0], pollfds.size(), -1);
}
- // 6. Close record file.
+ // 6. Dump additional features, and close record file.
+ if (!DumpAdditionalFeatures()) {
+ return false;
+ }
if (!record_file_writer_->Close()) {
return false;
}
@@ -302,6 +310,59 @@ bool RecordCommandImpl::DumpThreadCommAndMmaps() {
return true;
}
+bool RecordCommandImpl::DumpAdditionalFeatures() {
+ if (!record_file_writer_->WriteFeatureHeader(1)) {
+ return false;
+ }
+ return DumpBuildIdFeature();
+}
+
+bool RecordCommandImpl::DumpBuildIdFeature() {
+ std::vector<std::string> hit_kernel_modules;
+ std::vector<std::string> hit_user_files;
+ if (!record_file_writer_->GetHitModules(&hit_kernel_modules, &hit_user_files)) {
+ return false;
+ }
+ std::vector<BuildIdRecord> build_id_records;
+ BuildId build_id;
+ // Add build_ids for kernel/modules.
+ for (auto& filename : hit_kernel_modules) {
+ if (filename == DEFAULT_KERNEL_MMAP_NAME) {
+ if (!GetKernelBuildId(&build_id)) {
+ LOG(DEBUG) << "can't read build_id for kernel";
+ continue;
+ }
+ build_id_records.push_back(
+ CreateBuildIdRecord(true, UINT_MAX, build_id, DEFAULT_KERNEL_FILENAME_FOR_BUILD_ID));
+ } else {
+ std::string module_name = basename(&filename[0]);
+ if (android::base::EndsWith(module_name, ".ko")) {
+ module_name = module_name.substr(0, module_name.size() - 3);
+ }
+ if (!GetModuleBuildId(module_name, &build_id)) {
+ LOG(DEBUG) << "can't read build_id for module " << module_name;
+ continue;
+ }
+ build_id_records.push_back(CreateBuildIdRecord(true, UINT_MAX, build_id, filename));
+ }
+ }
+ // Add build_ids for user elf files.
+ for (auto& filename : hit_user_files) {
+ if (filename == DEFAULT_EXECNAME_FOR_THREAD_MMAP) {
+ continue;
+ }
+ if (!GetBuildIdFromElfFile(filename, &build_id)) {
+ LOG(DEBUG) << "can't read build_id from file " << filename;
+ continue;
+ }
+ build_id_records.push_back(CreateBuildIdRecord(false, UINT_MAX, build_id, filename));
+ }
+ if (!record_file_writer_->WriteBuildIdFeature(build_id_records)) {
+ return false;
+ }
+ return true;
+}
+
class RecordCommand : public Command {
public:
RecordCommand()
diff --git a/simpleperf/cmd_record_test.cpp b/simpleperf/cmd_record_test.cpp
index be011391..f0a8878b 100644
--- a/simpleperf/cmd_record_test.cpp
+++ b/simpleperf/cmd_record_test.cpp
@@ -21,6 +21,8 @@
#include "record.h"
#include "record_file.h"
+using namespace PerfFileFormat;
+
class RecordCommandTest : public ::testing::Test {
protected:
virtual void SetUp() {
@@ -74,3 +76,13 @@ TEST_F(RecordCommandTest, dump_kernel_mmap) {
}
ASSERT_TRUE(have_kernel_mmap);
}
+
+TEST_F(RecordCommandTest, dump_build_id_feature) {
+ ASSERT_TRUE(record_cmd->Run({"record", "sleep", "1"}));
+ std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance("perf.data");
+ ASSERT_TRUE(reader != nullptr);
+ const FileHeader* file_header = reader->FileHeader();
+ ASSERT_TRUE(file_header != nullptr);
+ ASSERT_TRUE(file_header->features[FEAT_BUILD_ID / 8] & (1 << (FEAT_BUILD_ID % 8)));
+ ASSERT_GT(reader->FeatureSectionDescriptors().size(), 0u);
+}
diff --git a/simpleperf/environment.cpp b/simpleperf/environment.cpp
index a2de935a..0270b247 100644
--- a/simpleperf/environment.cpp
+++ b/simpleperf/environment.cpp
@@ -27,6 +27,7 @@
#include <base/strings.h>
#include <base/stringprintf.h>
+#include "read_elf.h"
#include "utils.h"
std::vector<int> GetOnlineCpus() {
@@ -346,3 +347,12 @@ bool GetThreadMmapsInProcess(pid_t pid, std::vector<ThreadMmap>* thread_mmaps) {
}
return true;
}
+
+bool GetKernelBuildId(BuildId* build_id) {
+ return GetBuildIdFromNoteFile("/sys/kernel/notes", build_id);
+}
+
+bool GetModuleBuildId(const std::string& module_name, BuildId* build_id) {
+ std::string notefile = "/sys/module/" + module_name + "/notes/.note.gnu.build-id";
+ return GetBuildIdFromNoteFile(notefile, build_id);
+}
diff --git a/simpleperf/environment.h b/simpleperf/environment.h
index c4110677..f81005ce 100644
--- a/simpleperf/environment.h
+++ b/simpleperf/environment.h
@@ -20,6 +20,7 @@
#include <functional>
#include <string>
#include <vector>
+#include "build_id.h"
std::vector<int> GetOnlineCpus();
@@ -61,6 +62,11 @@ struct ThreadMmap {
bool GetThreadMmapsInProcess(pid_t pid, std::vector<ThreadMmap>* thread_mmaps);
+static const char* DEFAULT_KERNEL_FILENAME_FOR_BUILD_ID = "[kernel.kallsyms]";
+
+bool GetKernelBuildId(BuildId* build_id);
+bool GetModuleBuildId(const std::string& module_name, BuildId* build_id);
+
// Expose the following functions for unit tests.
std::vector<int> GetOnlineCpusFromString(const std::string& s);
diff --git a/simpleperf/event_type.cpp b/simpleperf/event_type.cpp
index 15e3cf17..ee0e161f 100644
--- a/simpleperf/event_type.cpp
+++ b/simpleperf/event_type.cpp
@@ -61,8 +61,8 @@ const EventType* EventTypeFactory::FindEventTypeByName(const std::string& name,
return nullptr;
}
if (!result->IsSupportedByKernel()) {
- (report_unsupported_type ? LOG(ERROR) : LOG(DEBUG)) << "Event type '" << result->name
- << "' is not supported by the kernel";
+ (report_unsupported_type ? PLOG(ERROR) : PLOG(DEBUG)) << "Event type '" << result->name
+ << "' is not supported by the kernel";
return nullptr;
}
return result;
diff --git a/simpleperf/read_elf.cpp b/simpleperf/read_elf.cpp
new file mode 100644
index 00000000..1873b308
--- /dev/null
+++ b/simpleperf/read_elf.cpp
@@ -0,0 +1,116 @@
+/*
+ * 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 "read_elf.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <algorithm>
+#include <base/file.h>
+#include <base/logging.h>
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-parameter"
+
+#include <llvm/ADT/StringRef.h>
+#include <llvm/Object/Binary.h>
+#include <llvm/Object/ELFObjectFile.h>
+#include <llvm/Object/ObjectFile.h>
+
+#pragma clang diagnostic pop
+
+#include <elf.h>
+
+#include "utils.h"
+
+static bool GetBuildIdFromNoteSection(const char* section, size_t section_size, BuildId* build_id) {
+ const char* p = section;
+ const char* end = p + section_size;
+ while (p < end) {
+ CHECK_LE(p + 12, end);
+ size_t namesz = *reinterpret_cast<const uint32_t*>(p);
+ p += 4;
+ size_t descsz = *reinterpret_cast<const uint32_t*>(p);
+ p += 4;
+ uint32_t type = *reinterpret_cast<const uint32_t*>(p);
+ p += 4;
+ namesz = ALIGN(namesz, 4);
+ descsz = ALIGN(descsz, 4);
+ CHECK_LE(p + namesz + descsz, end);
+ if ((type == NT_GNU_BUILD_ID) && (strcmp(p, ELF_NOTE_GNU) == 0)) {
+ std::fill(build_id->begin(), build_id->end(), 0);
+ memcpy(build_id->data(), p + namesz, std::min(build_id->size(), descsz));
+ return true;
+ }
+ p += namesz + descsz;
+ }
+ return false;
+}
+
+bool GetBuildIdFromNoteFile(const std::string& filename, BuildId* build_id) {
+ std::string content;
+ if (!android::base::ReadFileToString(filename, &content)) {
+ LOG(DEBUG) << "can't read note file " << filename;
+ return false;
+ }
+ if (GetBuildIdFromNoteSection(content.c_str(), content.size(), build_id) == false) {
+ LOG(DEBUG) << "can't read build_id from note file " << filename;
+ return false;
+ }
+ return true;
+}
+
+template <class ELFT>
+bool GetBuildIdFromELFFile(const llvm::object::ELFFile<ELFT>* elf, BuildId* build_id) {
+ for (auto section_iterator = elf->begin_sections(); section_iterator != elf->end_sections();
+ ++section_iterator) {
+ if (section_iterator->sh_type == SHT_NOTE) {
+ auto contents = elf->getSectionContents(&*section_iterator);
+ if (contents.getError()) {
+ LOG(DEBUG) << "read note section error";
+ continue;
+ }
+ if (GetBuildIdFromNoteSection(reinterpret_cast<const char*>(contents->data()),
+ contents->size(), build_id)) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool GetBuildIdFromElfFile(const std::string& filename, BuildId* build_id) {
+ auto owning_binary = llvm::object::createBinary(llvm::StringRef(filename));
+ if (owning_binary.getError()) {
+ PLOG(DEBUG) << "can't open file " << filename;
+ return false;
+ }
+ bool result = false;
+ llvm::object::Binary* binary = owning_binary.get().getBinary();
+ if (auto obj = llvm::dyn_cast<llvm::object::ObjectFile>(binary)) {
+ if (auto elf = llvm::dyn_cast<llvm::object::ELF32LEObjectFile>(obj)) {
+ result = GetBuildIdFromELFFile(elf->getELFFile(), build_id);
+ } else if (auto elf = llvm::dyn_cast<llvm::object::ELF64LEObjectFile>(obj)) {
+ result = GetBuildIdFromELFFile(elf->getELFFile(), build_id);
+ } else {
+ PLOG(DEBUG) << "unknown elf format in file " << filename;
+ }
+ }
+ if (!result) {
+ PLOG(DEBUG) << "can't read build_id from file " << filename;
+ }
+ return result;
+}
diff --git a/simpleperf/read_elf.h b/simpleperf/read_elf.h
new file mode 100644
index 00000000..bc65fea0
--- /dev/null
+++ b/simpleperf/read_elf.h
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+#ifndef SIMPLE_PERF_READ_ELF_H_
+#define SIMPLE_PERF_READ_ELF_H_
+
+#include <string>
+#include "build_id.h"
+
+bool GetBuildIdFromNoteFile(const std::string& filename, BuildId* build_id);
+bool GetBuildIdFromElfFile(const std::string& filename, BuildId* build_id);
+
+#endif // SIMPLE_PERF_READ_ELF_H_
diff --git a/simpleperf/record.cpp b/simpleperf/record.cpp
index 3e34d525..46910b92 100644
--- a/simpleperf/record.cpp
+++ b/simpleperf/record.cpp
@@ -303,6 +303,39 @@ void SampleRecord::DumpData(size_t indent) const {
}
}
+BuildIdRecord::BuildIdRecord(const perf_event_header* pheader) : Record(pheader) {
+ const char* p = reinterpret_cast<const char*>(pheader + 1);
+ const char* end = reinterpret_cast<const char*>(pheader) + pheader->size;
+ MoveFromBinaryFormat(pid, p);
+ std::copy_n(p, build_id.size(), build_id.begin());
+ p += ALIGN(build_id.size(), 8);
+ filename = p;
+ p += ALIGN(filename.size() + 1, 64);
+ CHECK_EQ(p, end);
+}
+
+void BuildIdRecord::DumpData(size_t indent) const {
+ PrintIndented(indent, "pid %u\n", pid);
+ PrintIndented(indent, "build_id 0x");
+ for (auto& c : build_id) {
+ printf("%02x", c);
+ }
+ printf("\n");
+ PrintIndented(indent, "filename %s\n", filename.c_str());
+}
+
+std::vector<char> BuildIdRecord::BinaryFormat() const {
+ std::vector<char> buf(header.size);
+ char* p = buf.data();
+ MoveToBinaryFormat(header, p);
+ MoveToBinaryFormat(pid, p);
+ memcpy(p, build_id.data(), build_id.size());
+ p += ALIGN(build_id.size(), 8);
+ strcpy(p, filename.c_str());
+ p += ALIGN(filename.size() + 1, 64);
+ return buf;
+}
+
std::unique_ptr<const Record> ReadRecordFromBuffer(const perf_event_attr& attr,
const perf_event_header* pheader) {
switch (pheader->type) {
@@ -350,3 +383,16 @@ CommRecord CreateCommRecord(const perf_event_attr& attr, uint32_t pid, uint32_t
ALIGN(record.comm.size() + 1, 8) + sample_id_size;
return record;
}
+
+BuildIdRecord CreateBuildIdRecord(bool in_kernel, pid_t pid, const BuildId& build_id,
+ const std::string& filename) {
+ BuildIdRecord record;
+ record.header.type = PERF_RECORD_BUILD_ID;
+ record.header.misc = (in_kernel ? PERF_RECORD_MISC_KERNEL : PERF_RECORD_MISC_USER);
+ record.pid = pid;
+ record.build_id = build_id;
+ record.filename = filename;
+ record.header.size = sizeof(record.header) + sizeof(record.pid) +
+ ALIGN(record.build_id.size(), 8) + ALIGN(filename.size() + 1, 64);
+ return record;
+}
diff --git a/simpleperf/record.h b/simpleperf/record.h
index 4d62784c..83f60db9 100644
--- a/simpleperf/record.h
+++ b/simpleperf/record.h
@@ -20,6 +20,7 @@
#include <string>
#include <vector>
+#include "build_id.h"
#include "perf_event.h"
struct KernelMmap;
@@ -129,8 +130,10 @@ struct MmapRecord : public Record {
}
MmapRecord(const perf_event_attr& attr, const perf_event_header* pheader);
- void DumpData(size_t indent) const override;
std::vector<char> BinaryFormat() const;
+
+ protected:
+ void DumpData(size_t indent) const override;
};
struct CommRecord : public Record {
@@ -143,8 +146,10 @@ struct CommRecord : public Record {
}
CommRecord(const perf_event_attr& attr, const perf_event_header* pheader);
- void DumpData(size_t indent) const override;
std::vector<char> BinaryFormat() const;
+
+ protected:
+ void DumpData(size_t indent) const override;
};
struct ExitRecord : public Record {
@@ -155,6 +160,8 @@ struct ExitRecord : public Record {
} data;
ExitRecord(const perf_event_attr& attr, const perf_event_header* pheader);
+
+ protected:
void DumpData(size_t indent) const override;
};
@@ -171,6 +178,24 @@ struct SampleRecord : public Record {
PerfSamplePeriodType period_data; // Valid if PERF_SAMPLE_PERIOD.
SampleRecord(const perf_event_attr& attr, const perf_event_header* pheader);
+
+ protected:
+ void DumpData(size_t indent) const override;
+};
+
+// BuildIdRecord is defined in user-space, stored in BuildId feature section in record file.
+struct BuildIdRecord : public Record {
+ uint32_t pid;
+ BuildId build_id;
+ std::string filename;
+
+ BuildIdRecord() {
+ }
+
+ BuildIdRecord(const perf_event_header* pheader);
+ std::vector<char> BinaryFormat() const;
+
+ protected:
void DumpData(size_t indent) const override;
};
@@ -181,4 +206,6 @@ MmapRecord CreateMmapRecord(const perf_event_attr& attr, bool in_kernel, uint32_
const std::string& filename);
CommRecord CreateCommRecord(const perf_event_attr& attr, uint32_t pid, uint32_t tid,
const std::string& comm);
+BuildIdRecord CreateBuildIdRecord(bool in_kernel, pid_t pid, const BuildId& build_id,
+ const std::string& filename);
#endif // SIMPLE_PERF_RECORD_H_
diff --git a/simpleperf/record_equal_test.h b/simpleperf/record_equal_test.h
index 45b0752c..03768dc5 100644
--- a/simpleperf/record_equal_test.h
+++ b/simpleperf/record_equal_test.h
@@ -24,6 +24,12 @@ static void CheckCommRecordDataEqual(const CommRecord& r1, const CommRecord& r2)
ASSERT_EQ(r1.comm, r2.comm);
}
+static void CheckBuildIdRecordDataEqual(const BuildIdRecord& r1, const BuildIdRecord& r2) {
+ ASSERT_EQ(r1.pid, r2.pid);
+ ASSERT_EQ(r1.build_id, r2.build_id);
+ ASSERT_EQ(r1.filename, r2.filename);
+}
+
static void CheckRecordEqual(const Record& r1, const Record& r2) {
ASSERT_EQ(0, memcmp(&r1.header, &r2.header, sizeof(r1.header)));
ASSERT_EQ(0, memcmp(&r1.sample_id, &r2.sample_id, sizeof(r1.sample_id)));
@@ -31,5 +37,8 @@ static void CheckRecordEqual(const Record& r1, const Record& r2) {
CheckMmapRecordDataEqual(static_cast<const MmapRecord&>(r1), static_cast<const MmapRecord&>(r2));
} else if (r1.header.type == PERF_RECORD_COMM) {
CheckCommRecordDataEqual(static_cast<const CommRecord&>(r1), static_cast<const CommRecord&>(r2));
+ } else if (r1.header.type == PERF_RECORD_BUILD_ID) {
+ CheckBuildIdRecordDataEqual(static_cast<const BuildIdRecord&>(r1),
+ static_cast<const BuildIdRecord&>(r2));
}
}
diff --git a/simpleperf/record_file.cpp b/simpleperf/record_file.cpp
index 784dc4e1..54a4ddaa 100644
--- a/simpleperf/record_file.cpp
+++ b/simpleperf/record_file.cpp
@@ -21,6 +21,7 @@
#include <sys/mman.h>
#include <unistd.h>
#include <set>
+#include <vector>
#include <base/logging.h>
@@ -48,7 +49,14 @@ std::unique_ptr<RecordFileWriter> RecordFileWriter::CreateInstance(
}
RecordFileWriter::RecordFileWriter(const std::string& filename, FILE* fp)
- : filename_(filename), record_fp_(fp), data_section_offset_(0), data_section_size_(0) {
+ : filename_(filename),
+ record_fp_(fp),
+ attr_section_offset_(0),
+ attr_section_size_(0),
+ data_section_offset_(0),
+ data_section_size_(0),
+ feature_count_(0),
+ current_feature_index_(0) {
}
RecordFileWriter::~RecordFileWriter() {
@@ -121,6 +129,152 @@ bool RecordFileWriter::Write(const void* buf, size_t len) {
return true;
}
+void RecordFileWriter::GetHitModulesInBuffer(const char* p, const char* end,
+ std::vector<std::string>* hit_kernel_modules,
+ std::vector<std::string>* hit_user_files) {
+ std::vector<std::unique_ptr<const Record>> kernel_mmaps;
+ std::vector<std::unique_ptr<const Record>> user_mmaps;
+ std::set<std::string> hit_kernel_set;
+ std::set<std::string> hit_user_set;
+
+ while (p < end) {
+ auto header = reinterpret_cast<const perf_event_header*>(p);
+ CHECK_LE(p + header->size, end);
+ p += header->size;
+ std::unique_ptr<const Record> record = ReadRecordFromBuffer(event_attr_, header);
+ CHECK(record != nullptr);
+ if (record->header.type == PERF_RECORD_MMAP) {
+ if (record->header.misc & PERF_RECORD_MISC_KERNEL) {
+ kernel_mmaps.push_back(std::move(record));
+ } else {
+ user_mmaps.push_back(std::move(record));
+ }
+ } else if (record->header.type == PERF_RECORD_SAMPLE) {
+ auto& r = *static_cast<const SampleRecord*>(record.get());
+ if (!(r.sample_type & PERF_SAMPLE_IP) || !(r.sample_type & PERF_SAMPLE_TID)) {
+ continue;
+ }
+ uint32_t pid = r.tid_data.pid;
+ uint64_t ip = r.ip_data.ip;
+ if (r.header.misc & PERF_RECORD_MISC_KERNEL) {
+ // Loop from back to front, because new MmapRecords are inserted at the end of the mmaps,
+ // and we want to match the newest one.
+ for (auto it = kernel_mmaps.rbegin(); it != kernel_mmaps.rend(); ++it) {
+ auto& m_record = *reinterpret_cast<const MmapRecord*>(it->get());
+ if (ip >= m_record.data.addr && ip < m_record.data.addr + m_record.data.len) {
+ hit_kernel_set.insert(m_record.filename);
+ break;
+ }
+ }
+ } else {
+ for (auto it = user_mmaps.rbegin(); it != user_mmaps.rend(); ++it) {
+ auto& m_record = *reinterpret_cast<const MmapRecord*>(it->get());
+ if (pid == m_record.data.pid && ip >= m_record.data.addr &&
+ ip < m_record.data.addr + m_record.data.len) {
+ hit_user_set.insert(m_record.filename);
+ break;
+ }
+ }
+ }
+ }
+ }
+ hit_kernel_modules->clear();
+ hit_kernel_modules->insert(hit_kernel_modules->begin(), hit_kernel_set.begin(),
+ hit_kernel_set.end());
+ hit_user_files->clear();
+ hit_user_files->insert(hit_user_files->begin(), hit_user_set.begin(), hit_user_set.end());
+}
+
+bool RecordFileWriter::GetHitModules(std::vector<std::string>* hit_kernel_modules,
+ std::vector<std::string>* hit_user_files) {
+ if (fflush(record_fp_) != 0) {
+ PLOG(ERROR) << "fflush() failed";
+ return false;
+ }
+ if (fseek(record_fp_, 0, SEEK_END) == -1) {
+ PLOG(ERROR) << "fseek() failed";
+ return false;
+ }
+ long file_size = ftell(record_fp_);
+ if (file_size == -1) {
+ PLOG(ERROR) << "ftell() failed";
+ return false;
+ }
+ size_t mmap_len = file_size;
+ void* mmap_addr = mmap(nullptr, mmap_len, PROT_READ, MAP_SHARED, fileno(record_fp_), 0);
+ if (mmap_addr == MAP_FAILED) {
+ PLOG(ERROR) << "mmap() failed";
+ return false;
+ }
+ const char* data_section_p = reinterpret_cast<const char*>(mmap_addr) + data_section_offset_;
+ const char* data_section_end = data_section_p + data_section_size_;
+ GetHitModulesInBuffer(data_section_p, data_section_end, hit_kernel_modules, hit_user_files);
+
+ if (munmap(mmap_addr, mmap_len) == -1) {
+ PLOG(ERROR) << "munmap() failed";
+ return false;
+ }
+ return true;
+}
+
+bool RecordFileWriter::WriteFeatureHeader(size_t feature_count) {
+ feature_count_ = feature_count;
+ current_feature_index_ = 0;
+ uint64_t feature_header_size = feature_count * sizeof(SectionDesc);
+
+ // Reserve enough space in the record file for the feature header.
+ std::vector<unsigned char> zero_data(feature_header_size);
+ if (fseek(record_fp_, data_section_offset_ + data_section_size_, SEEK_SET) == -1) {
+ PLOG(ERROR) << "fseek() failed";
+ return false;
+ }
+ return Write(zero_data.data(), zero_data.size());
+}
+
+bool RecordFileWriter::WriteBuildIdFeature(const std::vector<BuildIdRecord>& build_id_records) {
+ if (current_feature_index_ >= feature_count_) {
+ return false;
+ }
+ // Always write features at the end of the file.
+ if (fseek(record_fp_, 0, SEEK_END) == -1) {
+ PLOG(ERROR) << "fseek() failed";
+ return false;
+ }
+ long section_start = ftell(record_fp_);
+ if (section_start == -1) {
+ PLOG(ERROR) << "ftell() failed";
+ return false;
+ }
+ for (auto& record : build_id_records) {
+ std::vector<char> data = record.BinaryFormat();
+ if (!Write(data.data(), data.size())) {
+ return false;
+ }
+ }
+ long section_end = ftell(record_fp_);
+ if (section_end == -1) {
+ return false;
+ }
+
+ // Write feature section descriptor for build_id feature.
+ SectionDesc desc;
+ desc.offset = section_start;
+ desc.size = section_end - section_start;
+ uint64_t feature_offset = data_section_offset_ + data_section_size_;
+ if (fseek(record_fp_, feature_offset + current_feature_index_ * sizeof(SectionDesc), SEEK_SET) ==
+ -1) {
+ PLOG(ERROR) << "fseek() failed";
+ return false;
+ }
+ if (fwrite(&desc, sizeof(SectionDesc), 1, record_fp_) != 1) {
+ PLOG(ERROR) << "fwrite() failed";
+ return false;
+ }
+ ++current_feature_index_;
+ features_.push_back(FEAT_BUILD_ID);
+ return true;
+}
+
bool RecordFileWriter::WriteFileHeader() {
FileHeader header;
memset(&header, 0, sizeof(header));
@@ -261,3 +415,22 @@ std::vector<std::unique_ptr<const Record>> RecordFileReader::DataSection() {
}
return result;
}
+
+std::vector<SectionDesc> RecordFileReader::FeatureSectionDescriptors() {
+ std::vector<SectionDesc> result;
+ const struct FileHeader* header = FileHeader();
+ size_t feature_count = 0;
+ for (size_t i = 0; i < sizeof(header->features); ++i) {
+ for (size_t j = 0; j < 8; ++j) {
+ if (header->features[i] & (1 << j)) {
+ ++feature_count;
+ }
+ }
+ }
+ uint64_t feature_section_offset = header->data.offset + header->data.size;
+ const SectionDesc* p = reinterpret_cast<const SectionDesc*>(mmap_addr_ + feature_section_offset);
+ for (size_t i = 0; i < feature_count; ++i) {
+ result.push_back(*p++);
+ }
+ return result;
+}
diff --git a/simpleperf/record_file.h b/simpleperf/record_file.h
index cc213d57..694486c0 100644
--- a/simpleperf/record_file.h
+++ b/simpleperf/record_file.h
@@ -25,10 +25,10 @@
#include <base/macros.h>
#include "perf_event.h"
+#include "record.h"
#include "record_file_format.h"
class EventFd;
-struct Record;
// RecordFileWriter writes to a perf record file, like perf.data.
class RecordFileWriter {
@@ -45,6 +45,14 @@ class RecordFileWriter {
return WriteData(data.data(), data.size());
}
+ // Use MmapRecords and SampleRecords in record file to conclude which modules/files were executing
+ // at sample times.
+ bool GetHitModules(std::vector<std::string>* hit_kernel_modules,
+ std::vector<std::string>* hit_user_files);
+
+ bool WriteFeatureHeader(size_t feature_count);
+ bool WriteBuildIdFeature(const std::vector<BuildIdRecord>& build_id_records);
+
// Normally, Close() should be called after writing. But if something
// wrong happens and we need to finish in advance, the destructor
// will take care of calling Close().
@@ -54,6 +62,9 @@ class RecordFileWriter {
RecordFileWriter(const std::string& filename, FILE* fp);
bool WriteAttrSection(const perf_event_attr& event_attr,
const std::vector<std::unique_ptr<EventFd>>& event_fds);
+ void GetHitModulesInBuffer(const char* p, const char* end,
+ std::vector<std::string>* hit_kernel_modules,
+ std::vector<std::string>* hit_user_files);
bool WriteFileHeader();
bool Write(const void* buf, size_t len);
@@ -67,6 +78,8 @@ class RecordFileWriter {
uint64_t data_section_size_;
std::vector<int> features_;
+ int feature_count_;
+ int current_feature_index_;
DISALLOW_COPY_AND_ASSIGN(RecordFileWriter);
};
@@ -82,6 +95,10 @@ class RecordFileReader {
std::vector<const PerfFileFormat::FileAttr*> AttrSection();
std::vector<uint64_t> IdsForAttr(const PerfFileFormat::FileAttr* attr);
std::vector<std::unique_ptr<const Record>> DataSection();
+ std::vector<PerfFileFormat::SectionDesc> FeatureSectionDescriptors();
+ const char* DataAtOffset(uint64_t offset) {
+ return mmap_addr_ + offset;
+ }
bool Close();
private:
diff --git a/simpleperf/record_file_test.cpp b/simpleperf/record_file_test.cpp
index df138def..fffaa2a9 100644
--- a/simpleperf/record_file_test.cpp
+++ b/simpleperf/record_file_test.cpp
@@ -33,6 +33,7 @@ class RecordFileTest : public ::testing::Test {
virtual void SetUp() {
filename = "temporary.record_file";
const EventType* event_type = EventTypeFactory::FindEventTypeByName("cpu-cycles");
+ ASSERT_TRUE(event_type != nullptr);
event_attr = CreateDefaultPerfEventAttr(*event_type);
std::unique_ptr<EventFd> event_fd = EventFd::OpenEventFileForProcess(event_attr, getpid());
ASSERT_TRUE(event_fd != nullptr);
@@ -50,10 +51,19 @@ TEST_F(RecordFileTest, smoke) {
RecordFileWriter::CreateInstance(filename, event_attr, event_fds);
ASSERT_TRUE(writer != nullptr);
- // Write Data section.
+ // Write data section.
MmapRecord mmap_record =
CreateMmapRecord(event_attr, true, 1, 1, 0x1000, 0x2000, 0x3000, "mmap_record_example");
ASSERT_TRUE(writer->WriteData(mmap_record.BinaryFormat()));
+
+ // Write feature section.
+ ASSERT_TRUE(writer->WriteFeatureHeader(1));
+ BuildId build_id;
+ for (size_t i = 0; i < build_id.size(); ++i) {
+ build_id[i] = i;
+ }
+ BuildIdRecord build_id_record = CreateBuildIdRecord(false, getpid(), build_id, "init");
+ ASSERT_TRUE(writer->WriteBuildIdFeature({build_id_record}));
ASSERT_TRUE(writer->Close());
// Read from a record file.
@@ -73,5 +83,15 @@ TEST_F(RecordFileTest, smoke) {
ASSERT_EQ(mmap_record.header.type, records[0]->header.type);
CheckRecordEqual(mmap_record, *records[0]);
+ // Read and check feature section.
+ ASSERT_TRUE(file_header->features[FEAT_BUILD_ID / 8] & (1 << (FEAT_BUILD_ID % 8)));
+ std::vector<SectionDesc> sections = reader->FeatureSectionDescriptors();
+ ASSERT_EQ(1u, sections.size());
+ const perf_event_header* header =
+ reinterpret_cast<const perf_event_header*>(reader->DataAtOffset(sections[0].offset));
+ ASSERT_TRUE(header != nullptr);
+ ASSERT_EQ(sections[0].size, header->size);
+ CheckRecordEqual(build_id_record, BuildIdRecord(header));
+
ASSERT_TRUE(reader->Close());
}