diff options
author | Than McIntosh <thanm@google.com> | 2016-02-03 12:00:41 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2016-02-03 12:00:41 +0000 |
commit | 4a5dafcc3093d55fc9d135b9a18c73bd96bcf3b1 (patch) | |
tree | f7e3c2fc8c7adfb96028684664e0841a476f546e | |
parent | 1a5e41d3a1a7c1289503643d88748516649383ed (diff) | |
parent | f831e6d12ca2298cb34b155a9d91ef002d0482f3 (diff) | |
download | extras-4a5dafcc3093d55fc9d135b9a18c73bd96bcf3b1.tar.gz |
Merge "Support profiling of shared libs embedded in APKs."
-rw-r--r-- | simpleperf/Android.mk | 15 | ||||
-rw-r--r-- | simpleperf/cmd_record.cpp | 74 | ||||
-rw-r--r-- | simpleperf/read_apk.cpp | 179 | ||||
-rw-r--r-- | simpleperf/read_apk.h | 99 | ||||
-rw-r--r-- | simpleperf/read_apk_test.cpp | 40 | ||||
-rw-r--r-- | simpleperf/read_elf.cpp | 74 | ||||
-rw-r--r-- | simpleperf/read_elf.h | 16 | ||||
-rw-r--r-- | simpleperf/record.cpp | 12 | ||||
-rw-r--r-- | simpleperf/record.h | 1 | ||||
-rw-r--r-- | simpleperf/test_inputs/Android.mk | 20 | ||||
-rw-r--r-- | simpleperf/test_inputs/fibonacci.jar | bin | 0 -> 1205 bytes | |||
-rw-r--r-- | simpleperf/test_inputs/has_embedded_native_libs.apk | bin | 0 -> 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 Binary files differnew file mode 100644 index 00000000..df57e40f --- /dev/null +++ b/simpleperf/test_inputs/fibonacci.jar diff --git a/simpleperf/test_inputs/has_embedded_native_libs.apk b/simpleperf/test_inputs/has_embedded_native_libs.apk Binary files differnew file mode 100644 index 00000000..2a1924cd --- /dev/null +++ b/simpleperf/test_inputs/has_embedded_native_libs.apk |