Merge "adbd: add a log message on start."
diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp
index 0749fe3..f3b584c 100644
--- a/init/mount_namespace.cpp
+++ b/init/mount_namespace.cpp
@@ -240,14 +240,20 @@
// slave to the /mnt/user mount, and at the same time /mnt/installer in the
// bootstrap namespace shares a peer group with /mnt/installer in the
// default namespace.
+ // /mnt/androidwritable is similar to /mnt/installer but serves for
+ // MOUNT_EXTERNAL_ANDROID_WRITABLE apps.
if (!mkdir_recursive("/mnt/user", 0755)) return false;
if (!mkdir_recursive("/mnt/installer", 0755)) return false;
+ if (!mkdir_recursive("/mnt/androidwritable", 0755)) return false;
if (!(BindMount("/mnt/user", "/mnt/installer", true))) return false;
- // First, make /mnt/installer a slave bind mount
+ if (!(BindMount("/mnt/user", "/mnt/androidwritable", true))) return false;
+ // First, make /mnt/installer and /mnt/androidwritable a slave bind mount
if (!(MakeSlave("/mnt/installer"))) return false;
+ if (!(MakeSlave("/mnt/androidwritable"))) return false;
// Then, make it shared again - effectively creating a new peer group, that
// will be inherited by new mount namespaces.
if (!(MakeShared("/mnt/installer"))) return false;
+ if (!(MakeShared("/mnt/androidwritable"))) return false;
bootstrap_ns_fd.reset(OpenMountNamespace());
bootstrap_ns_id = GetMountNamespaceId();
diff --git a/libcutils/arch-x86/android_memset16.S b/libcutils/arch-x86/android_memset16.S
index cb2ff14..f4d497e 100755
--- a/libcutils/arch-x86/android_memset16.S
+++ b/libcutils/arch-x86/android_memset16.S
@@ -105,14 +105,16 @@
/* We loaded the jump table and adjuested EDX. Go. */ \
jmp *%ebx
- .section .gnu.linkonce.t.__x86.get_pc_thunk.bx,"ax",@progbits
+ .section .text.__x86.get_pc_thunk.bx,"axG",@progbits,__x86.get_pc_thunk.bx,comdat
.globl __x86.get_pc_thunk.bx
.hidden __x86.get_pc_thunk.bx
ALIGN (4)
.type __x86.get_pc_thunk.bx,@function
__x86.get_pc_thunk.bx:
+ cfi_startproc
movl (%esp), %ebx
ret
+ cfi_endproc
#else
# define ENTRANCE
# define RETURN_END ret
diff --git a/libcutils/arch-x86/android_memset32.S b/libcutils/arch-x86/android_memset32.S
index f4326dc..b928f6b 100755
--- a/libcutils/arch-x86/android_memset32.S
+++ b/libcutils/arch-x86/android_memset32.S
@@ -105,14 +105,16 @@
/* We loaded the jump table and adjuested EDX. Go. */ \
jmp *%ebx
- .section .gnu.linkonce.t.__x86.get_pc_thunk.bx,"ax",@progbits
+ .section .text.__x86.get_pc_thunk.bx,"axG",@progbits,__x86.get_pc_thunk.bx,comdat
.globl __x86.get_pc_thunk.bx
.hidden __x86.get_pc_thunk.bx
ALIGN (4)
.type __x86.get_pc_thunk.bx,@function
__x86.get_pc_thunk.bx:
+ cfi_startproc
movl (%esp), %ebx
ret
+ cfi_endproc
#else
# define ENTRANCE
# define RETURN_END ret
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 57c4c2e..bf7d69e 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -395,7 +395,9 @@
srcs: [
"benchmarks/unwind_benchmarks.cpp",
+ "benchmarks/ElfBenchmark.cpp",
"benchmarks/SymbolBenchmark.cpp",
+ "benchmarks/Utils.cpp",
],
data: [
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index 821e042..17470fd 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -371,7 +371,7 @@
// Look for the .debug_frame and .gnu_debugdata.
if (shdr.sh_name < sec_size) {
std::string name;
- if (memory_->ReadString(sec_offset + shdr.sh_name, &name)) {
+ if (memory_->ReadString(sec_offset + shdr.sh_name, &name, sec_size - shdr.sh_name)) {
if (name == ".debug_frame") {
debug_frame_offset_ = shdr.sh_offset;
debug_frame_size_ = shdr.sh_size;
@@ -405,7 +405,7 @@
} else if (shdr.sh_type == SHT_NOTE) {
if (shdr.sh_name < sec_size) {
std::string name;
- if (memory_->ReadString(sec_offset + shdr.sh_name, &name) &&
+ if (memory_->ReadString(sec_offset + shdr.sh_name, &name, sec_size - shdr.sh_name) &&
name == ".note.gnu.build-id") {
gnu_build_id_offset_ = shdr.sh_offset;
gnu_build_id_size_ = shdr.sh_size;
@@ -456,10 +456,11 @@
for (const auto& entry : strtabs_) {
if (entry.first == strtab_addr) {
soname_offset = entry.second + soname_offset;
- if (soname_offset >= entry.second + strtab_size) {
+ uint64_t soname_max = entry.second + strtab_size;
+ if (soname_offset >= soname_max) {
return "";
}
- if (!memory_->ReadString(soname_offset, &soname_)) {
+ if (!memory_->ReadString(soname_offset, &soname_, soname_max - soname_offset)) {
return "";
}
soname_type_ = SONAME_VALID;
@@ -608,7 +609,8 @@
}
std::string name;
if (shdr.sh_type == SHT_NOTE && shdr.sh_name < sec_size &&
- memory->ReadString(sec_offset + shdr.sh_name, &name) && name == ".note.gnu.build-id") {
+ memory->ReadString(sec_offset + shdr.sh_name, &name, sec_size - shdr.sh_name) &&
+ name == ".note.gnu.build-id") {
*build_id_offset = shdr.sh_offset;
*build_id_size = shdr.sh_size;
return true;
diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp
index fac9085..b4623fa 100644
--- a/libunwindstack/Memory.cpp
+++ b/libunwindstack/Memory.cpp
@@ -158,20 +158,30 @@
return rc == size;
}
-bool Memory::ReadString(uint64_t addr, std::string* string, uint64_t max_read) {
- string->clear();
- uint64_t bytes_read = 0;
- while (bytes_read < max_read) {
- uint8_t value;
- if (!ReadFully(addr, &value, sizeof(value))) {
- return false;
+bool Memory::ReadString(uint64_t addr, std::string* dst, size_t max_read) {
+ char buffer[256]; // Large enough for 99% of symbol names.
+ size_t size = 0; // Number of bytes which were read into the buffer.
+ for (size_t offset = 0; offset < max_read; offset += size) {
+ // Look for null-terminator first, so we can allocate string of exact size.
+ // If we know the end of valid memory range, do the reads in larger blocks.
+ size_t read = std::min(sizeof(buffer), max_read - offset);
+ size = Read(addr + offset, buffer, read);
+ if (size == 0) {
+ return false; // We have not found end of string yet and we can not read more data.
}
- if (value == '\0') {
- return true;
+ size_t length = strnlen(buffer, size); // Index of the null-terminator.
+ if (length < size) {
+ // We found the null-terminator. Allocate the string and set its content.
+ if (offset == 0) {
+ // We did just single read, so the buffer already contains the whole string.
+ dst->assign(buffer, length);
+ return true;
+ } else {
+ // The buffer contains only the last block. Read the whole string again.
+ dst->assign(offset + length, '\0');
+ return ReadFully(addr, dst->data(), dst->size());
+ }
}
- string->push_back(value);
- addr++;
- bytes_read++;
}
return false;
}
diff --git a/libunwindstack/Symbols.cpp b/libunwindstack/Symbols.cpp
index e3c15a2..2117ebd 100644
--- a/libunwindstack/Symbols.cpp
+++ b/libunwindstack/Symbols.cpp
@@ -19,6 +19,7 @@
#include <algorithm>
#include <string>
+#include <vector>
#include <unwindstack/Memory.h>
@@ -29,23 +30,55 @@
Symbols::Symbols(uint64_t offset, uint64_t size, uint64_t entry_size, uint64_t str_offset,
uint64_t str_size)
- : cur_offset_(offset),
- offset_(offset),
- end_(offset + size),
+ : offset_(offset),
+ count_(entry_size != 0 ? size / entry_size : 0),
entry_size_(entry_size),
str_offset_(str_offset),
str_end_(str_offset_ + str_size) {}
-const Symbols::Info* Symbols::GetInfoFromCache(uint64_t addr) {
- // Binary search the table.
+template <typename SymType>
+static bool IsFunc(const SymType* entry) {
+ return entry->st_shndx != SHN_UNDEF && ELF32_ST_TYPE(entry->st_info) == STT_FUNC;
+}
+
+// Read symbol entry from memory and cache it so we don't have to read it again.
+template <typename SymType>
+inline __attribute__((__always_inline__)) const Symbols::Info* Symbols::ReadFuncInfo(
+ uint32_t symbol_index, Memory* elf_memory) {
+ auto it = symbols_.find(symbol_index);
+ if (it != symbols_.end()) {
+ return &it->second;
+ }
+ SymType sym;
+ if (!elf_memory->ReadFully(offset_ + symbol_index * entry_size_, &sym, sizeof(sym))) {
+ return nullptr;
+ }
+ if (!IsFunc(&sym)) {
+ // We need the address for binary search, but we don't want it to be matched.
+ sym.st_size = 0;
+ }
+ Info info{.addr = sym.st_value, .size = static_cast<uint32_t>(sym.st_size), .name = sym.st_name};
+ return &symbols_.emplace(symbol_index, info).first->second;
+}
+
+// Binary search the symbol table to find function containing the given address.
+// Without remap, the symbol table is assumed to be sorted and accessed directly.
+// If the symbol table is not sorted this method might fail but should not crash.
+// When the indices are remapped, they are guaranteed to be sorted by address.
+template <typename SymType, bool RemapIndices>
+const Symbols::Info* Symbols::BinarySearch(uint64_t addr, Memory* elf_memory) {
size_t first = 0;
- size_t last = symbols_.size();
+ size_t last = RemapIndices ? remap_->size() : count_;
while (first < last) {
size_t current = first + (last - first) / 2;
- const Info* info = &symbols_[current];
- if (addr < info->start_offset) {
+ size_t symbol_index = RemapIndices ? remap_.value()[current] : current;
+ const Info* info = ReadFuncInfo<SymType>(symbol_index, elf_memory);
+ if (info == nullptr) {
+ return nullptr;
+ }
+ if (addr < info->addr) {
last = current;
- } else if (addr < info->end_offset) {
+ } else if (addr < info->addr + info->size) {
return info;
} else {
first = current + 1;
@@ -54,64 +87,72 @@
return nullptr;
}
+// Create remapping table which allows us to access symbols as if they were sorted by address.
template <typename SymType>
-bool Symbols::GetName(uint64_t addr, Memory* elf_memory, std::string* name, uint64_t* func_offset) {
- if (symbols_.size() != 0) {
- const Info* info = GetInfoFromCache(addr);
- if (info) {
- CHECK(addr >= info->start_offset && addr <= info->end_offset);
- *func_offset = addr - info->start_offset;
- return elf_memory->ReadString(info->str_offset, name, str_end_ - info->str_offset);
+void Symbols::BuildRemapTable(Memory* elf_memory) {
+ std::vector<uint64_t> addrs; // Addresses of all symbols (addrs[i] == symbols[i].st_value).
+ addrs.reserve(count_);
+ remap_.emplace(); // Construct the optional remap table.
+ remap_->reserve(count_);
+ for (size_t symbol_idx = 0; symbol_idx < count_;) {
+ // Read symbols from memory. We intentionally bypass the cache to save memory.
+ // Do the reads in batches so that we minimize the number of memory read calls.
+ uint8_t buffer[1024];
+ size_t read = std::min<size_t>(sizeof(buffer), (count_ - symbol_idx) * entry_size_);
+ size_t size = elf_memory->Read(offset_ + symbol_idx * entry_size_, buffer, read);
+ if (size < sizeof(SymType)) {
+ break; // Stop processing, something looks like it is corrupted.
}
- }
-
- bool symbol_added = false;
- bool return_value = false;
- while (cur_offset_ + entry_size_ <= end_) {
- SymType entry;
- if (!elf_memory->ReadFully(cur_offset_, &entry, sizeof(entry))) {
- // Stop all processing, something looks like it is corrupted.
- cur_offset_ = UINT64_MAX;
- return false;
- }
- cur_offset_ += entry_size_;
-
- if (entry.st_shndx != SHN_UNDEF && ELF32_ST_TYPE(entry.st_info) == STT_FUNC) {
- // Treat st_value as virtual address.
- uint64_t start_offset = entry.st_value;
- uint64_t end_offset = start_offset + entry.st_size;
-
- // Cache the value.
- symbols_.emplace_back(start_offset, end_offset, str_offset_ + entry.st_name);
- symbol_added = true;
-
- if (addr >= start_offset && addr < end_offset) {
- *func_offset = addr - start_offset;
- uint64_t offset = str_offset_ + entry.st_name;
- if (offset < str_end_) {
- return_value = elf_memory->ReadString(offset, name, str_end_ - offset);
- }
- break;
+ for (size_t offset = 0; offset + sizeof(SymType) <= size; offset += entry_size_, symbol_idx++) {
+ SymType sym;
+ memcpy(&sym, &buffer[offset], sizeof(SymType)); // Copy to ensure alignment.
+ addrs.push_back(sym.st_value); // Always insert so it is indexable by symbol index.
+ if (IsFunc(&sym)) {
+ remap_->push_back(symbol_idx); // Indices of function symbols only.
}
}
}
+ // Sort by address to make the remap list binary searchable (stable due to the a<b tie break).
+ auto comp = [&addrs](auto a, auto b) { return std::tie(addrs[a], a) < std::tie(addrs[b], b); };
+ std::sort(remap_->begin(), remap_->end(), comp);
+ // Remove duplicate entries (methods de-duplicated by the linker).
+ auto pred = [&addrs](auto a, auto b) { return addrs[a] == addrs[b]; };
+ remap_->erase(std::unique(remap_->begin(), remap_->end(), pred), remap_->end());
+ remap_->shrink_to_fit();
+}
- if (symbol_added) {
- std::sort(symbols_.begin(), symbols_.end(),
- [](const Info& a, const Info& b) { return a.start_offset < b.start_offset; });
+template <typename SymType>
+bool Symbols::GetName(uint64_t addr, Memory* elf_memory, std::string* name, uint64_t* func_offset) {
+ const Info* info;
+ if (!remap_.has_value()) {
+ // Assume the symbol table is sorted. If it is not, this will gracefully fail.
+ info = BinarySearch<SymType, false>(addr, elf_memory);
+ if (info == nullptr) {
+ // Create the remapping table and retry the search.
+ BuildRemapTable<SymType>(elf_memory);
+ symbols_.clear(); // Remove cached symbols since the access pattern will be different.
+ info = BinarySearch<SymType, true>(addr, elf_memory);
+ }
+ } else {
+ // Fast search using the previously created remap table.
+ info = BinarySearch<SymType, true>(addr, elf_memory);
}
- return return_value;
+ if (info == nullptr) {
+ return false;
+ }
+ // Read the function name from the string table.
+ *func_offset = addr - info->addr;
+ uint64_t str = str_offset_ + info->name;
+ return str < str_end_ && elf_memory->ReadString(str, name, str_end_ - str);
}
template <typename SymType>
bool Symbols::GetGlobal(Memory* elf_memory, const std::string& name, uint64_t* memory_address) {
- uint64_t cur_offset = offset_;
- while (cur_offset + entry_size_ <= end_) {
+ for (uint32_t i = 0; i < count_; i++) {
SymType entry;
- if (!elf_memory->ReadFully(cur_offset, &entry, sizeof(entry))) {
+ if (!elf_memory->ReadFully(offset_ + i * entry_size_, &entry, sizeof(entry))) {
return false;
}
- cur_offset += entry_size_;
if (entry.st_shndx != SHN_UNDEF && ELF32_ST_TYPE(entry.st_info) == STT_OBJECT &&
ELF32_ST_BIND(entry.st_info) == STB_GLOBAL) {
diff --git a/libunwindstack/Symbols.h b/libunwindstack/Symbols.h
index 7fcd067..3b3f20b 100644
--- a/libunwindstack/Symbols.h
+++ b/libunwindstack/Symbols.h
@@ -19,8 +19,9 @@
#include <stdint.h>
+#include <optional>
#include <string>
-#include <vector>
+#include <unordered_map>
namespace unwindstack {
@@ -29,11 +30,9 @@
class Symbols {
struct Info {
- Info(uint64_t start_offset, uint64_t end_offset, uint64_t str_offset)
- : start_offset(start_offset), end_offset(end_offset), str_offset(str_offset) {}
- uint64_t start_offset;
- uint64_t end_offset;
- uint64_t str_offset;
+ uint64_t addr; // Symbol address.
+ uint32_t size; // Symbol size in bytes. Zero if not a function.
+ uint32_t name; // Offset in .strtab.
};
public:
@@ -41,8 +40,6 @@
uint64_t str_size);
virtual ~Symbols() = default;
- const Info* GetInfoFromCache(uint64_t addr);
-
template <typename SymType>
bool GetName(uint64_t addr, Memory* elf_memory, std::string* name, uint64_t* func_offset);
@@ -51,18 +48,27 @@
void ClearCache() {
symbols_.clear();
- cur_offset_ = offset_;
+ remap_.reset();
}
private:
- uint64_t cur_offset_;
- uint64_t offset_;
- uint64_t end_;
- uint64_t entry_size_;
- uint64_t str_offset_;
- uint64_t str_end_;
+ template <typename SymType>
+ const Info* ReadFuncInfo(uint32_t symbol_index, Memory* elf_memory);
- std::vector<Info> symbols_;
+ template <typename SymType, bool RemapIndices>
+ const Info* BinarySearch(uint64_t addr, Memory* elf_memory);
+
+ template <typename SymType>
+ void BuildRemapTable(Memory* elf_memory);
+
+ const uint64_t offset_;
+ const uint64_t count_;
+ const uint64_t entry_size_;
+ const uint64_t str_offset_;
+ const uint64_t str_end_;
+
+ std::unordered_map<uint32_t, Info> symbols_; // Cache of read symbols (keyed by symbol index).
+ std::optional<std::vector<uint32_t>> remap_; // Indices of function symbols sorted by address.
};
} // namespace unwindstack
diff --git a/libunwindstack/benchmarks/ElfBenchmark.cpp b/libunwindstack/benchmarks/ElfBenchmark.cpp
new file mode 100644
index 0000000..c108a2a
--- /dev/null
+++ b/libunwindstack/benchmarks/ElfBenchmark.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2020 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 <err.h>
+#include <malloc.h>
+#include <stdint.h>
+
+#include <string>
+
+#include <benchmark/benchmark.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Memory.h>
+
+#include "Utils.h"
+
+static void BenchmarkElfCreate(benchmark::State& state, const std::string& elf_file) {
+#if defined(__BIONIC__)
+ uint64_t rss_bytes = 0;
+#endif
+ uint64_t alloc_bytes = 0;
+ for (auto _ : state) {
+ state.PauseTiming();
+#if defined(__BIONIC__)
+ mallopt(M_PURGE, 0);
+ uint64_t rss_bytes_before = 0;
+ GatherRss(&rss_bytes_before);
+#endif
+ uint64_t alloc_bytes_before = mallinfo().uordblks;
+ auto file_memory = unwindstack::Memory::CreateFileMemory(elf_file, 0);
+ state.ResumeTiming();
+
+ unwindstack::Elf elf(file_memory.release());
+ if (!elf.Init() || !elf.valid()) {
+ errx(1, "Internal Error: Cannot open elf.");
+ }
+
+ state.PauseTiming();
+#if defined(__BIONIC__)
+ mallopt(M_PURGE, 0);
+#endif
+ alloc_bytes += mallinfo().uordblks - alloc_bytes_before;
+#if defined(__BIONIC__)
+ GatherRss(&rss_bytes);
+ rss_bytes -= rss_bytes_before;
+#endif
+ state.ResumeTiming();
+ }
+
+#if defined(__BIONIC__)
+ state.counters["RSS_BYTES"] = rss_bytes / static_cast<double>(state.iterations());
+#endif
+ state.counters["ALLOCATED_BYTES"] = alloc_bytes / static_cast<double>(state.iterations());
+}
+
+void BM_elf_create(benchmark::State& state) {
+ BenchmarkElfCreate(state, GetElfFile());
+}
+BENCHMARK(BM_elf_create);
+
+void BM_elf_create_compressed(benchmark::State& state) {
+ BenchmarkElfCreate(state, GetCompressedElfFile());
+}
+BENCHMARK(BM_elf_create_compressed);
diff --git a/libunwindstack/benchmarks/SymbolBenchmark.cpp b/libunwindstack/benchmarks/SymbolBenchmark.cpp
index a850ff0..73088da 100644
--- a/libunwindstack/benchmarks/SymbolBenchmark.cpp
+++ b/libunwindstack/benchmarks/SymbolBenchmark.cpp
@@ -22,33 +22,12 @@
#include <string>
#include <vector>
-#include <android-base/file.h>
-#include <android-base/strings.h>
#include <benchmark/benchmark.h>
#include <unwindstack/Elf.h>
#include <unwindstack/Memory.h>
-#if defined(__BIONIC__)
-
-#include <meminfo/procmeminfo.h>
-#include <procinfo/process_map.h>
-
-static void Gather(uint64_t* rss_bytes) {
- android::meminfo::ProcMemInfo proc_mem(getpid());
- const std::vector<android::meminfo::Vma>& maps = proc_mem.MapsWithoutUsageStats();
- for (auto& vma : maps) {
- if (vma.name == "[anon:libc_malloc]" || android::base::StartsWith(vma.name, "[anon:scudo:") ||
- android::base::StartsWith(vma.name, "[anon:GWP-ASan")) {
- android::meminfo::Vma update_vma(vma);
- if (!proc_mem.FillInVmaStats(update_vma)) {
- err(1, "FillInVmaStats failed\n");
- }
- *rss_bytes += update_vma.usage.rss;
- }
- }
-}
-#endif
+#include "Utils.h"
static void BenchmarkSymbolLookup(benchmark::State& state, std::vector<uint64_t> offsets,
std::string elf_file, bool expect_found) {
@@ -66,7 +45,7 @@
#if defined(__BIONIC__)
mallopt(M_PURGE, 0);
uint64_t rss_bytes_before = 0;
- Gather(&rss_bytes_before);
+ GatherRss(&rss_bytes_before);
#endif
uint64_t alloc_bytes_before = mallinfo().uordblks;
state.ResumeTiming();
@@ -88,7 +67,7 @@
#endif
alloc_bytes += mallinfo().uordblks - alloc_bytes_before;
#if defined(__BIONIC__)
- Gather(&rss_bytes);
+ GatherRss(&rss_bytes);
rss_bytes -= rss_bytes_before;
#endif
state.ResumeTiming();
@@ -105,14 +84,6 @@
BenchmarkSymbolLookup(state, std::vector<uint64_t>{pc}, elf_file, expect_found);
}
-std::string GetElfFile() {
- return android::base::GetExecutableDirectory() + "/benchmarks/files/libart_arm.so";
-}
-
-std::string GetSortedElfFile() {
- return android::base::GetExecutableDirectory() + "/benchmarks/files/boot_arm.oat";
-}
-
void BM_symbol_not_present(benchmark::State& state) {
BenchmarkSymbolLookup(state, 0, GetElfFile(), false);
}
@@ -136,23 +107,23 @@
BENCHMARK(BM_symbol_find_multiple);
void BM_symbol_not_present_from_sorted(benchmark::State& state) {
- BenchmarkSymbolLookup(state, 0, GetSortedElfFile(), false);
+ BenchmarkSymbolLookup(state, 0, GetSymbolSortedElfFile(), false);
}
BENCHMARK(BM_symbol_not_present_from_sorted);
void BM_symbol_find_single_from_sorted(benchmark::State& state) {
- BenchmarkSymbolLookup(state, 0x138638, GetSortedElfFile(), true);
+ BenchmarkSymbolLookup(state, 0x138638, GetSymbolSortedElfFile(), true);
}
BENCHMARK(BM_symbol_find_single_from_sorted);
void BM_symbol_find_single_many_times_from_sorted(benchmark::State& state) {
- BenchmarkSymbolLookup(state, std::vector<uint64_t>(15, 0x138638), GetSortedElfFile(), true);
+ BenchmarkSymbolLookup(state, std::vector<uint64_t>(15, 0x138638), GetSymbolSortedElfFile(), true);
}
BENCHMARK(BM_symbol_find_single_many_times_from_sorted);
void BM_symbol_find_multiple_from_sorted(benchmark::State& state) {
BenchmarkSymbolLookup(state,
std::vector<uint64_t>{0x138638, 0x84350, 0x14df18, 0x1f3a38, 0x1f3ca8},
- GetSortedElfFile(), true);
+ GetSymbolSortedElfFile(), true);
}
BENCHMARK(BM_symbol_find_multiple_from_sorted);
diff --git a/libunwindstack/benchmarks/Utils.cpp b/libunwindstack/benchmarks/Utils.cpp
new file mode 100644
index 0000000..c92f109
--- /dev/null
+++ b/libunwindstack/benchmarks/Utils.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2020 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 <err.h>
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/strings.h>
+#include <benchmark/benchmark.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Memory.h>
+
+std::string GetElfFile() {
+ return android::base::GetExecutableDirectory() + "/benchmarks/files/libart_arm.so";
+}
+
+std::string GetSymbolSortedElfFile() {
+ return android::base::GetExecutableDirectory() + "/benchmarks/files/boot_arm.oat";
+}
+
+std::string GetCompressedElfFile() {
+ // Both are the same right now.
+ return GetSymbolSortedElfFile();
+}
+
+#if defined(__BIONIC__)
+
+#include <meminfo/procmeminfo.h>
+#include <procinfo/process_map.h>
+
+void GatherRss(uint64_t* rss_bytes) {
+ android::meminfo::ProcMemInfo proc_mem(getpid());
+ const std::vector<android::meminfo::Vma>& maps = proc_mem.MapsWithoutUsageStats();
+ for (auto& vma : maps) {
+ if (vma.name == "[anon:libc_malloc]" || android::base::StartsWith(vma.name, "[anon:scudo:") ||
+ android::base::StartsWith(vma.name, "[anon:GWP-ASan")) {
+ android::meminfo::Vma update_vma(vma);
+ if (!proc_mem.FillInVmaStats(update_vma)) {
+ err(1, "FillInVmaStats failed\n");
+ }
+ *rss_bytes += update_vma.usage.rss;
+ }
+ }
+}
+#endif
diff --git a/libunwindstack/benchmarks/Utils.h b/libunwindstack/benchmarks/Utils.h
new file mode 100644
index 0000000..bee6efc
--- /dev/null
+++ b/libunwindstack/benchmarks/Utils.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020 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 _LIBUNWINDSTACK_UTILS_H
+#define _LIBUNWINDSTACK_UTILS_H
+
+#include <stdint.h>
+
+#include <string>
+
+std::string GetElfFile();
+
+std::string GetSymbolSortedElfFile();
+
+std::string GetCompressedElfFile();
+
+#if defined(__BIONIC__)
+
+#include <meminfo/procmeminfo.h>
+#include <procinfo/process_map.h>
+
+void GatherRss(uint64_t* rss_bytes);
+
+#endif
+
+#endif // _LIBUNWINDSTACK_UTILS_h
diff --git a/libunwindstack/include/unwindstack/Memory.h b/libunwindstack/include/unwindstack/Memory.h
index 26252b6..3d81878 100644
--- a/libunwindstack/include/unwindstack/Memory.h
+++ b/libunwindstack/include/unwindstack/Memory.h
@@ -37,7 +37,7 @@
uint64_t end);
static std::unique_ptr<Memory> CreateFileMemory(const std::string& path, uint64_t offset);
- virtual bool ReadString(uint64_t addr, std::string* string, uint64_t max_read = UINT64_MAX);
+ virtual bool ReadString(uint64_t addr, std::string* dst, size_t max_read);
virtual void Clear() {}
diff --git a/libunwindstack/tests/MemoryTest.cpp b/libunwindstack/tests/MemoryTest.cpp
index 3655984..8a8eb24 100644
--- a/libunwindstack/tests/MemoryTest.cpp
+++ b/libunwindstack/tests/MemoryTest.cpp
@@ -59,10 +59,10 @@
memory.SetMemory(100, name.c_str(), name.size() + 1);
std::string dst_name;
- ASSERT_TRUE(memory.ReadString(100, &dst_name));
+ ASSERT_TRUE(memory.ReadString(100, &dst_name, 100));
ASSERT_EQ("string_in_memory", dst_name);
- ASSERT_TRUE(memory.ReadString(107, &dst_name));
+ ASSERT_TRUE(memory.ReadString(107, &dst_name, 100));
ASSERT_EQ("in_memory", dst_name);
// Set size greater than string.
@@ -82,15 +82,56 @@
std::string dst_name;
// Read from a non-existant address.
- ASSERT_FALSE(memory.ReadString(100, &dst_name));
+ ASSERT_FALSE(memory.ReadString(100, &dst_name, 100));
// This should fail because there is no terminating '\0'.
- ASSERT_FALSE(memory.ReadString(0, &dst_name));
+ ASSERT_FALSE(memory.ReadString(0, &dst_name, 100));
// This should pass because there is a terminating '\0'.
memory.SetData8(name.size(), '\0');
- ASSERT_TRUE(memory.ReadString(0, &dst_name));
+ ASSERT_TRUE(memory.ReadString(0, &dst_name, 100));
ASSERT_EQ("short", dst_name);
}
+TEST(MemoryTest, read_string_long) {
+ // This string should be greater than 768 characters long (greater than 3 times
+ // the buffer in the ReadString function) to read multiple blocks.
+ static constexpr char kLongString[] =
+ "one two three four five six seven eight nine ten eleven twelve thirteen fourteen fifteen "
+ "sixteen seventeen eightteen nineteen twenty twenty-one twenty-two twenty-three twenty-four "
+ "twenty-five twenty-six twenty-seven twenty-eight twenty-nine thirty thirty-one thirty-two "
+ "thirty-three thirty-four thirty-five thirty-six thirty-seven thirty-eight thirty-nine forty "
+ "forty-one forty-two forty-three forty-four forty-five forty-size forty-seven forty-eight "
+ "forty-nine fifty fifty-one fifty-two fifty-three fifty-four fifty-five fifty-six "
+ "fifty-seven fifty-eight fifty-nine sixty sixty-one sixty-two sixty-three sixty-four "
+ "sixty-five sixty-six sixty-seven sixty-eight sixty-nine seventy seventy-one seventy-two "
+ "seventy-three seventy-four seventy-five seventy-six seventy-seven seventy-eight "
+ "seventy-nine eighty";
+
+ MemoryFake memory;
+
+ memory.SetMemory(100, kLongString, sizeof(kLongString));
+
+ std::string dst_name;
+ ASSERT_TRUE(memory.ReadString(100, &dst_name, sizeof(kLongString)));
+ ASSERT_EQ(kLongString, dst_name);
+
+ std::string expected_str(kLongString, 255);
+ memory.SetMemory(100, expected_str.data(), expected_str.length() + 1);
+ ASSERT_TRUE(memory.ReadString(100, &dst_name, 256));
+ ASSERT_EQ(expected_str, dst_name);
+ ASSERT_FALSE(memory.ReadString(100, &dst_name, 255));
+
+ expected_str = std::string(kLongString, 256);
+ memory.SetMemory(100, expected_str.data(), expected_str.length() + 1);
+ ASSERT_TRUE(memory.ReadString(100, &dst_name, 257));
+ ASSERT_EQ(expected_str, dst_name);
+ ASSERT_FALSE(memory.ReadString(100, &dst_name, 256));
+
+ expected_str = std::string(kLongString, 299);
+ memory.SetMemory(100, expected_str.data(), expected_str.length() + 1);
+ ASSERT_TRUE(memory.ReadString(100, &dst_name, 300));
+ ASSERT_EQ(expected_str, dst_name);
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/SymbolsTest.cpp b/libunwindstack/tests/SymbolsTest.cpp
index c58aeff..9afbeec 100644
--- a/libunwindstack/tests/SymbolsTest.cpp
+++ b/libunwindstack/tests/SymbolsTest.cpp
@@ -185,18 +185,21 @@
std::string fake_name;
this->InitSym(&sym, 0x5000, 0x10, 0x40);
+ this->memory_.SetMemoryBlock(offset, entry_size, 0);
this->memory_.SetMemory(offset, &sym, sizeof(sym));
fake_name = "function_one";
this->memory_.SetMemory(0x2040, fake_name.c_str(), fake_name.size() + 1);
offset += entry_size;
this->InitSym(&sym, 0x3004, 0x200, 0x100);
+ this->memory_.SetMemoryBlock(offset, entry_size, 0);
this->memory_.SetMemory(offset, &sym, sizeof(sym));
fake_name = "function_two";
this->memory_.SetMemory(0x2100, fake_name.c_str(), fake_name.size() + 1);
offset += entry_size;
this->InitSym(&sym, 0xa010, 0x20, 0x230);
+ this->memory_.SetMemoryBlock(offset, entry_size, 0);
this->memory_.SetMemory(offset, &sym, sizeof(sym));
fake_name = "function_three";
this->memory_.SetMemory(0x2230, fake_name.c_str(), fake_name.size() + 1);
@@ -274,7 +277,9 @@
// Do call that should cache all of the entries (except the string data).
std::string name;
uint64_t func_offset;
- ASSERT_FALSE(symbols.GetName<TypeParam>(0x6000, &this->memory_, &name, &func_offset));
+ ASSERT_FALSE(symbols.GetName<TypeParam>(0x5000, &this->memory_, &name, &func_offset));
+ ASSERT_FALSE(symbols.GetName<TypeParam>(0x2000, &this->memory_, &name, &func_offset));
+ ASSERT_FALSE(symbols.GetName<TypeParam>(0x1000, &this->memory_, &name, &func_offset));
this->memory_.Clear();
ASSERT_FALSE(symbols.GetName<TypeParam>(0x6000, &this->memory_, &name, &func_offset));