summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThan McIntosh <thanm@google.com>2016-02-03 12:00:41 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2016-02-03 12:00:41 +0000
commit4a5dafcc3093d55fc9d135b9a18c73bd96bcf3b1 (patch)
treef7e3c2fc8c7adfb96028684664e0841a476f546e
parent1a5e41d3a1a7c1289503643d88748516649383ed (diff)
parentf831e6d12ca2298cb34b155a9d91ef002d0482f3 (diff)
downloadextras-4a5dafcc3093d55fc9d135b9a18c73bd96bcf3b1.tar.gz
Merge "Support profiling of shared libs embedded in APKs."
-rw-r--r--simpleperf/Android.mk15
-rw-r--r--simpleperf/cmd_record.cpp74
-rw-r--r--simpleperf/read_apk.cpp179
-rw-r--r--simpleperf/read_apk.h99
-rw-r--r--simpleperf/read_apk_test.cpp40
-rw-r--r--simpleperf/read_elf.cpp74
-rw-r--r--simpleperf/read_elf.h16
-rw-r--r--simpleperf/record.cpp12
-rw-r--r--simpleperf/record.h1
-rw-r--r--simpleperf/test_inputs/Android.mk20
-rw-r--r--simpleperf/test_inputs/fibonacci.jarbin0 -> 1205 bytes
-rw-r--r--simpleperf/test_inputs/has_embedded_native_libs.apkbin0 -> 723220 bytes
12 files changed, 516 insertions, 14 deletions
diff --git a/simpleperf/Android.mk b/simpleperf/Android.mk
index a0736465..17676b35 100644
--- a/simpleperf/Android.mk
+++ b/simpleperf/Android.mk
@@ -34,11 +34,19 @@ simpleperf_shared_libraries_target := \
libbacktrace \
libbacktrace_offline \
libbase \
+ liblog \
+ libutils \
libLLVM \
+simpleperf_static_libraries_target := \
+ libziparchive \
+
simpleperf_shared_libraries_host := libbase
-simpleperf_shared_libraries_host_linux := libbacktrace libbacktrace_offline
+simpleperf_shared_libraries_host_linux := \
+ libbacktrace \
+ libbacktrace_offline \
+ libziparchive-host \
simpleperf_shared_libraries_host_darwin := libLLVM
@@ -74,6 +82,7 @@ libsimpleperf_src_files_linux := \
environment.cpp \
event_fd.cpp \
event_selection_set.cpp \
+ read_apk.cpp \
record_file_writer.cpp \
workload.cpp \
@@ -135,6 +144,7 @@ LOCAL_CPPFLAGS := $(simpleperf_cppflags_target)
LOCAL_SRC_FILES := main.cpp
LOCAL_WHOLE_STATIC_LIBRARIES := libsimpleperf
LOCAL_SHARED_LIBRARIES := $(simpleperf_shared_libraries_target)
+LOCAL_STATIC_LIBRARIES := $(simpleperf_static_libraries_target)
LOCAL_MULTILIB := first
include $(BUILD_EXECUTABLE)
@@ -173,6 +183,7 @@ simpleperf_unit_test_src_files_linux := \
cmd_stat_test.cpp \
environment_test.cpp \
read_elf_test.cpp \
+ read_apk_test.cpp \
record_file_test.cpp \
workload_test.cpp \
@@ -187,6 +198,7 @@ LOCAL_SRC_FILES := \
LOCAL_WHOLE_STATIC_LIBRARIES := libsimpleperf
LOCAL_SHARED_LIBRARIES := $(simpleperf_shared_libraries_target)
+LOCAL_STATIC_LIBRARIES += $(simpleperf_static_libraries_target)
LOCAL_MULTILIB := first
include $(BUILD_NATIVE_TEST)
@@ -224,6 +236,7 @@ LOCAL_CPPFLAGS := $(simpleperf_cppflags_target)
LOCAL_SRC_FILES := $(simpleperf_cpu_hotplug_test_src_files)
LOCAL_WHOLE_STATIC_LIBRARIES := libsimpleperf
LOCAL_SHARED_LIBRARIES := $(simpleperf_shared_libraries_target)
+LOCAL_STATIC_LIBRARIES := $(simpleperf_static_libraries_target)
LOCAL_MULTILIB := first
include $(BUILD_NATIVE_TEST)
diff --git a/simpleperf/cmd_record.cpp b/simpleperf/cmd_record.cpp
index ee738c00..edeb64cf 100644
--- a/simpleperf/cmd_record.cpp
+++ b/simpleperf/cmd_record.cpp
@@ -32,6 +32,7 @@
#include "environment.h"
#include "event_selection_set.h"
#include "event_type.h"
+#include "read_apk.h"
#include "read_elf.h"
#include "record.h"
#include "record_file.h"
@@ -149,7 +150,9 @@ class RecordCommand : public Command {
bool PostUnwind(const std::vector<std::string>& args);
bool DumpAdditionalFeatures(const std::vector<std::string>& args);
bool DumpBuildIdFeature();
- void CollectHitFileInfo(const Record* record);
+ void CollectHitFileInfo(Record* record);
+ std::pair<std::string, uint64_t> TestForEmbeddedElf(Dso *dso, uint64_t pgoff);
+
bool use_sample_freq_; // Use sample_freq_ when true, otherwise using sample_period_.
uint64_t sample_freq_; // Sample 'sample_freq_' times per second.
@@ -177,7 +180,8 @@ class RecordCommand : public Command {
std::unique_ptr<RecordFileWriter> record_file_writer_;
std::set<std::string> hit_kernel_modules_;
- std::set<std::string> hit_user_files_;
+ std::set<std::pair<std::string, uint64_t> > hit_user_files_;
+ ApkInspector apk_inspector_;
std::unique_ptr<ScopedSignalHandler> scoped_signal_handler_;
};
@@ -764,10 +768,26 @@ bool RecordCommand::DumpBuildIdFeature() {
}
}
// Add build_ids for user elf files.
- for (auto& filename : hit_user_files_) {
+ for (auto& dso_origin : hit_user_files_) {
+ auto& filename = dso_origin.first;
+ auto& offset = dso_origin.second;
if (filename == DEFAULT_EXECNAME_FOR_THREAD_MMAP) {
continue;
}
+ EmbeddedElf *ee = apk_inspector_.FindElfInApkByMmapOffset(filename, offset);
+ if (ee) {
+ if (!GetBuildIdFromEmbeddedElfFile(filename,
+ ee->entry_offset(),
+ ee->entry_size(),
+ &build_id)) {
+ LOG(DEBUG) << "can't read build_id from archive file " << filename
+ << "entry " << ee->entry_name();
+ continue;
+ }
+ std::string ee_filename = filename + "!" + ee->entry_name();
+ build_id_records.push_back(CreateBuildIdRecord(false, UINT_MAX, build_id, ee_filename));
+ continue;
+ }
if (!GetBuildIdFromElfFile(filename, &build_id)) {
LOG(DEBUG) << "can't read build_id from file " << filename;
continue;
@@ -780,20 +800,62 @@ bool RecordCommand::DumpBuildIdFeature() {
return true;
}
-void RecordCommand::CollectHitFileInfo(const Record* record) {
+void RecordCommand::CollectHitFileInfo(Record* record) {
if (record->type() == PERF_RECORD_SAMPLE) {
- auto r = *static_cast<const SampleRecord*>(record);
+ auto r = *static_cast<SampleRecord*>(record);
bool in_kernel = ((r.header.misc & PERF_RECORD_MISC_CPUMODE_MASK) == PERF_RECORD_MISC_KERNEL);
const ThreadEntry* thread = thread_tree_.FindThreadOrNew(r.tid_data.pid, r.tid_data.tid);
const MapEntry* map = thread_tree_.FindMap(thread, r.ip_data.ip, in_kernel);
if (in_kernel) {
hit_kernel_modules_.insert(map->dso->Path());
} else {
- hit_user_files_.insert(map->dso->Path());
+ auto apair = std::make_pair(map->dso->Path(), map->pgoff);
+ hit_user_files_.insert(apair);
+ }
+ }
+ if (record->type() == PERF_RECORD_MMAP) {
+ MmapRecord& r = *static_cast<MmapRecord*>(record);
+ bool in_kernel = ((r.header.misc & PERF_RECORD_MISC_CPUMODE_MASK) == PERF_RECORD_MISC_KERNEL);
+ if (!in_kernel) {
+ const ThreadEntry* thread = thread_tree_.FindThreadOrNew(r.data.pid, r.data.tid);
+ const MapEntry* map = thread_tree_.FindMap(thread, r.data.addr, in_kernel);
+ if (map->pgoff != 0u) {
+ std::pair<std::string, uint64_t> ee_info = TestForEmbeddedElf(map->dso, map->pgoff);
+ if (!ee_info.first.empty()) {
+ // For the case of a shared library "foobar.so" embedded
+ // inside an APK, we rewrite the original MMAP from
+ // ["path.apk" offset=X] to ["path.apk!foobar.so" offset=W]
+ // so as to make the library name explicit. This update is
+ // done here (as part of the record operation) as opposed to
+ // on the host during the report, since we want to report
+ // the correct library name even if the the APK in question
+ // is not present on the host. The new offset W is
+ // calculated to be with respect to the start of foobar.so,
+ // not to the start of path.apk.
+ const std::string& entry_name = ee_info.first;
+ uint64_t new_offset = ee_info.second;
+ std::string new_filename = r.filename + "!" + entry_name;
+ UpdateMmapRecord(&r, new_filename, new_offset);
+ }
+ }
}
}
}
+std::pair<std::string, uint64_t> RecordCommand::TestForEmbeddedElf(Dso *dso, uint64_t pgoff)
+{
+ // Examine the DSO to determine whether it corresponds to an ELF
+ // file embedded in an APK.
+ std::string ee_name;
+ EmbeddedElf *ee = apk_inspector_.FindElfInApkByMmapOffset(dso->Path(), pgoff);
+ if (ee) {
+ // Compute new offset relative to start of elf in APK.
+ uint64_t elf_offset = pgoff - ee->entry_offset();
+ return std::make_pair(ee->entry_name(), elf_offset);
+ }
+ return std::make_pair(std::string(), 0u);
+}
+
__attribute__((constructor)) static void RegisterRecordCommand() {
RegisterCommand("record", [] { return std::unique_ptr<Command>(new RecordCommand()); });
}
diff --git a/simpleperf/read_apk.cpp b/simpleperf/read_apk.cpp
new file mode 100644
index 00000000..5e8077e9
--- /dev/null
+++ b/simpleperf/read_apk.cpp
@@ -0,0 +1,179 @@
+/*
+**
+** Copyright 2016, 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_apk.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <iostream>
+#include <map>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+
+#include "read_elf.h"
+#include "utils.h"
+#include "ziparchive/zip_archive.h"
+
+bool IsValidJarOrApkPath(const std::string& filename) {
+ static const char zip_preamble[] = {0x50, 0x4b, 0x03, 0x04 };
+ if (!IsRegularFile(filename)) {
+ return false;
+ }
+ FILE* fp = fopen(filename.c_str(), "reb");
+ if (fp == nullptr) {
+ return false;
+ }
+ char buf[4];
+ if (fread(buf, 4, 1, fp) != 1) {
+ fclose(fp);
+ return false;
+ }
+ fclose(fp);
+ return memcmp(buf, zip_preamble, 4) == 0;
+}
+
+class ArchiveHelper {
+ public:
+ explicit ArchiveHelper(int fd) : valid_(false) {
+ int rc = OpenArchiveFd(fd, "", &handle_, false);
+ if (rc == 0) {
+ valid_ = true;
+ }
+ }
+ ~ArchiveHelper() {
+ if (valid_) {
+ CloseArchive(handle_);
+ }
+ }
+ bool valid() const { return valid_; }
+ ZipArchiveHandle &archive_handle() { return handle_; }
+
+ private:
+ ZipArchiveHandle handle_;
+ bool valid_;
+};
+
+// First component of pair is APK file path, second is offset into APK
+typedef std::pair<std::string, size_t> ApkOffset;
+
+class ApkInspectorImpl {
+ public:
+ EmbeddedElf *FindElfInApkByMmapOffset(const std::string& apk_path,
+ size_t mmap_offset);
+ private:
+ std::vector<EmbeddedElf> embedded_elf_files_;
+ // Value is either 0 (no elf) or 1-based slot in array above.
+ std::map<ApkOffset, uint32_t> cache_;
+};
+
+EmbeddedElf *ApkInspectorImpl::FindElfInApkByMmapOffset(const std::string& apk_path,
+ size_t mmap_offset)
+{
+ // Already in cache?
+ ApkOffset ami(apk_path, mmap_offset);
+ auto it = cache_.find(ami);
+ if (it != cache_.end()) {
+ uint32_t idx = it->second;
+ return (idx ? &embedded_elf_files_[idx-1] : nullptr);
+ }
+ cache_[ami] = 0u;
+
+ // Crack open the apk(zip) file and take a look.
+ if (! IsValidJarOrApkPath(apk_path)) {
+ return nullptr;
+ }
+ FileHelper fhelper(apk_path.c_str());
+ if (fhelper.fd() == -1) {
+ return nullptr;
+ }
+
+ ArchiveHelper ahelper(fhelper.fd());
+ if (!ahelper.valid()) {
+ return nullptr;
+ }
+ ZipArchiveHandle &handle = ahelper.archive_handle();
+
+ // Iterate through the zip file. Look for a zip entry corresponding
+ // to an uncompressed blob whose range intersects with the mmap
+ // offset we're interested in.
+ void* iteration_cookie;
+ if (StartIteration(handle, &iteration_cookie, nullptr, nullptr) < 0) {
+ return nullptr;
+ }
+ ZipEntry zentry;
+ ZipString zname;
+ bool found = false;
+ int zrc;
+ off64_t mmap_off64 = mmap_offset;
+ while ((zrc = Next(iteration_cookie, &zentry, &zname)) == 0) {
+ if (zentry.method == kCompressStored &&
+ mmap_off64 >= zentry.offset &&
+ mmap_off64 < zentry.offset + zentry.uncompressed_length) {
+ // Found.
+ found = true;
+ break;
+ }
+ }
+ EndIteration(iteration_cookie);
+ if (!found) {
+ return nullptr;
+ }
+
+ // We found something in the zip file at the right spot. Is it an ELF?
+ if (lseek(fhelper.fd(), zentry.offset, SEEK_SET) != zentry.offset) {
+ PLOG(ERROR) << "lseek() failed in " << apk_path << " offset " << zentry.offset;
+ return nullptr;
+ }
+ std::string entry_name;
+ entry_name.resize(zname.name_length,'\0');
+ memcpy(&entry_name[0], zname.name, zname.name_length);
+ if (!IsValidElfFile(fhelper.fd())) {
+ LOG(ERROR) << "problems reading ELF from in " << apk_path << " entry '"
+ << entry_name << "'";
+ return nullptr;
+ }
+
+ // Elf found: add EmbeddedElf to vector, update cache.
+ EmbeddedElf ee(apk_path, entry_name, zentry.offset, zentry.uncompressed_length);
+ embedded_elf_files_.push_back(ee);
+ unsigned idx = embedded_elf_files_.size();
+ cache_[ami] = idx;
+ return &embedded_elf_files_[idx-1];
+}
+
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+ApkInspector::ApkInspector()
+ : impl_(new ApkInspectorImpl())
+{
+}
+
+ApkInspector::~ApkInspector()
+{
+}
+
+EmbeddedElf *ApkInspector::FindElfInApkByMmapOffset(const std::string& apk_path,
+ size_t mmap_offset)
+{
+ return impl_->FindElfInApkByMmapOffset(apk_path, mmap_offset);
+}
diff --git a/simpleperf/read_apk.h b/simpleperf/read_apk.h
new file mode 100644
index 00000000..c35cac73
--- /dev/null
+++ b/simpleperf/read_apk.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2016 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_APK_H_
+#define SIMPLE_PERF_READ_APK_H_
+
+#include <memory>
+#include <string>
+
+#include <android-base/file.h>
+
+// Exposed for unit testing
+bool IsValidJarOrApkPath(const std::string& filename);
+
+// Container for info an on ELF file embedded into an APK file
+class EmbeddedElf {
+ public:
+ EmbeddedElf()
+ : entry_offset_(0)
+ , entry_size_(0)
+ {
+ }
+
+ EmbeddedElf(std::string filepath,
+ std::string entry_name,
+ size_t entry_offset,
+ size_t entry_size)
+ : filepath_(filepath)
+ , entry_name_(entry_name)
+ , entry_offset_(entry_offset)
+ , entry_size_(entry_size)
+ {
+ }
+
+ // Path to APK file
+ const std::string &filepath() const { return filepath_; }
+
+ // Entry name within zip archive
+ const std::string &entry_name() const { return entry_name_; }
+
+ // Offset of zip entry from start of containing APK file
+ size_t entry_offset() const { return entry_offset_; }
+
+ // Size of zip entry (length of embedded ELF)
+ uint32_t entry_size() const { return entry_size_; }
+
+ private:
+ std::string filepath_; // containing APK path
+ std::string entry_name_; // name of entry in zip index of embedded elf file
+ size_t entry_offset_; // offset of ELF from start of containing APK file
+ uint32_t entry_size_; // size of ELF file in zip
+};
+
+struct EmbeddedElfComparator {
+ bool operator()(const EmbeddedElf& ee1, const EmbeddedElf& ee2) {
+ int res1 = ee1.filepath().compare(ee2.filepath());
+ if (res1 != 0) {
+ return res1 < 0;
+ }
+ int res2 = ee1.entry_name().compare(ee2.entry_name());
+ if (res2 != 0) {
+ return res2 < 0;
+ }
+ return ee1.entry_offset() < ee2.entry_offset();
+ }
+};
+
+class ApkInspectorImpl;
+
+// APK inspector helper class
+class ApkInspector {
+ public:
+ ApkInspector();
+ ~ApkInspector();
+
+ // Given an APK/ZIP/JAR file and an offset into that file, if the
+ // corresponding region of the APK corresponds to an uncompressed
+ // ELF file, then return pertinent info on the ELF.
+ EmbeddedElf *FindElfInApkByMmapOffset(const std::string& apk_path,
+ size_t mmap_offset);
+
+ private:
+ std::unique_ptr<ApkInspectorImpl> impl_;
+};
+
+#endif // SIMPLE_PERF_READ_APK_H_
diff --git a/simpleperf/read_apk_test.cpp b/simpleperf/read_apk_test.cpp
new file mode 100644
index 00000000..f2234486
--- /dev/null
+++ b/simpleperf/read_apk_test.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 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_apk.h"
+
+#include <gtest/gtest.h>
+
+static const std::string fibjar = "fibonacci.jar";
+static const std::string jniapk = "has_embedded_native_libs.apk";
+
+TEST(read_apk, IsValidJarOrApkPath) {
+ ASSERT_FALSE(IsValidJarOrApkPath("/dev/zero"));
+ ASSERT_FALSE(IsValidJarOrApkPath("/proc/self/exe"));
+ ASSERT_TRUE(IsValidJarOrApkPath(fibjar));
+}
+
+TEST(read_apk, CollectEmbeddedElfInfoFromApk) {
+ ApkInspector inspector;
+ ASSERT_TRUE(inspector.FindElfInApkByMmapOffset("/dev/null", 0) == nullptr);
+ ASSERT_TRUE(inspector.FindElfInApkByMmapOffset(fibjar, 0) == nullptr);
+ ASSERT_TRUE(inspector.FindElfInApkByMmapOffset(jniapk, 0) == nullptr);
+ EmbeddedElf *ee1 = inspector.FindElfInApkByMmapOffset(jniapk, 0x91000);
+ ASSERT_TRUE(ee1 != nullptr);
+ ASSERT_EQ(ee1->entry_name(), "lib/armeabi-v7a/libframeworks_coretests_jni.so");
+ ASSERT_TRUE(ee1->entry_offset() == 593920);
+ ASSERT_TRUE(ee1->entry_size() == 13904);
+}
diff --git a/simpleperf/read_elf.cpp b/simpleperf/read_elf.cpp
index d1e1255a..a4e65ff2 100644
--- a/simpleperf/read_elf.cpp
+++ b/simpleperf/read_elf.cpp
@@ -15,9 +15,13 @@
*/
#include "read_elf.h"
+#include "read_apk.h"
#include <stdio.h>
#include <string.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
#include <algorithm>
#include <limits>
@@ -40,9 +44,29 @@
#define ELF_NOTE_GNU "GNU"
#define NT_GNU_BUILD_ID 3
-bool IsValidElfPath(const std::string& filename) {
+FileHelper::FileHelper(const char *filename) : fd_(-1)
+{
+ fd_ = TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
+}
+
+FileHelper::~FileHelper()
+{
+ if (fd_ != -1) { close(fd_); }
+}
+
+bool IsValidElfFile(int fd) {
static const char elf_magic[] = {0x7f, 'E', 'L', 'F'};
+ char buf[4];
+ size_t sz4 = 4;
+
+ ssize_t rc = TEMP_FAILURE_RETRY(read(fd, buf, sz4));
+ if (rc < 0 || rc != 4 || memcmp(buf, elf_magic, 4) != 0) {
+ return false;
+ }
+ return true;
+}
+bool IsValidElfPath(const std::string& filename) {
if (!IsRegularFile(filename)) {
return false;
}
@@ -51,13 +75,9 @@ bool IsValidElfPath(const std::string& filename) {
if (fp == nullptr) {
return false;
}
- char buf[4];
- if (fread(buf, 4, 1, fp) != 1) {
- fclose(fp);
- return false;
- }
+ bool result = IsValidElfFile(fileno(fp));
fclose(fp);
- return memcmp(buf, elf_magic, 4) == 0;
+ return result;
}
static bool GetBuildIdFromNoteSection(const char* section, size_t section_size, BuildId* build_id) {
@@ -131,6 +151,46 @@ static bool GetBuildIdFromObjectFile(llvm::object::ObjectFile* obj, BuildId* bui
return result;
}
+bool GetBuildIdFromEmbeddedElfFile(const std::string& filename,
+ uint64_t offsetInFile,
+ uint32_t sizeInFile,
+ BuildId* build_id) {
+ FileHelper opener(filename.c_str());
+ if (opener.fd() == -1) {
+ LOG(DEBUG) << "unable to open " << filename
+ << "to collect embedded ELF build id";
+ return false;
+ }
+ llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> bufferOrErr =
+ llvm::MemoryBuffer::getOpenFileSlice(opener.fd(), filename, sizeInFile,
+ offsetInFile);
+ if (std::error_code EC = bufferOrErr.getError()) {
+ LOG(DEBUG) << "MemoryBuffer::getOpenFileSlice failed opening "
+ << filename << "while collecting embedded ELF build id: "
+ << EC.message();
+ return false;
+ }
+ std::unique_ptr<llvm::MemoryBuffer> buffer = std::move(bufferOrErr.get());
+ llvm::LLVMContext *context = nullptr;
+ llvm::ErrorOr<std::unique_ptr<llvm::object::Binary>> binaryOrErr =
+ llvm::object::createBinary(buffer->getMemBufferRef(), context);
+ if (std::error_code EC = binaryOrErr.getError()) {
+ LOG(DEBUG) << "llvm::object::createBinary failed opening "
+ << filename << "while collecting embedded ELF build id: "
+ << EC.message();
+ return false;
+ }
+ std::unique_ptr<llvm::object::Binary> binary = std::move(binaryOrErr.get());
+ auto obj = llvm::dyn_cast<llvm::object::ObjectFile>(binary.get());
+ if (obj == nullptr) {
+ LOG(DEBUG) << "unable to cast to interpret contents of " << filename
+ << "at offset " << offsetInFile
+ << ": failed to cast to llvm::object::ObjectFile";
+ return false;
+ }
+ return GetBuildIdFromObjectFile(obj, build_id);
+}
+
bool GetBuildIdFromElfFile(const std::string& filename, BuildId* build_id) {
if (!IsValidElfPath(filename)) {
return false;
diff --git a/simpleperf/read_elf.h b/simpleperf/read_elf.h
index dda9a3b4..d0626b24 100644
--- a/simpleperf/read_elf.h
+++ b/simpleperf/read_elf.h
@@ -23,6 +23,10 @@
bool GetBuildIdFromNoteFile(const std::string& filename, BuildId* build_id);
bool GetBuildIdFromElfFile(const std::string& filename, BuildId* build_id);
+bool GetBuildIdFromEmbeddedElfFile(const std::string& filename,
+ uint64_t offsetInFile,
+ uint32_t sizeInFile,
+ BuildId* build_id);
// The symbol prefix used to indicate that the symbol belongs to android linker.
static const std::string linker_prefix = "__dl_";
@@ -46,8 +50,20 @@ bool ReadMinExecutableVirtualAddressFromElfFile(const std::string& filename,
const BuildId& expected_build_id,
uint64_t* min_addr);
+// Opens file in constructor, then closes file when object is destroyed.
+class FileHelper {
+ public:
+ explicit FileHelper(const char *filename);
+ ~FileHelper();
+ int fd() const { return fd_; }
+
+ private:
+ int fd_;
+};
+
// Expose the following functions for unit tests.
bool IsArmMappingSymbol(const char* name);
+bool IsValidElfFile(int fd);
bool IsValidElfPath(const std::string& filename);
#endif // SIMPLE_PERF_READ_ELF_H_
diff --git a/simpleperf/record.cpp b/simpleperf/record.cpp
index 5f853a4a..26bc588a 100644
--- a/simpleperf/record.cpp
+++ b/simpleperf/record.cpp
@@ -632,6 +632,18 @@ MmapRecord CreateMmapRecord(const perf_event_attr& attr, bool in_kernel, uint32_
return record;
}
+void UpdateMmapRecord(MmapRecord *record, const std::string& new_filename, uint64_t new_pgoff)
+{
+ size_t new_filename_size = ALIGN(new_filename.size() + 1, 8);
+ size_t old_filename_size = ALIGN(record->filename.size() + 1, 8);
+ record->data.pgoff = new_pgoff;
+ record->filename = new_filename;
+ if (new_filename_size > old_filename_size)
+ record->header.size += (new_filename_size - old_filename_size);
+ else if (new_filename_size < old_filename_size)
+ record->header.size += (old_filename_size - new_filename_size);
+}
+
CommRecord CreateCommRecord(const perf_event_attr& attr, uint32_t pid, uint32_t tid,
const std::string& comm) {
CommRecord record;
diff --git a/simpleperf/record.h b/simpleperf/record.h
index 88f3750c..26e45990 100644
--- a/simpleperf/record.h
+++ b/simpleperf/record.h
@@ -357,6 +357,7 @@ std::unique_ptr<Record> ReadRecordFromFile(const perf_event_attr& attr, FILE* fp
MmapRecord CreateMmapRecord(const perf_event_attr& attr, bool in_kernel, uint32_t pid, uint32_t tid,
uint64_t addr, uint64_t len, uint64_t pgoff,
const std::string& filename);
+void UpdateMmapRecord(MmapRecord *record, const std::string& new_filename, uint64_t new_pgoff);
CommRecord CreateCommRecord(const perf_event_attr& attr, uint32_t pid, uint32_t tid,
const std::string& comm);
ForkRecord CreateForkRecord(const perf_event_attr& attr, uint32_t pid, uint32_t tid, uint32_t ppid,
diff --git a/simpleperf/test_inputs/Android.mk b/simpleperf/test_inputs/Android.mk
new file mode 100644
index 00000000..6f565c1c
--- /dev/null
+++ b/simpleperf/test_inputs/Android.mk
@@ -0,0 +1,20 @@
+
+# some data files used by simpleperf_unit_test
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := fibonacci.jar
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := DATA
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_NATIVE_TESTS)/simpleperf_unit_test
+LOCAL_SRC_FILES := fibonacci.jar
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := has_embedded_native_libs.apk
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := DATA
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_NATIVE_TESTS)/simpleperf_unit_test
+LOCAL_SRC_FILES := has_embedded_native_libs.apk
+include $(BUILD_PREBUILT)
diff --git a/simpleperf/test_inputs/fibonacci.jar b/simpleperf/test_inputs/fibonacci.jar
new file mode 100644
index 00000000..df57e40f
--- /dev/null
+++ b/simpleperf/test_inputs/fibonacci.jar
Binary files differ
diff --git a/simpleperf/test_inputs/has_embedded_native_libs.apk b/simpleperf/test_inputs/has_embedded_native_libs.apk
new file mode 100644
index 00000000..2a1924cd
--- /dev/null
+++ b/simpleperf/test_inputs/has_embedded_native_libs.apk
Binary files differ