summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThan McIntosh <thanm@google.com>2016-02-01 19:50:20 -0500
committerThan McIntosh <thanm@google.com>2016-02-02 18:17:15 -0500
commitf831e6d12ca2298cb34b155a9d91ef002d0482f3 (patch)
tree75ef2b46be3c506e50336e2f95e21b9fbbedd30e
parentb764f45fd9758c8222bc08d7fa4944e8f7da6f40 (diff)
downloadextras-f831e6d12ca2298cb34b155a9d91ef002d0482f3.tar.gz
Support profiling of shared libs embedded in APKs.
Some APKs contain shared libraries that the linker handles by mmap'ing directly from their APKs (if the library is uncompressed and the proper manifest flag is set). With this patch simpleperf now breaks out samples on a per-li basis and reports the name of the lib within the APK. Example output: Cmdline: /system/xbin/simpleperf record -a sleep 30 Samples: 140672 of event 'cpu-cycles' Event count: 84111474884 Overhead Command Pid Tid Shared Object 90.22% b_open_from_apk 19066 19066 /data/app/com.android.frameworks.coretests.install_jni_lib_open_from_apk-2/base.apk!lib/armeabi-v7a/libgcdstuff.so 4.85% b_open_from_apk 19066 19066 /data/app/com.android.frameworks.coretests.install_jni_lib_open_from_apk-2/base.apk!lib/armeabi-v7a/libframeworks_coretests_jni.so 1.19% simpleperf 19085 19085 /system/lib/libc.so ... Bug: 22560619 Change-Id: I1e0f2e155e03b33935eac24e104c3fd7b9a7e33c
-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