Merge "Allow multiple threads sharing a map to unwind."

am: f819c1d94b

Change-Id: I708bd4218372b6d008f40df6fada92b601586a01
This commit is contained in:
Christopher Ferris 2017-11-29 03:07:08 +00:00 committed by android-build-merger
commit 19b90d2092
16 changed files with 502 additions and 367 deletions

View file

@ -44,15 +44,15 @@ bool UnwindStackMap::Build() {
}
// Iterate through the maps and fill in the backtrace_map_t structure.
for (auto& map_info : *stack_maps_) {
for (auto* map_info : *stack_maps_) {
backtrace_map_t map;
map.start = map_info.start;
map.end = map_info.end;
map.offset = map_info.offset;
map.start = map_info->start;
map.end = map_info->end;
map.offset = map_info->offset;
// Set to -1 so that it is demand loaded.
map.load_bias = static_cast<uintptr_t>(-1);
map.flags = map_info.flags;
map.name = map_info.name;
map.flags = map_info->flags;
map.name = map_info->name;
maps_.push_back(map);
}

View file

@ -77,6 +77,7 @@ struct thread_t {
struct dump_thread_t {
thread_t thread;
BacktraceMap* map;
Backtrace* backtrace;
int32_t* now;
int32_t done;
@ -632,7 +633,7 @@ static void* ThreadDump(void* data) {
}
// The status of the actual unwind will be checked elsewhere.
dump->backtrace = Backtrace::Create(getpid(), dump->thread.tid);
dump->backtrace = Backtrace::Create(getpid(), dump->thread.tid, dump->map);
dump->backtrace->Unwind(0);
android_atomic_acquire_store(1, &dump->done);
@ -640,8 +641,8 @@ static void* ThreadDump(void* data) {
return nullptr;
}
TEST(libbacktrace, thread_multiple_dump) {
// Dump NUM_THREADS simultaneously.
static void MultipleThreadDumpTest(bool share_map) {
// Dump NUM_THREADS simultaneously using the same map.
std::vector<thread_t> runners(NUM_THREADS);
std::vector<dump_thread_t> dumpers(NUM_THREADS);
@ -662,12 +663,17 @@ TEST(libbacktrace, thread_multiple_dump) {
// Start all of the dumpers at once, they will spin until they are signalled
// to begin their dump run.
std::unique_ptr<BacktraceMap> map;
if (share_map) {
map.reset(BacktraceMap::Create(getpid()));
}
int32_t dump_now = 0;
for (size_t i = 0; i < NUM_THREADS; i++) {
dumpers[i].thread.tid = runners[i].tid;
dumpers[i].thread.state = 0;
dumpers[i].done = 0;
dumpers[i].now = &dump_now;
dumpers[i].map = map.get();
ASSERT_TRUE(pthread_create(&dumpers[i].thread.threadId, &attr, ThreadDump, &dumpers[i]) == 0);
}
@ -689,47 +695,12 @@ TEST(libbacktrace, thread_multiple_dump) {
}
}
TEST(libbacktrace, thread_multiple_dump_same_thread) {
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
thread_t runner;
runner.tid = 0;
runner.state = 0;
ASSERT_TRUE(pthread_create(&runner.threadId, &attr, ThreadMaxRun, &runner) == 0);
TEST(libbacktrace, thread_multiple_dump) {
MultipleThreadDumpTest(false);
}
// Wait for tids to be set.
ASSERT_TRUE(WaitForNonZero(&runner.state, 30));
// Start all of the dumpers at once, they will spin until they are signalled
// to begin their dump run.
int32_t dump_now = 0;
// Dump the same thread NUM_THREADS simultaneously.
std::vector<dump_thread_t> dumpers(NUM_THREADS);
for (size_t i = 0; i < NUM_THREADS; i++) {
dumpers[i].thread.tid = runner.tid;
dumpers[i].thread.state = 0;
dumpers[i].done = 0;
dumpers[i].now = &dump_now;
ASSERT_TRUE(pthread_create(&dumpers[i].thread.threadId, &attr, ThreadDump, &dumpers[i]) == 0);
}
// Start all of the dumpers going at once.
android_atomic_acquire_store(1, &dump_now);
for (size_t i = 0; i < NUM_THREADS; i++) {
ASSERT_TRUE(WaitForNonZero(&dumpers[i].done, 30));
ASSERT_TRUE(dumpers[i].backtrace != nullptr);
VerifyMaxDump(dumpers[i].backtrace);
delete dumpers[i].backtrace;
dumpers[i].backtrace = nullptr;
}
// Tell the runner thread to exit its infinite loop.
android_atomic_acquire_store(0, &runner.state);
TEST(libbacktrace, thread_multiple_dump_same_map) {
MultipleThreadDumpTest(true);
}
// This test is for UnwindMaps that should share the same map cursor when

View file

@ -18,6 +18,7 @@
#include <string.h>
#include <memory>
#include <mutex>
#include <string>
#define LOG_TAG "unwind"
@ -87,6 +88,7 @@ void Elf::InitGnuDebugdata() {
}
bool Elf::GetSoname(std::string* name) {
std::lock_guard<std::mutex> guard(lock_);
return valid_ && interface_->GetSoname(name);
}
@ -95,6 +97,7 @@ uint64_t Elf::GetRelPc(uint64_t pc, const MapInfo* map_info) {
}
bool Elf::GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) {
std::lock_guard<std::mutex> guard(lock_);
return valid_ && (interface_->GetFunctionName(addr, load_bias_, name, func_offset) ||
(gnu_debugdata_interface_ && gnu_debugdata_interface_->GetFunctionName(
addr, load_bias_, name, func_offset)));
@ -119,6 +122,8 @@ bool Elf::Step(uint64_t rel_pc, uint64_t elf_offset, Regs* regs, Memory* process
}
rel_pc -= load_bias_;
// Lock during the step which can update information in the object.
std::lock_guard<std::mutex> guard(lock_);
return interface_->Step(rel_pc, regs, process_memory, finished) ||
(gnu_debugdata_interface_ &&
gnu_debugdata_interface_->Step(rel_pc, regs, process_memory, finished));

View file

@ -19,6 +19,7 @@
#include <unistd.h>
#include <memory>
#include <mutex>
#include <string>
#include <unwindstack/Elf.h>
@ -105,6 +106,9 @@ Memory* MapInfo::CreateMemory(const std::shared_ptr<Memory>& process_memory) {
}
Elf* MapInfo::GetElf(const std::shared_ptr<Memory>& process_memory, bool init_gnu_debugdata) {
// Make sure no other thread is trying to add the elf to this map.
std::lock_guard<std::mutex> guard(mutex_);
if (elf) {
return elf;
}

View file

@ -44,7 +44,7 @@ MapInfo* Maps::Find(uint64_t pc) {
size_t last = maps_.size();
while (first < last) {
size_t index = (first + last) / 2;
MapInfo* cur = &maps_[index];
MapInfo* cur = maps_[index];
if (pc >= cur->start && pc < cur->end) {
return cur;
} else if (pc < cur->start) {
@ -57,22 +57,22 @@ MapInfo* Maps::Find(uint64_t pc) {
}
// Assumes that line does not end in '\n'.
static bool InternalParseLine(const char* line, MapInfo* map_info) {
static MapInfo* InternalParseLine(const char* line) {
// Do not use a sscanf implementation since it is not performant.
// Example linux /proc/<pid>/maps lines:
// 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so
char* str;
const char* old_str = line;
map_info->start = strtoul(old_str, &str, 16);
uint64_t start = strtoul(old_str, &str, 16);
if (old_str == str || *str++ != '-') {
return false;
return nullptr;
}
old_str = str;
map_info->end = strtoul(old_str, &str, 16);
uint64_t end = strtoul(old_str, &str, 16);
if (old_str == str || !std::isspace(*str++)) {
return false;
return nullptr;
}
while (std::isspace(*str)) {
@ -81,82 +81,81 @@ static bool InternalParseLine(const char* line, MapInfo* map_info) {
// Parse permissions data.
if (*str == '\0') {
return false;
return nullptr;
}
map_info->flags = 0;
uint16_t flags = 0;
if (*str == 'r') {
map_info->flags |= PROT_READ;
flags |= PROT_READ;
} else if (*str != '-') {
return false;
return nullptr;
}
str++;
if (*str == 'w') {
map_info->flags |= PROT_WRITE;
flags |= PROT_WRITE;
} else if (*str != '-') {
return false;
return nullptr;
}
str++;
if (*str == 'x') {
map_info->flags |= PROT_EXEC;
flags |= PROT_EXEC;
} else if (*str != '-') {
return false;
return nullptr;
}
str++;
if (*str != 'p' && *str != 's') {
return false;
return nullptr;
}
str++;
if (!std::isspace(*str++)) {
return false;
return nullptr;
}
old_str = str;
map_info->offset = strtoul(old_str, &str, 16);
uint64_t offset = strtoul(old_str, &str, 16);
if (old_str == str || !std::isspace(*str)) {
return false;
return nullptr;
}
// Ignore the 00:00 values.
old_str = str;
(void)strtoul(old_str, &str, 16);
if (old_str == str || *str++ != ':') {
return false;
return nullptr;
}
if (std::isspace(*str)) {
return false;
return nullptr;
}
// Skip the inode.
old_str = str;
(void)strtoul(str, &str, 16);
if (old_str == str || !std::isspace(*str++)) {
return false;
return nullptr;
}
// Skip decimal digit.
old_str = str;
(void)strtoul(old_str, &str, 10);
if (old_str == str || (!std::isspace(*str) && *str != '\0')) {
return false;
return nullptr;
}
while (std::isspace(*str)) {
str++;
}
if (*str == '\0') {
map_info->name = str;
return true;
return new MapInfo(start, end, offset, flags, "");
}
// Save the name data.
map_info->name = str;
std::string name(str);
// Mark a device map in /dev/ and not in /dev/ashmem/ specially.
if (map_info->name.substr(0, 5) == "/dev/" && map_info->name.substr(5, 7) != "ashmem/") {
map_info->flags |= MAPS_FLAGS_DEVICE_MAP;
if (name.substr(0, 5) == "/dev/" && name.substr(5, 7) != "ashmem/") {
flags |= MAPS_FLAGS_DEVICE_MAP;
}
return true;
return new MapInfo(start, end, offset, flags, name);
}
bool Maps::Parse() {
@ -187,8 +186,8 @@ bool Maps::Parse() {
}
*newline = '\0';
MapInfo map_info;
if (!InternalParseLine(line, &map_info)) {
MapInfo* map_info = InternalParseLine(line);
if (map_info == nullptr) {
return_value = false;
break;
}
@ -205,8 +204,7 @@ bool Maps::Parse() {
Maps::~Maps() {
for (auto& map : maps_) {
delete map.elf;
map.elf = nullptr;
delete map;
}
}
@ -222,8 +220,8 @@ bool BufferMaps::Parse() {
end_of_line++;
}
MapInfo map_info;
if (!InternalParseLine(line.c_str(), &map_info)) {
MapInfo* map_info = InternalParseLine(line.c_str());
if (map_info == nullptr) {
return false;
}
maps_.push_back(map_info);
@ -252,24 +250,27 @@ bool OfflineMaps::Parse() {
std::vector<char> name;
while (true) {
MapInfo map_info;
ssize_t bytes = TEMP_FAILURE_RETRY(read(fd, &map_info.start, sizeof(map_info.start)));
uint64_t start;
ssize_t bytes = TEMP_FAILURE_RETRY(read(fd, &start, sizeof(start)));
if (bytes == 0) {
break;
}
if (bytes == -1 || bytes != sizeof(map_info.start)) {
if (bytes == -1 || bytes != sizeof(start)) {
return false;
}
bytes = TEMP_FAILURE_RETRY(read(fd, &map_info.end, sizeof(map_info.end)));
if (bytes == -1 || bytes != sizeof(map_info.end)) {
uint64_t end;
bytes = TEMP_FAILURE_RETRY(read(fd, &end, sizeof(end)));
if (bytes == -1 || bytes != sizeof(end)) {
return false;
}
bytes = TEMP_FAILURE_RETRY(read(fd, &map_info.offset, sizeof(map_info.offset)));
if (bytes == -1 || bytes != sizeof(map_info.offset)) {
uint64_t offset;
bytes = TEMP_FAILURE_RETRY(read(fd, &offset, sizeof(offset)));
if (bytes == -1 || bytes != sizeof(offset)) {
return false;
}
bytes = TEMP_FAILURE_RETRY(read(fd, &map_info.flags, sizeof(map_info.flags)));
if (bytes == -1 || bytes != sizeof(map_info.flags)) {
uint16_t flags;
bytes = TEMP_FAILURE_RETRY(read(fd, &flags, sizeof(flags)));
if (bytes == -1 || bytes != sizeof(flags)) {
return false;
}
uint16_t len;
@ -283,9 +284,10 @@ bool OfflineMaps::Parse() {
if (bytes == -1 || bytes != len) {
return false;
}
map_info.name = std::string(name.data(), len);
maps_.push_back(new MapInfo(start, end, offset, flags, std::string(name.data(), len)));
} else {
maps_.push_back(new MapInfo(start, end, offset, flags, ""));
}
maps_.push_back(map_info);
}
return true;
}

View file

@ -20,6 +20,7 @@
#include <stddef.h>
#include <memory>
#include <mutex>
#include <string>
#include <unwindstack/ElfInterface.h>
@ -80,6 +81,8 @@ class Elf {
std::unique_ptr<Memory> memory_;
uint32_t machine_type_;
uint8_t class_type_;
// Protect calls that can modify internal state of the interface object.
std::mutex lock_;
std::unique_ptr<Memory> gnu_debugdata_memory_;
std::unique_ptr<ElfInterface> gnu_debugdata_interface_;

View file

@ -19,34 +19,48 @@
#include <stdint.h>
#include <mutex>
#include <string>
#include <unwindstack/Elf.h>
namespace unwindstack {
// Forward declarations.
class Elf;
class Memory;
struct MapInfo {
uint64_t start;
uint64_t end;
uint64_t offset;
uint16_t flags;
MapInfo() = default;
MapInfo(uint64_t start, uint64_t end) : start(start), end(end) {}
MapInfo(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags, const std::string& name)
: start(start), end(end), offset(offset), flags(flags), name(name) {}
~MapInfo() { delete elf; }
uint64_t start = 0;
uint64_t end = 0;
uint64_t offset = 0;
uint16_t flags = 0;
std::string name;
Elf* elf = nullptr;
// This value is only non-zero if the offset is non-zero but there is
// no elf signature found at that offset. This indicates that the
// entire file is represented by the Memory object returned by CreateMemory,
// instead of a portion of the file.
uint64_t elf_offset;
uint64_t elf_offset = 0;
// This function guarantees it will never return nullptr.
Elf* GetElf(const std::shared_ptr<Memory>& process_memory, bool init_gnu_debugdata = false);
private:
MapInfo(const MapInfo&) = delete;
void operator=(const MapInfo&) = delete;
Memory* GetFileMemory();
Memory* CreateMemory(const std::shared_ptr<Memory>& process_memory);
// Protect the creation of the elf object.
std::mutex mutex_;
};
} // namespace unwindstack

View file

@ -42,11 +42,11 @@ class Maps {
virtual const std::string GetMapsFile() const { return ""; }
typedef std::vector<MapInfo>::iterator iterator;
typedef std::vector<MapInfo*>::iterator iterator;
iterator begin() { return maps_.begin(); }
iterator end() { return maps_.end(); }
typedef std::vector<MapInfo>::const_iterator const_iterator;
typedef std::vector<MapInfo*>::const_iterator const_iterator;
const_iterator begin() const { return maps_.begin(); }
const_iterator end() const { return maps_.end(); }
@ -54,11 +54,11 @@ class Maps {
MapInfo* Get(size_t index) {
if (index >= maps_.size()) return nullptr;
return &maps_[index];
return maps_[index];
}
protected:
std::vector<MapInfo> maps_;
std::vector<MapInfo*> maps_;
};
class RemoteMaps : public Maps {

View file

@ -33,7 +33,7 @@ namespace unwindstack {
#if defined(__arm__)
inline void RegsGetLocal(Regs* regs) {
inline __always_inline void RegsGetLocal(Regs* regs) {
void* reg_data = regs->RawData();
asm volatile(
".align 2\n"
@ -57,7 +57,7 @@ inline void RegsGetLocal(Regs* regs) {
#elif defined(__aarch64__)
inline void RegsGetLocal(Regs* regs) {
inline __always_inline void RegsGetLocal(Regs* regs) {
void* reg_data = regs->RawData();
asm volatile(
"1:\n"

View file

@ -273,7 +273,7 @@ TEST_F(ElfTest, rel_pc) {
elf.FakeSetValid(true);
elf.FakeSetLoadBias(0);
MapInfo map_info{.start = 0x1000, .end = 0x2000};
MapInfo map_info(0x1000, 0x2000);
ASSERT_EQ(0x101U, elf.GetRelPc(0x1101, &map_info));

View file

@ -94,7 +94,7 @@ TemporaryFile MapInfoCreateMemoryTest::elf32_at_map_;
TemporaryFile MapInfoCreateMemoryTest::elf64_at_map_;
TEST_F(MapInfoCreateMemoryTest, end_le_start) {
MapInfo info{.start = 0x100, .end = 0x100, .offset = 0, .name = elf_.path};
MapInfo info(0x100, 0x100, 0, 0, elf_.path);
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() == nullptr);
@ -112,7 +112,7 @@ TEST_F(MapInfoCreateMemoryTest, end_le_start) {
// Verify that if the offset is non-zero but there is no elf at the offset,
// that the full file is used.
TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_full_file) {
MapInfo info{.start = 0x100, .end = 0x200, .offset = 0x100, .name = elf_.path};
MapInfo info(0x100, 0x200, 0x100, 0, elf_.path);
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
@ -133,7 +133,7 @@ TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_full_file) {
// Verify that if the offset is non-zero and there is an elf at that
// offset, that only part of the file is used.
TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file) {
MapInfo info{.start = 0x100, .end = 0x200, .offset = 0x100, .name = elf_at_100_.path};
MapInfo info(0x100, 0x200, 0x100, 0, elf_at_100_.path);
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
@ -156,7 +156,7 @@ TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file) {
// embedded elf is bigger than the initial map, the new object is larger
// than the original map size. Do this for a 32 bit elf and a 64 bit elf.
TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file_whole_elf32) {
MapInfo info{.start = 0x5000, .end = 0x6000, .offset = 0x1000, .name = elf32_at_map_.path};
MapInfo info(0x5000, 0x6000, 0x1000, 0, elf32_at_map_.path);
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
@ -172,7 +172,7 @@ TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file_whole_e
}
TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file_whole_elf64) {
MapInfo info{.start = 0x7000, .end = 0x8000, .offset = 0x2000, .name = elf64_at_map_.path};
MapInfo info(0x7000, 0x8000, 0x2000, 0, elf64_at_map_.path);
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);

View file

@ -23,7 +23,9 @@
#include <sys/types.h>
#include <unistd.h>
#include <atomic>
#include <memory>
#include <thread>
#include <vector>
#include <android-base/file.h>
@ -67,52 +69,52 @@ class MapInfoGetElfTest : public ::testing::Test {
};
TEST_F(MapInfoGetElfTest, invalid) {
MapInfo info{.start = 0x1000, .end = 0x2000, .offset = 0, .flags = PROT_READ, .name = ""};
MapInfo info(0x1000, 0x2000, 0, PROT_READ, "");
// The map is empty, but this should still create an invalid elf object.
std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
ASSERT_TRUE(elf.get() != nullptr);
Elf* elf = info.GetElf(process_memory_, false);
ASSERT_TRUE(elf != nullptr);
ASSERT_FALSE(elf->valid());
}
TEST_F(MapInfoGetElfTest, valid32) {
MapInfo info{.start = 0x3000, .end = 0x4000, .offset = 0, .flags = PROT_READ, .name = ""};
MapInfo info(0x3000, 0x4000, 0, PROT_READ, "");
Elf32_Ehdr ehdr;
TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
memory_->SetMemory(0x3000, &ehdr, sizeof(ehdr));
std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
ASSERT_TRUE(elf.get() != nullptr);
Elf* elf = info.GetElf(process_memory_, false);
ASSERT_TRUE(elf != nullptr);
ASSERT_TRUE(elf->valid());
EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
EXPECT_EQ(ELFCLASS32, elf->class_type());
}
TEST_F(MapInfoGetElfTest, valid64) {
MapInfo info{.start = 0x8000, .end = 0x9000, .offset = 0, .flags = PROT_READ, .name = ""};
MapInfo info(0x8000, 0x9000, 0, PROT_READ, "");
Elf64_Ehdr ehdr;
TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_AARCH64);
memory_->SetMemory(0x8000, &ehdr, sizeof(ehdr));
std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
ASSERT_TRUE(elf.get() != nullptr);
Elf* elf = info.GetElf(process_memory_, false);
ASSERT_TRUE(elf != nullptr);
ASSERT_TRUE(elf->valid());
EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type());
EXPECT_EQ(ELFCLASS64, elf->class_type());
}
TEST_F(MapInfoGetElfTest, gnu_debugdata_do_not_init32) {
MapInfo info{.start = 0x4000, .end = 0x8000, .offset = 0, .flags = PROT_READ, .name = ""};
MapInfo info(0x4000, 0x8000, 0, PROT_READ, "");
TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, false,
[&](uint64_t offset, const void* ptr, size_t size) {
memory_->SetMemory(0x4000 + offset, ptr, size);
});
std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
ASSERT_TRUE(elf.get() != nullptr);
Elf* elf = info.GetElf(process_memory_, false);
ASSERT_TRUE(elf != nullptr);
ASSERT_TRUE(elf->valid());
EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
EXPECT_EQ(ELFCLASS32, elf->class_type());
@ -120,15 +122,15 @@ TEST_F(MapInfoGetElfTest, gnu_debugdata_do_not_init32) {
}
TEST_F(MapInfoGetElfTest, gnu_debugdata_do_not_init64) {
MapInfo info{.start = 0x6000, .end = 0x8000, .offset = 0, .flags = PROT_READ, .name = ""};
MapInfo info(0x6000, 0x8000, 0, PROT_READ, "");
TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(ELFCLASS64, EM_AARCH64, false,
[&](uint64_t offset, const void* ptr, size_t size) {
memory_->SetMemory(0x6000 + offset, ptr, size);
});
std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
ASSERT_TRUE(elf.get() != nullptr);
Elf* elf = info.GetElf(process_memory_, false);
ASSERT_TRUE(elf != nullptr);
ASSERT_TRUE(elf->valid());
EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type());
EXPECT_EQ(ELFCLASS64, elf->class_type());
@ -136,15 +138,15 @@ TEST_F(MapInfoGetElfTest, gnu_debugdata_do_not_init64) {
}
TEST_F(MapInfoGetElfTest, gnu_debugdata_init32) {
MapInfo info{.start = 0x2000, .end = 0x3000, .offset = 0, .flags = PROT_READ, .name = ""};
MapInfo info(0x2000, 0x3000, 0, PROT_READ, "");
TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, true,
[&](uint64_t offset, const void* ptr, size_t size) {
memory_->SetMemory(0x2000 + offset, ptr, size);
});
std::unique_ptr<Elf> elf(info.GetElf(process_memory_, true));
ASSERT_TRUE(elf.get() != nullptr);
Elf* elf = info.GetElf(process_memory_, true);
ASSERT_TRUE(elf != nullptr);
ASSERT_TRUE(elf->valid());
EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
EXPECT_EQ(ELFCLASS32, elf->class_type());
@ -152,15 +154,15 @@ TEST_F(MapInfoGetElfTest, gnu_debugdata_init32) {
}
TEST_F(MapInfoGetElfTest, gnu_debugdata_init64) {
MapInfo info{.start = 0x5000, .end = 0x8000, .offset = 0, .flags = PROT_READ, .name = ""};
MapInfo info(0x5000, 0x8000, 0, PROT_READ, "");
TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(ELFCLASS64, EM_AARCH64, true,
[&](uint64_t offset, const void* ptr, size_t size) {
memory_->SetMemory(0x5000 + offset, ptr, size);
});
std::unique_ptr<Elf> elf(info.GetElf(process_memory_, true));
ASSERT_TRUE(elf.get() != nullptr);
Elf* elf = info.GetElf(process_memory_, true);
ASSERT_TRUE(elf != nullptr);
ASSERT_TRUE(elf->valid());
EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type());
EXPECT_EQ(ELFCLASS64, elf->class_type());
@ -168,32 +170,36 @@ TEST_F(MapInfoGetElfTest, gnu_debugdata_init64) {
}
TEST_F(MapInfoGetElfTest, end_le_start) {
MapInfo info{.start = 0x1000, .end = 0x1000, .offset = 0, .flags = PROT_READ, .name = elf_.path};
MapInfo info(0x1000, 0x1000, 0, PROT_READ, elf_.path);
Elf32_Ehdr ehdr;
TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
ASSERT_TRUE(android::base::WriteFully(elf_.fd, &ehdr, sizeof(ehdr)));
std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
Elf* elf = info.GetElf(process_memory_, false);
ASSERT_TRUE(elf != nullptr);
ASSERT_FALSE(elf->valid());
delete info.elf;
info.elf = nullptr;
info.end = 0xfff;
elf.reset(info.GetElf(process_memory_, false));
elf = info.GetElf(process_memory_, false);
ASSERT_TRUE(elf != nullptr);
ASSERT_FALSE(elf->valid());
// Make sure this test is valid.
delete info.elf;
info.elf = nullptr;
info.end = 0x2000;
elf.reset(info.GetElf(process_memory_, false));
elf = info.GetElf(process_memory_, false);
ASSERT_TRUE(elf != nullptr);
ASSERT_TRUE(elf->valid());
}
// Verify that if the offset is non-zero but there is no elf at the offset,
// that the full file is used.
TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_full_file) {
MapInfo info{
.start = 0x1000, .end = 0x2000, .offset = 0x100, .flags = PROT_READ, .name = elf_.path};
MapInfo info(0x1000, 0x2000, 0x100, PROT_READ, elf_.path);
std::vector<uint8_t> buffer(0x1000);
memset(buffer.data(), 0, buffer.size());
@ -202,7 +208,8 @@ TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_full_file) {
memcpy(buffer.data(), &ehdr, sizeof(ehdr));
ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
Elf* elf = info.GetElf(process_memory_, false);
ASSERT_TRUE(elf != nullptr);
ASSERT_TRUE(elf->valid());
ASSERT_TRUE(elf->memory() != nullptr);
ASSERT_EQ(0x100U, info.elf_offset);
@ -221,8 +228,7 @@ TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_full_file) {
// Verify that if the offset is non-zero and there is an elf at that
// offset, that only part of the file is used.
TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file) {
MapInfo info{
.start = 0x1000, .end = 0x2000, .offset = 0x2000, .flags = PROT_READ, .name = elf_.path};
MapInfo info(0x1000, 0x2000, 0x2000, PROT_READ, elf_.path);
std::vector<uint8_t> buffer(0x4000);
memset(buffer.data(), 0, buffer.size());
@ -231,7 +237,8 @@ TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file) {
memcpy(&buffer[info.offset], &ehdr, sizeof(ehdr));
ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
Elf* elf = info.GetElf(process_memory_, false);
ASSERT_TRUE(elf != nullptr);
ASSERT_TRUE(elf->valid());
ASSERT_TRUE(elf->memory() != nullptr);
ASSERT_EQ(0U, info.elf_offset);
@ -251,8 +258,7 @@ TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file) {
// embedded elf is bigger than the initial map, the new object is larger
// than the original map size. Do this for a 32 bit elf and a 64 bit elf.
TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file_whole_elf32) {
MapInfo info{
.start = 0x5000, .end = 0x6000, .offset = 0x1000, .flags = PROT_READ, .name = elf_.path};
MapInfo info(0x5000, 0x6000, 0x1000, PROT_READ, elf_.path);
std::vector<uint8_t> buffer(0x4000);
memset(buffer.data(), 0, buffer.size());
@ -264,7 +270,8 @@ TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file_whole_elf32)
memcpy(&buffer[info.offset], &ehdr, sizeof(ehdr));
ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
Elf* elf = info.GetElf(process_memory_, false);
ASSERT_TRUE(elf != nullptr);
ASSERT_TRUE(elf->valid());
ASSERT_TRUE(elf->memory() != nullptr);
ASSERT_EQ(0U, info.elf_offset);
@ -279,8 +286,7 @@ TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file_whole_elf32)
}
TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file_whole_elf64) {
MapInfo info{
.start = 0x7000, .end = 0x8000, .offset = 0x1000, .flags = PROT_READ, .name = elf_.path};
MapInfo info(0x7000, 0x8000, 0x1000, PROT_READ, elf_.path);
std::vector<uint8_t> buffer(0x4000);
memset(buffer.data(), 0, buffer.size());
@ -292,7 +298,8 @@ TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file_whole_elf64)
memcpy(&buffer[info.offset], &ehdr, sizeof(ehdr));
ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
Elf* elf = info.GetElf(process_memory_, false);
ASSERT_TRUE(elf != nullptr);
ASSERT_TRUE(elf->valid());
ASSERT_TRUE(elf->memory() != nullptr);
ASSERT_EQ(0U, info.elf_offset);
@ -307,7 +314,7 @@ TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file_whole_elf64)
}
TEST_F(MapInfoGetElfTest, process_memory_not_read_only) {
MapInfo info{.start = 0x9000, .end = 0xa000, .offset = 0x1000, .flags = 0, .name = ""};
MapInfo info(0x9000, 0xa000, 0x1000, 0, "");
// Create valid elf data in process memory only.
Elf64_Ehdr ehdr;
@ -317,21 +324,19 @@ TEST_F(MapInfoGetElfTest, process_memory_not_read_only) {
ehdr.e_shnum = 4;
memory_->SetMemory(0x9000, &ehdr, sizeof(ehdr));
std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
Elf* elf = info.GetElf(process_memory_, false);
ASSERT_TRUE(elf != nullptr);
ASSERT_FALSE(elf->valid());
delete info.elf;
info.elf = nullptr;
info.flags = PROT_READ;
elf.reset(info.GetElf(process_memory_, false));
elf = info.GetElf(process_memory_, false);
ASSERT_TRUE(elf->valid());
}
TEST_F(MapInfoGetElfTest, check_device_maps) {
MapInfo info{.start = 0x7000,
.end = 0x8000,
.offset = 0x1000,
.flags = PROT_READ | MAPS_FLAGS_DEVICE_MAP,
.name = "/dev/something"};
MapInfo info(0x7000, 0x8000, 0x1000, PROT_READ | MAPS_FLAGS_DEVICE_MAP, "/dev/something");
// Create valid elf data in process memory for this to verify that only
// the name is causing invalid elf data.
@ -342,20 +347,68 @@ TEST_F(MapInfoGetElfTest, check_device_maps) {
ehdr.e_shnum = 4;
memory_->SetMemory(0x7000, &ehdr, sizeof(ehdr));
std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
Elf* elf = info.GetElf(process_memory_, false);
ASSERT_TRUE(elf != nullptr);
ASSERT_FALSE(elf->valid());
// Set the name to nothing to verify that it still fails.
delete info.elf;
info.elf = nullptr;
info.name = "";
elf.reset(info.GetElf(process_memory_, false));
elf = info.GetElf(process_memory_, false);
ASSERT_FALSE(elf->valid());
// Change the flags and verify the elf is valid now.
delete info.elf;
info.elf = nullptr;
info.flags = PROT_READ;
elf.reset(info.GetElf(process_memory_, false));
elf = info.GetElf(process_memory_, false);
ASSERT_TRUE(elf->valid());
}
TEST_F(MapInfoGetElfTest, multiple_thread_get_elf) {
static constexpr size_t kNumConcurrentThreads = 100;
Elf64_Ehdr ehdr;
TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_X86_64);
ehdr.e_shoff = 0x2000;
ehdr.e_shentsize = sizeof(Elf64_Shdr) + 100;
ehdr.e_shnum = 4;
memory_->SetMemory(0x7000, &ehdr, sizeof(ehdr));
Elf* elf_in_threads[kNumConcurrentThreads];
std::vector<std::thread*> threads;
std::atomic_bool wait;
wait = true;
// Create all of the threads and have them do the GetElf at the same time
// to make it likely that a race will occur.
MapInfo info(0x7000, 0x8000, 0x1000, PROT_READ, "");
for (size_t i = 0; i < kNumConcurrentThreads; i++) {
std::thread* thread = new std::thread([i, this, &wait, &info, &elf_in_threads]() {
while (wait)
;
Elf* elf = info.GetElf(process_memory_, false);
elf_in_threads[i] = elf;
});
threads.push_back(thread);
}
ASSERT_TRUE(info.elf == nullptr);
// Set them all going and wait for the threads to finish.
wait = false;
for (auto thread : threads) {
thread->join();
delete thread;
}
// Now verify that all of the elf files are exactly the same and valid.
Elf* elf = info.elf;
ASSERT_TRUE(elf != nullptr);
EXPECT_TRUE(elf->valid());
for (size_t i = 0; i < kNumConcurrentThreads; i++) {
EXPECT_EQ(elf, elf_in_threads[i]) << "Thread " << i << " mismatched.";
}
}
} // namespace unwindstack

View file

@ -35,7 +35,12 @@ static void VerifyLine(std::string line, MapInfo* info) {
ASSERT_TRUE(maps.Parse()) << "Failed on: " + line;
MapInfo* element = maps.Get(0);
ASSERT_TRUE(element != nullptr) << "Failed on: " + line;
*info = *element;
info->start = element->start;
info->end = element->end;
info->offset = element->offset;
info->flags = element->flags;
info->name = element->name;
info->elf_offset = element->elf_offset;
}
}
@ -139,38 +144,48 @@ TEST(MapsTest, parse_permissions) {
ASSERT_TRUE(maps.Parse());
ASSERT_EQ(5U, maps.Total());
auto it = maps.begin();
ASSERT_EQ(PROT_NONE, it->flags);
ASSERT_EQ(0x1000U, it->start);
ASSERT_EQ(0x2000U, it->end);
ASSERT_EQ(0U, it->offset);
ASSERT_EQ("", it->name);
++it;
ASSERT_EQ(PROT_READ, it->flags);
ASSERT_EQ(0x2000U, it->start);
ASSERT_EQ(0x3000U, it->end);
ASSERT_EQ(0U, it->offset);
ASSERT_EQ("", it->name);
++it;
ASSERT_EQ(PROT_WRITE, it->flags);
ASSERT_EQ(0x3000U, it->start);
ASSERT_EQ(0x4000U, it->end);
ASSERT_EQ(0U, it->offset);
ASSERT_EQ("", it->name);
++it;
ASSERT_EQ(PROT_EXEC, it->flags);
ASSERT_EQ(0x4000U, it->start);
ASSERT_EQ(0x5000U, it->end);
ASSERT_EQ(0U, it->offset);
ASSERT_EQ("", it->name);
++it;
ASSERT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, it->flags);
ASSERT_EQ(0x5000U, it->start);
ASSERT_EQ(0x6000U, it->end);
ASSERT_EQ(0U, it->offset);
ASSERT_EQ("", it->name);
++it;
ASSERT_EQ(it, maps.end());
MapInfo* info = maps.Get(0);
ASSERT_TRUE(info != nullptr);
EXPECT_EQ(PROT_NONE, info->flags);
EXPECT_EQ(0x1000U, info->start);
EXPECT_EQ(0x2000U, info->end);
EXPECT_EQ(0U, info->offset);
EXPECT_EQ("", info->name);
info = maps.Get(1);
ASSERT_TRUE(info != nullptr);
EXPECT_EQ(PROT_READ, info->flags);
EXPECT_EQ(0x2000U, info->start);
EXPECT_EQ(0x3000U, info->end);
EXPECT_EQ(0U, info->offset);
EXPECT_EQ("", info->name);
info = maps.Get(2);
ASSERT_TRUE(info != nullptr);
EXPECT_EQ(PROT_WRITE, info->flags);
EXPECT_EQ(0x3000U, info->start);
EXPECT_EQ(0x4000U, info->end);
EXPECT_EQ(0U, info->offset);
EXPECT_EQ("", info->name);
info = maps.Get(3);
ASSERT_TRUE(info != nullptr);
EXPECT_EQ(PROT_EXEC, info->flags);
EXPECT_EQ(0x4000U, info->start);
EXPECT_EQ(0x5000U, info->end);
EXPECT_EQ(0U, info->offset);
EXPECT_EQ("", info->name);
info = maps.Get(4);
ASSERT_TRUE(info != nullptr);
EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info->flags);
EXPECT_EQ(0x5000U, info->start);
EXPECT_EQ(0x6000U, info->end);
EXPECT_EQ(0U, info->offset);
EXPECT_EQ("", info->name);
ASSERT_TRUE(maps.Get(5) == nullptr);
}
TEST(MapsTest, parse_name) {
@ -181,26 +196,32 @@ TEST(MapsTest, parse_name) {
ASSERT_TRUE(maps.Parse());
ASSERT_EQ(3U, maps.Total());
auto it = maps.begin();
ASSERT_EQ("", it->name);
ASSERT_EQ(0x7b29b000U, it->start);
ASSERT_EQ(0x7b29e000U, it->end);
ASSERT_EQ(0U, it->offset);
ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
++it;
ASSERT_EQ("/system/lib/fake.so", it->name);
ASSERT_EQ(0x7b29e000U, it->start);
ASSERT_EQ(0x7b29f000U, it->end);
ASSERT_EQ(0U, it->offset);
ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
++it;
ASSERT_EQ("", it->name);
ASSERT_EQ(0x7b29f000U, it->start);
ASSERT_EQ(0x7b2a0000U, it->end);
ASSERT_EQ(0U, it->offset);
ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
++it;
ASSERT_EQ(it, maps.end());
MapInfo* info = maps.Get(0);
ASSERT_TRUE(info != nullptr);
EXPECT_EQ("", info->name);
EXPECT_EQ(0x7b29b000U, info->start);
EXPECT_EQ(0x7b29e000U, info->end);
EXPECT_EQ(0U, info->offset);
EXPECT_EQ(PROT_READ | PROT_WRITE, info->flags);
info = maps.Get(1);
ASSERT_TRUE(info != nullptr);
EXPECT_EQ("/system/lib/fake.so", info->name);
EXPECT_EQ(0x7b29e000U, info->start);
EXPECT_EQ(0x7b29f000U, info->end);
EXPECT_EQ(0U, info->offset);
EXPECT_EQ(PROT_READ | PROT_WRITE, info->flags);
info = maps.Get(2);
ASSERT_TRUE(info != nullptr);
EXPECT_EQ("", info->name);
EXPECT_EQ(0x7b29f000U, info->start);
EXPECT_EQ(0x7b2a0000U, info->end);
EXPECT_EQ(0U, info->offset);
EXPECT_EQ(PROT_READ | PROT_WRITE, info->flags);
ASSERT_TRUE(maps.Get(3) == nullptr);
}
TEST(MapsTest, parse_offset) {
@ -210,20 +231,60 @@ TEST(MapsTest, parse_offset) {
ASSERT_TRUE(maps.Parse());
ASSERT_EQ(2U, maps.Total());
auto it = maps.begin();
ASSERT_EQ(0U, it->offset);
ASSERT_EQ(0xa000U, it->start);
ASSERT_EQ(0xe000U, it->end);
ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
ASSERT_EQ("/system/lib/fake.so", it->name);
MapInfo* info = maps.Get(0);
ASSERT_TRUE(info != nullptr);
EXPECT_EQ(0U, info->offset);
EXPECT_EQ(0xa000U, info->start);
EXPECT_EQ(0xe000U, info->end);
EXPECT_EQ(PROT_READ | PROT_WRITE, info->flags);
EXPECT_EQ("/system/lib/fake.so", info->name);
info = maps.Get(1);
ASSERT_TRUE(info != nullptr);
EXPECT_EQ(0xa12345U, info->offset);
EXPECT_EQ(0xe000U, info->start);
EXPECT_EQ(0xf000U, info->end);
EXPECT_EQ(PROT_READ | PROT_WRITE, info->flags);
EXPECT_EQ("/system/lib/fake.so", info->name);
ASSERT_TRUE(maps.Get(2) == nullptr);
}
TEST(MapsTest, iterate) {
BufferMaps maps(
"a000-e000 rw-p 00000000 00:00 0 /system/lib/fake.so\n"
"e000-f000 rw-p 00a12345 00:00 0 /system/lib/fake.so\n");
ASSERT_TRUE(maps.Parse());
ASSERT_EQ(2U, maps.Total());
Maps::iterator it = maps.begin();
EXPECT_EQ(0xa000U, (*it)->start);
EXPECT_EQ(0xe000U, (*it)->end);
++it;
ASSERT_EQ(0xa12345U, it->offset);
ASSERT_EQ(0xe000U, it->start);
ASSERT_EQ(0xf000U, it->end);
ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
ASSERT_EQ("/system/lib/fake.so", it->name);
EXPECT_EQ(0xe000U, (*it)->start);
EXPECT_EQ(0xf000U, (*it)->end);
++it;
ASSERT_EQ(maps.end(), it);
EXPECT_EQ(maps.end(), it);
}
TEST(MapsTest, const_iterate) {
BufferMaps maps(
"a000-e000 rw-p 00000000 00:00 0 /system/lib/fake.so\n"
"e000-f000 rw-p 00a12345 00:00 0 /system/lib/fake.so\n");
ASSERT_TRUE(maps.Parse());
ASSERT_EQ(2U, maps.Total());
Maps::const_iterator it = maps.begin();
EXPECT_EQ(0xa000U, (*it)->start);
EXPECT_EQ(0xe000U, (*it)->end);
++it;
EXPECT_EQ(0xe000U, (*it)->start);
EXPECT_EQ(0xf000U, (*it)->end);
++it;
EXPECT_EQ(maps.end(), it);
}
TEST(MapsTest, device) {
@ -235,18 +296,23 @@ TEST(MapsTest, device) {
ASSERT_TRUE(maps.Parse());
ASSERT_EQ(4U, maps.Total());
auto it = maps.begin();
ASSERT_TRUE(it->flags & 0x8000);
ASSERT_EQ("/dev/", it->name);
++it;
ASSERT_TRUE(it->flags & 0x8000);
ASSERT_EQ("/dev/does_not_exist", it->name);
++it;
ASSERT_FALSE(it->flags & 0x8000);
ASSERT_EQ("/dev/ashmem/does_not_exist", it->name);
++it;
ASSERT_FALSE(it->flags & 0x8000);
ASSERT_EQ("/devsomething/does_not_exist", it->name);
MapInfo* info = maps.Get(0);
ASSERT_TRUE(info != nullptr);
EXPECT_TRUE(info->flags & 0x8000);
EXPECT_EQ("/dev/", info->name);
info = maps.Get(1);
EXPECT_TRUE(info->flags & 0x8000);
EXPECT_EQ("/dev/does_not_exist", info->name);
info = maps.Get(2);
EXPECT_FALSE(info->flags & 0x8000);
EXPECT_EQ("/dev/ashmem/does_not_exist", info->name);
info = maps.Get(3);
EXPECT_FALSE(info->flags & 0x8000);
EXPECT_EQ("/devsomething/does_not_exist", info->name);
}
TEST(MapsTest, file_smoke) {
@ -263,26 +329,32 @@ TEST(MapsTest, file_smoke) {
ASSERT_TRUE(maps.Parse());
ASSERT_EQ(3U, maps.Total());
auto it = maps.begin();
ASSERT_EQ(0x7b29b000U, it->start);
ASSERT_EQ(0x7b29e000U, it->end);
ASSERT_EQ(0xa0000000U, it->offset);
ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags);
ASSERT_EQ("/fake.so", it->name);
++it;
ASSERT_EQ(0x7b2b0000U, it->start);
ASSERT_EQ(0x7b2e0000U, it->end);
ASSERT_EQ(0xb0000000U, it->offset);
ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags);
ASSERT_EQ("/fake2.so", it->name);
++it;
ASSERT_EQ(0x7b2e0000U, it->start);
ASSERT_EQ(0x7b2f0000U, it->end);
ASSERT_EQ(0xc0000000U, it->offset);
ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags);
ASSERT_EQ("/fake3.so", it->name);
++it;
ASSERT_EQ(it, maps.end());
MapInfo* info = maps.Get(0);
ASSERT_TRUE(info != nullptr);
EXPECT_EQ(0x7b29b000U, info->start);
EXPECT_EQ(0x7b29e000U, info->end);
EXPECT_EQ(0xa0000000U, info->offset);
EXPECT_EQ(PROT_READ | PROT_EXEC, info->flags);
EXPECT_EQ("/fake.so", info->name);
info = maps.Get(1);
ASSERT_TRUE(info != nullptr);
EXPECT_EQ(0x7b2b0000U, info->start);
EXPECT_EQ(0x7b2e0000U, info->end);
EXPECT_EQ(0xb0000000U, info->offset);
EXPECT_EQ(PROT_READ | PROT_EXEC, info->flags);
EXPECT_EQ("/fake2.so", info->name);
info = maps.Get(2);
ASSERT_TRUE(info != nullptr);
EXPECT_EQ(0x7b2e0000U, info->start);
EXPECT_EQ(0x7b2f0000U, info->end);
EXPECT_EQ(0xc0000000U, info->offset);
EXPECT_EQ(PROT_READ | PROT_EXEC, info->flags);
EXPECT_EQ("/fake3.so", info->name);
ASSERT_TRUE(maps.Get(3) == nullptr);
}
TEST(MapsTest, file_no_map_name) {
@ -299,26 +371,32 @@ TEST(MapsTest, file_no_map_name) {
ASSERT_TRUE(maps.Parse());
ASSERT_EQ(3U, maps.Total());
auto it = maps.begin();
ASSERT_EQ(0x7b29b000U, it->start);
ASSERT_EQ(0x7b29e000U, it->end);
ASSERT_EQ(0xa0000000U, it->offset);
ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags);
ASSERT_EQ("", it->name);
++it;
ASSERT_EQ(0x7b2b0000U, it->start);
ASSERT_EQ(0x7b2e0000U, it->end);
ASSERT_EQ(0xb0000000U, it->offset);
ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags);
ASSERT_EQ("/fake2.so", it->name);
++it;
ASSERT_EQ(0x7b2e0000U, it->start);
ASSERT_EQ(0x7b2f0000U, it->end);
ASSERT_EQ(0xc0000000U, it->offset);
ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags);
ASSERT_EQ("", it->name);
++it;
ASSERT_EQ(it, maps.end());
MapInfo* info = maps.Get(0);
ASSERT_TRUE(info != nullptr);
EXPECT_EQ(0x7b29b000U, info->start);
EXPECT_EQ(0x7b29e000U, info->end);
EXPECT_EQ(0xa0000000U, info->offset);
EXPECT_EQ(PROT_READ | PROT_EXEC, info->flags);
EXPECT_EQ("", info->name);
info = maps.Get(1);
ASSERT_TRUE(info != nullptr);
EXPECT_EQ(0x7b2b0000U, info->start);
EXPECT_EQ(0x7b2e0000U, info->end);
EXPECT_EQ(0xb0000000U, info->offset);
EXPECT_EQ(PROT_READ | PROT_EXEC, info->flags);
EXPECT_EQ("/fake2.so", info->name);
info = maps.Get(2);
ASSERT_TRUE(info != nullptr);
EXPECT_EQ(0x7b2e0000U, info->start);
EXPECT_EQ(0x7b2f0000U, info->end);
EXPECT_EQ(0xc0000000U, info->offset);
EXPECT_EQ(PROT_READ | PROT_EXEC, info->flags);
EXPECT_EQ("", info->name);
ASSERT_TRUE(maps.Get(3) == nullptr);
}
// Verify that a file that crosses a buffer is parsed correctly.
@ -435,10 +513,10 @@ TEST(MapsTest, large_file) {
ASSERT_EQ(5000U, maps.Total());
for (size_t i = 0; i < 5000; i++) {
MapInfo* info = maps.Get(i);
ASSERT_EQ(start + i * 4096, info->start) << "Failed at map " + std::to_string(i);
ASSERT_EQ(start + (i + 1) * 4096, info->end) << "Failed at map " + std::to_string(i);
EXPECT_EQ(start + i * 4096, info->start) << "Failed at map " + std::to_string(i);
EXPECT_EQ(start + (i + 1) * 4096, info->end) << "Failed at map " + std::to_string(i);
std::string name = "/fake" + std::to_string(i) + ".so";
ASSERT_EQ(name, info->name) << "Failed at map " + std::to_string(i);
EXPECT_EQ(name, info->name) << "Failed at map " + std::to_string(i);
}
}
@ -452,52 +530,52 @@ TEST(MapsTest, find) {
ASSERT_TRUE(maps.Parse());
ASSERT_EQ(5U, maps.Total());
ASSERT_TRUE(maps.Find(0x500) == nullptr);
ASSERT_TRUE(maps.Find(0x2000) == nullptr);
ASSERT_TRUE(maps.Find(0x5010) == nullptr);
ASSERT_TRUE(maps.Find(0x9a00) == nullptr);
ASSERT_TRUE(maps.Find(0xf000) == nullptr);
ASSERT_TRUE(maps.Find(0xf010) == nullptr);
EXPECT_TRUE(maps.Find(0x500) == nullptr);
EXPECT_TRUE(maps.Find(0x2000) == nullptr);
EXPECT_TRUE(maps.Find(0x5010) == nullptr);
EXPECT_TRUE(maps.Find(0x9a00) == nullptr);
EXPECT_TRUE(maps.Find(0xf000) == nullptr);
EXPECT_TRUE(maps.Find(0xf010) == nullptr);
MapInfo* info = maps.Find(0x1000);
ASSERT_TRUE(info != nullptr);
ASSERT_EQ(0x1000U, info->start);
ASSERT_EQ(0x2000U, info->end);
ASSERT_EQ(0x10U, info->offset);
ASSERT_EQ(PROT_READ, info->flags);
ASSERT_EQ("/system/lib/fake1.so", info->name);
EXPECT_EQ(0x1000U, info->start);
EXPECT_EQ(0x2000U, info->end);
EXPECT_EQ(0x10U, info->offset);
EXPECT_EQ(PROT_READ, info->flags);
EXPECT_EQ("/system/lib/fake1.so", info->name);
info = maps.Find(0x3020);
ASSERT_TRUE(info != nullptr);
ASSERT_EQ(0x3000U, info->start);
ASSERT_EQ(0x4000U, info->end);
ASSERT_EQ(0x20U, info->offset);
ASSERT_EQ(PROT_WRITE, info->flags);
ASSERT_EQ("/system/lib/fake2.so", info->name);
EXPECT_EQ(0x3000U, info->start);
EXPECT_EQ(0x4000U, info->end);
EXPECT_EQ(0x20U, info->offset);
EXPECT_EQ(PROT_WRITE, info->flags);
EXPECT_EQ("/system/lib/fake2.so", info->name);
info = maps.Find(0x6020);
ASSERT_TRUE(info != nullptr);
ASSERT_EQ(0x6000U, info->start);
ASSERT_EQ(0x8000U, info->end);
ASSERT_EQ(0x30U, info->offset);
ASSERT_EQ(PROT_EXEC, info->flags);
ASSERT_EQ("/system/lib/fake3.so", info->name);
EXPECT_EQ(0x6000U, info->start);
EXPECT_EQ(0x8000U, info->end);
EXPECT_EQ(0x30U, info->offset);
EXPECT_EQ(PROT_EXEC, info->flags);
EXPECT_EQ("/system/lib/fake3.so", info->name);
info = maps.Find(0xafff);
ASSERT_TRUE(info != nullptr);
ASSERT_EQ(0xa000U, info->start);
ASSERT_EQ(0xb000U, info->end);
ASSERT_EQ(0x40U, info->offset);
ASSERT_EQ(PROT_READ | PROT_WRITE, info->flags);
ASSERT_EQ("/system/lib/fake4.so", info->name);
EXPECT_EQ(0xa000U, info->start);
EXPECT_EQ(0xb000U, info->end);
EXPECT_EQ(0x40U, info->offset);
EXPECT_EQ(PROT_READ | PROT_WRITE, info->flags);
EXPECT_EQ("/system/lib/fake4.so", info->name);
info = maps.Find(0xe500);
ASSERT_TRUE(info != nullptr);
ASSERT_EQ(0xe000U, info->start);
ASSERT_EQ(0xf000U, info->end);
ASSERT_EQ(0x50U, info->offset);
ASSERT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info->flags);
ASSERT_EQ("/system/lib/fake5.so", info->name);
EXPECT_EQ(0xe000U, info->start);
EXPECT_EQ(0xf000U, info->end);
EXPECT_EQ(0x50U, info->offset);
EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info->flags);
EXPECT_EQ("/system/lib/fake5.so", info->name);
}
} // namespace unwindstack

View file

@ -147,28 +147,29 @@ TEST_F(RegsTest, rel_pc_arm) {
}
TEST_F(RegsTest, elf_invalid) {
Elf invalid_elf(new MemoryFake);
RegsArm regs_arm;
RegsArm64 regs_arm64;
RegsX86 regs_x86;
RegsX86_64 regs_x86_64;
MapInfo map_info{.start = 0x1000, .end = 0x2000};
MapInfo map_info(0x1000, 0x2000);
Elf* invalid_elf = new Elf(new MemoryFake);
map_info.elf = invalid_elf;
regs_arm.set_pc(0x1500);
ASSERT_EQ(0x500U, invalid_elf.GetRelPc(regs_arm.pc(), &map_info));
ASSERT_EQ(0x500U, regs_arm.GetAdjustedPc(0x500U, &invalid_elf));
EXPECT_EQ(0x500U, invalid_elf->GetRelPc(regs_arm.pc(), &map_info));
EXPECT_EQ(0x500U, regs_arm.GetAdjustedPc(0x500U, invalid_elf));
regs_arm64.set_pc(0x1600);
ASSERT_EQ(0x600U, invalid_elf.GetRelPc(regs_arm64.pc(), &map_info));
ASSERT_EQ(0x600U, regs_arm64.GetAdjustedPc(0x600U, &invalid_elf));
EXPECT_EQ(0x600U, invalid_elf->GetRelPc(regs_arm64.pc(), &map_info));
EXPECT_EQ(0x600U, regs_arm64.GetAdjustedPc(0x600U, invalid_elf));
regs_x86.set_pc(0x1700);
ASSERT_EQ(0x700U, invalid_elf.GetRelPc(regs_x86.pc(), &map_info));
ASSERT_EQ(0x700U, regs_x86.GetAdjustedPc(0x700U, &invalid_elf));
EXPECT_EQ(0x700U, invalid_elf->GetRelPc(regs_x86.pc(), &map_info));
EXPECT_EQ(0x700U, regs_x86.GetAdjustedPc(0x700U, invalid_elf));
regs_x86_64.set_pc(0x1800);
ASSERT_EQ(0x800U, invalid_elf.GetRelPc(regs_x86_64.pc(), &map_info));
ASSERT_EQ(0x800U, regs_x86_64.GetAdjustedPc(0x800U, &invalid_elf));
EXPECT_EQ(0x800U, invalid_elf->GetRelPc(regs_x86_64.pc(), &map_info));
EXPECT_EQ(0x800U, regs_x86_64.GetAdjustedPc(0x800U, invalid_elf));
}
TEST_F(RegsTest, arm_set_from_raw) {

View file

@ -311,4 +311,39 @@ TEST_F(UnwindTest, remote_through_signal_sa_siginfo_with_invalid_func) {
RemoteThroughSignal(SIGSEGV, SA_SIGINFO);
}
// Verify that using the same map while unwinding multiple threads at the
// same time doesn't cause problems.
TEST_F(UnwindTest, multiple_threads_unwind_same_map) {
static constexpr size_t kNumConcurrentThreads = 100;
LocalMaps maps;
ASSERT_TRUE(maps.Parse());
auto process_memory(Memory::CreateProcessMemory(getpid()));
std::vector<std::thread*> threads;
std::atomic_bool wait;
wait = true;
size_t frames[kNumConcurrentThreads];
for (size_t i = 0; i < kNumConcurrentThreads; i++) {
std::thread* thread = new std::thread([i, &frames, &maps, &process_memory, &wait]() {
while (wait)
;
std::unique_ptr<Regs> regs(Regs::CreateFromLocal());
RegsGetLocal(regs.get());
Unwinder unwinder(512, &maps, regs.get(), process_memory);
unwinder.Unwind();
frames[i] = unwinder.NumFrames();
ASSERT_LE(3U, frames[i]) << "Failed for thread " << i;
});
threads.push_back(thread);
}
wait = false;
for (auto thread : threads) {
thread->join();
delete thread;
}
}
} // namespace unwindstack

View file

@ -45,82 +45,51 @@ class MapsFake : public Maps {
void FakeClear() { maps_.clear(); }
void FakeAddMapInfo(const MapInfo& map_info) { maps_.push_back(map_info); }
void FakeAddMapInfo(MapInfo* map_info) { maps_.push_back(map_info); }
};
class UnwinderTest : public ::testing::Test {
protected:
static void SetUpTestCase() {
maps_.FakeClear();
MapInfo info;
info.name = "/system/fake/libc.so";
info.start = 0x1000;
info.end = 0x8000;
info.offset = 0;
info.flags = PROT_READ | PROT_WRITE;
MapInfo* info = new MapInfo(0x1000, 0x8000, 0, PROT_READ | PROT_WRITE, "/system/fake/libc.so");
ElfFake* elf = new ElfFake(nullptr);
info.elf = elf;
info->elf = elf;
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
info.elf_offset = 0;
maps_.FakeAddMapInfo(info);
info.name = "[stack]";
info.start = 0x10000;
info.end = 0x12000;
info.flags = PROT_READ | PROT_WRITE;
info.elf = nullptr;
info = new MapInfo(0x10000, 0x12000, 0, PROT_READ | PROT_WRITE, "[stack]");
maps_.FakeAddMapInfo(info);
info.name = "/dev/fake_device";
info.start = 0x13000;
info.end = 0x15000;
info.flags = PROT_READ | PROT_WRITE | MAPS_FLAGS_DEVICE_MAP;
info.elf = nullptr;
info = new MapInfo(0x13000, 0x15000, 0, PROT_READ | PROT_WRITE | MAPS_FLAGS_DEVICE_MAP,
"/dev/fake_device");
maps_.FakeAddMapInfo(info);
info.name = "/system/fake/libunwind.so";
info.start = 0x20000;
info.end = 0x22000;
info.flags = PROT_READ | PROT_WRITE;
info = new MapInfo(0x20000, 0x22000, 0, PROT_READ | PROT_WRITE, "/system/fake/libunwind.so");
elf = new ElfFake(nullptr);
info.elf = elf;
info->elf = elf;
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
maps_.FakeAddMapInfo(info);
info.name = "/fake/libanother.so";
info.start = 0x23000;
info.end = 0x24000;
info.flags = PROT_READ | PROT_WRITE;
info = new MapInfo(0x23000, 0x24000, 0, PROT_READ | PROT_WRITE, "/fake/libanother.so");
elf = new ElfFake(nullptr);
info.elf = elf;
info->elf = elf;
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
maps_.FakeAddMapInfo(info);
info.name = "/fake/compressed.so";
info.start = 0x33000;
info.end = 0x34000;
info.flags = PROT_READ | PROT_WRITE;
info = new MapInfo(0x33000, 0x34000, 0, PROT_READ | PROT_WRITE, "/fake/compressed.so");
elf = new ElfFake(nullptr);
info.elf = elf;
info->elf = elf;
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
maps_.FakeAddMapInfo(info);
info.name = "/fake/fake.apk";
info.start = 0x43000;
info.end = 0x44000;
info.offset = 0x1d000;
info.flags = PROT_READ | PROT_WRITE;
info = new MapInfo(0x43000, 0x44000, 0x1d000, PROT_READ | PROT_WRITE, "/fake/fake.apk");
elf = new ElfFake(nullptr);
info.elf = elf;
info->elf = elf;
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
maps_.FakeAddMapInfo(info);
info.name = "/fake/fake.oat";
info.start = 0x53000;
info.end = 0x54000;
info.offset = 0;
info.flags = PROT_READ | PROT_WRITE;
info.elf = nullptr;
info = new MapInfo(0x53000, 0x54000, 0, PROT_READ | PROT_WRITE, "/fake/fake.oat");
maps_.FakeAddMapInfo(info);
}