blob: 2897a06a90a9520b37c6ac82baf8ee7d95d1ae08 [file] [log] [blame]
/*
* Copyright (C) 2018 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 "JITDebugReader.h"
#include <inttypes.h>
#include <sys/mman.h>
#include <sys/uio.h>
#include <sys/user.h>
#include <algorithm>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/strings.h>
#include "dso.h"
#include "environment.h"
#include "read_apk.h"
#include "read_elf.h"
#include "utils.h"
namespace simpleperf {
// If the size of a symfile is larger than EXPECTED_MAX_SYMFILE_SIZE, we don't want to read it
// remotely.
static constexpr size_t MAX_JIT_SYMFILE_SIZE = 1024 * 1024u;
// It takes about 30us-130us on Pixel (depending on the cpu frequency) to check if the descriptors
// have been updated (most time spent in process_vm_preadv). We want to know if the JIT debug info
// changed as soon as possible, while not wasting too much time checking for updates. So use a
// period of 100 ms.
// In system wide profiling, we may need to check JIT debug info changes for many processes, to
// avoid spending all time checking, wait 100 ms between any two checks.
static constexpr size_t kUpdateJITDebugInfoIntervalInMs = 100;
// Match the format of JITDescriptor in art/runtime/jit/debugger_interface.cc.
template <typename ADDRT>
struct JITDescriptor {
uint32_t version;
uint32_t action_flag;
ADDRT relevant_entry_addr;
ADDRT first_entry_addr;
uint8_t magic[8];
uint32_t flags;
uint32_t sizeof_descriptor;
uint32_t sizeof_entry;
uint32_t action_seqlock; // incremented before and after any modification
uint64_t action_timestamp; // CLOCK_MONOTONIC time of last action
bool Valid() const;
int AndroidVersion() const {
return magic[7] - '0';
}
};
// Match the format of JITCodeEntry in art/runtime/jit/debugger_interface.cc
// with JITDescriptor.magic == "Android1".
template <typename ADDRT>
struct JITCodeEntry {
ADDRT next_addr;
ADDRT prev_addr;
ADDRT symfile_addr;
uint64_t symfile_size;
uint64_t register_timestamp; // CLOCK_MONOTONIC time of entry registration
bool Valid() const {
return symfile_addr > 0u && symfile_size > 0u;
}
};
// Match the format of JITCodeEntry in art/runtime/jit/debugger_interface.cc
// with JITDescriptor.magic == "Android1".
template <typename ADDRT>
struct __attribute__((packed)) PackedJITCodeEntry {
ADDRT next_addr;
ADDRT prev_addr;
ADDRT symfile_addr;
uint64_t symfile_size;
uint64_t register_timestamp;
bool Valid() const {
return symfile_addr > 0u && symfile_size > 0u;
}
};
// Match the format of JITCodeEntry in art/runtime/jit/debugger_interface.cc
// with JITDescriptor.magic == "Android2".
template <typename ADDRT>
struct JITCodeEntryV2 {
ADDRT next_addr;
ADDRT prev_addr;
ADDRT symfile_addr;
uint64_t symfile_size;
uint64_t register_timestamp; // CLOCK_MONOTONIC time of entry registration
uint32_t seqlock; // even value if valid
bool Valid() const {
return (seqlock & 1) == 0;
}
};
// Match the format of JITCodeEntry in art/runtime/jit/debugger_interface.cc
// with JITDescriptor.magic == "Android2".
template <typename ADDRT>
struct __attribute__((packed)) PackedJITCodeEntryV2 {
ADDRT next_addr;
ADDRT prev_addr;
ADDRT symfile_addr;
uint64_t symfile_size;
uint64_t register_timestamp;
uint32_t seqlock;
bool Valid() const {
return (seqlock & 1) == 0;
}
};
// Match the format of JITCodeEntry in art/runtime/jit/debugger_interface.cc
// with JITDescriptor.magic == "Android2".
template <typename ADDRT>
struct __attribute__((packed)) PaddedJITCodeEntryV2 {
ADDRT next_addr;
ADDRT prev_addr;
ADDRT symfile_addr;
uint64_t symfile_size;
uint64_t register_timestamp;
uint32_t seqlock;
uint32_t pad;
bool Valid() const {
return (seqlock & 1) == 0;
}
};
using JITDescriptor32 = JITDescriptor<uint32_t>;
using JITDescriptor64 = JITDescriptor<uint64_t>;
#if defined(__x86_64__)
// Make sure simpleperf built for i386 and x86_64 see the correct JITCodeEntry layout of i386.
using JITCodeEntry32 = PackedJITCodeEntry<uint32_t>;
using JITCodeEntry32V2 = PackedJITCodeEntryV2<uint32_t>;
#else
using JITCodeEntry32 = JITCodeEntry<uint32_t>;
using JITCodeEntry32V2 = JITCodeEntryV2<uint32_t>;
#endif
using JITCodeEntry64 = JITCodeEntry<uint64_t>;
#if defined(__i386__)
// Make sure simpleperf built for i386 and x86_64 see the correct JITCodeEntry layout of x86_64.
using JITCodeEntry64V2 = PaddedJITCodeEntryV2<uint64_t>;
#else
using JITCodeEntry64V2 = JITCodeEntryV2<uint64_t>;
#endif
template <typename ADDRT>
bool JITDescriptor<ADDRT>::Valid() const {
const char* magic_str = reinterpret_cast<const char*>(magic);
if (version != 1 ||
!(strncmp(magic_str, "Android1", 8) == 0 || strncmp(magic_str, "Android2", 8) == 0)) {
return false;
}
if (sizeof(*this) != sizeof_descriptor) {
return false;
}
if (sizeof(ADDRT) == 4) {
return sizeof_entry == (AndroidVersion() == 1) ? sizeof(JITCodeEntry32)
: sizeof(JITCodeEntry32V2);
}
return sizeof_entry == (AndroidVersion() == 1) ? sizeof(JITCodeEntry64)
: sizeof(JITCodeEntry64V2);
}
// We want to support both 64-bit and 32-bit simpleperf when profiling either 64-bit or 32-bit
// apps. So using static_asserts to make sure that simpleperf on arm and aarch64 having the same
// view of structures, and simpleperf on i386 and x86_64 having the same view of structures.
static_assert(sizeof(JITDescriptor32) == 48, "");
static_assert(sizeof(JITDescriptor64) == 56, "");
#if defined(__i386__) or defined(__x86_64__)
static_assert(sizeof(JITCodeEntry32) == 28, "");
static_assert(sizeof(JITCodeEntry32V2) == 32, "");
static_assert(sizeof(JITCodeEntry64) == 40, "");
static_assert(sizeof(JITCodeEntry64V2) == 48, "");
#else
static_assert(sizeof(JITCodeEntry32) == 32, "");
static_assert(sizeof(JITCodeEntry32V2) == 40, "");
static_assert(sizeof(JITCodeEntry64) == 40, "");
static_assert(sizeof(JITCodeEntry64V2) == 48, "");
#endif
bool JITDebugReader::RegisterDebugInfoCallback(IOEventLoop* loop,
const debug_info_callback_t& callback) {
debug_info_callback_ = callback;
read_event_ = loop->AddPeriodicEvent(SecondToTimeval(kUpdateJITDebugInfoIntervalInMs / 1000.0),
[this]() { return ReadAllProcesses(); });
return (read_event_ != nullptr && IOEventLoop::DisableEvent(read_event_));
}
bool JITDebugReader::MonitorProcess(pid_t pid) {
if (processes_.find(pid) == processes_.end()) {
processes_[pid].pid = pid;
LOG(DEBUG) << "Start monitoring process " << pid;
if (processes_.size() == 1u) {
if (!IOEventLoop::EnableEvent(read_event_)) {
return false;
}
}
}
return true;
}
static bool IsArtLib(const std::string& filename) {
return android::base::EndsWith(filename, "libart.so") ||
android::base::EndsWith(filename, "libartd.so");
}
bool JITDebugReader::UpdateRecord(const Record* record) {
if (record->type() == PERF_RECORD_MMAP) {
auto r = static_cast<const MmapRecord*>(record);
if (IsArtLib(r->filename)) {
pids_with_art_lib_.emplace(r->data->pid, false);
}
} else if (record->type() == PERF_RECORD_MMAP2) {
auto r = static_cast<const Mmap2Record*>(record);
if (IsArtLib(r->filename)) {
pids_with_art_lib_.emplace(r->data->pid, false);
}
} else if (record->type() == PERF_RECORD_FORK) {
auto r = static_cast<const ForkRecord*>(record);
if (r->data->pid != r->data->ppid &&
pids_with_art_lib_.find(r->data->ppid) != pids_with_art_lib_.end()) {
pids_with_art_lib_.emplace(r->data->pid, false);
}
} else if (record->type() == PERF_RECORD_SAMPLE) {
auto r = static_cast<const SampleRecord*>(record);
auto it = pids_with_art_lib_.find(r->tid_data.pid);
if (it != pids_with_art_lib_.end() && !it->second) {
it->second = true;
if (!MonitorProcess(r->tid_data.pid)) {
return false;
}
return ReadProcess(r->tid_data.pid);
}
}
return FlushDebugInfo(record->Timestamp());
}
bool JITDebugReader::FlushDebugInfo(uint64_t timestamp) {
if (sync_with_records_) {
if (!debug_info_q_.empty() && debug_info_q_.top().timestamp < timestamp) {
std::vector<JITDebugInfo> debug_info;
while (!debug_info_q_.empty() && debug_info_q_.top().timestamp < timestamp) {
debug_info.emplace_back(debug_info_q_.top());
debug_info_q_.pop();
}
return debug_info_callback_(debug_info, false);
}
}
return true;
}
bool JITDebugReader::ReadAllProcesses() {
if (!IOEventLoop::DisableEvent(read_event_)) {
return false;
}
std::vector<JITDebugInfo> debug_info;
for (auto it = processes_.begin(); it != processes_.end();) {
Process& process = it->second;
ReadProcess(process, &debug_info);
if (process.died) {
LOG(DEBUG) << "Stop monitoring process " << process.pid;
it = processes_.erase(it);
} else {
++it;
}
}
if (!AddDebugInfo(debug_info, true)) {
return false;
}
if (!processes_.empty()) {
return IOEventLoop::EnableEvent(read_event_);
}
return true;
}
bool JITDebugReader::ReadProcess(pid_t pid) {
auto it = processes_.find(pid);
if (it != processes_.end()) {
std::vector<JITDebugInfo> debug_info;
ReadProcess(it->second, &debug_info);
return AddDebugInfo(debug_info, false);
}
return true;
}
void JITDebugReader::ReadProcess(Process& process, std::vector<JITDebugInfo>* debug_info) {
if (process.died || (!process.initialized && !InitializeProcess(process))) {
return;
}
// 1. Read descriptors.
Descriptor jit_descriptor;
Descriptor dex_descriptor;
if (!ReadDescriptors(process, &jit_descriptor, &dex_descriptor)) {
return;
}
// 2. Return if descriptors are not changed.
if (jit_descriptor.action_seqlock == process.last_jit_descriptor.action_seqlock &&
dex_descriptor.action_seqlock == process.last_dex_descriptor.action_seqlock) {
return;
}
// 3. Read new symfiles.
auto check_descriptor = [&](Descriptor& descriptor, bool is_jit) {
Descriptor tmp_jit_descriptor;
Descriptor tmp_dex_descriptor;
if (!ReadDescriptors(process, &tmp_jit_descriptor, &tmp_dex_descriptor)) {
return false;
}
if (is_jit) {
return descriptor.action_seqlock == tmp_jit_descriptor.action_seqlock;
}
return descriptor.action_seqlock == tmp_dex_descriptor.action_seqlock;
};
auto read_debug_info = [&](Descriptor& new_descriptor, Descriptor& old_descriptor, bool is_jit) {
bool has_update = new_descriptor.action_seqlock != old_descriptor.action_seqlock &&
(new_descriptor.action_seqlock & 1) == 0;
LOG(DEBUG) << (is_jit ? "JIT" : "Dex") << " symfiles of pid " << process.pid
<< ": old seqlock " << old_descriptor.action_seqlock
<< ", new seqlock " << new_descriptor.action_seqlock;
if (!has_update) {
return false;
}
std::vector<CodeEntry> new_entries;
// Adding or removing one code entry will make two increments of action_seqlock. So we should
// not read more than (seqlock_diff / 2) new entries.
uint32_t read_entry_limit = (new_descriptor.action_seqlock - old_descriptor.action_seqlock) / 2;
if (!ReadNewCodeEntries(process, new_descriptor, old_descriptor.action_timestamp,
read_entry_limit, &new_entries)) {
return false;
}
// Check if the descriptor was changed while we were reading new entries.
if (!check_descriptor(new_descriptor, is_jit)) {
return false;
}
LOG(DEBUG) << (is_jit ? "JIT" : "Dex") << " symfiles of pid " << process.pid
<< ": read " << new_entries.size() << " new entries";
if (new_entries.empty()) {
return true;
}
if (is_jit) {
ReadJITCodeDebugInfo(process, new_entries, debug_info);
} else {
ReadDexFileDebugInfo(process, new_entries, debug_info);
}
return true;
};
if (read_debug_info(jit_descriptor, process.last_jit_descriptor, true)) {
process.last_jit_descriptor = jit_descriptor;
}
if (read_debug_info(dex_descriptor, process.last_dex_descriptor, false)) {
process.last_dex_descriptor = dex_descriptor;
}
}
bool JITDebugReader::InitializeProcess(Process& process) {
// 1. Read map file to find the location of libart.so.
std::vector<ThreadMmap> thread_mmaps;
if (!GetThreadMmapsInProcess(process.pid, &thread_mmaps)) {
process.died = true;
return false;
}
std::string art_lib_path;
uint64_t min_vaddr_in_memory;
for (auto& map : thread_mmaps) {
if ((map.prot & PROT_EXEC) && IsArtLib(map.name)) {
art_lib_path = map.name;
min_vaddr_in_memory = map.start_addr;
break;
}
}
if (art_lib_path.empty()) {
return false;
}
process.is_64bit = art_lib_path.find("lib64") != std::string::npos;
// 2. Read libart.so to find the addresses of __jit_debug_descriptor and __dex_debug_descriptor.
const DescriptorsLocation* location = GetDescriptorsLocation(art_lib_path, process.is_64bit);
if (location == nullptr) {
return false;
}
process.descriptors_addr = location->relative_addr + min_vaddr_in_memory;
process.descriptors_size = location->size;
process.jit_descriptor_offset = location->jit_descriptor_offset;
process.dex_descriptor_offset = location->dex_descriptor_offset;
process.initialized = true;
return true;
}
const JITDebugReader::DescriptorsLocation* JITDebugReader::GetDescriptorsLocation(
const std::string& art_lib_path, bool is_64bit) {
auto it = descriptors_location_cache_.find(art_lib_path);
if (it != descriptors_location_cache_.end()) {
return it->second.relative_addr == 0u ? nullptr : &it->second;
}
DescriptorsLocation& location = descriptors_location_cache_[art_lib_path];
// Read libart.so to find the addresses of __jit_debug_descriptor and __dex_debug_descriptor.
uint64_t min_vaddr_in_file;
uint64_t file_offset;
ElfStatus status = ReadMinExecutableVirtualAddressFromElfFile(art_lib_path, BuildId(),
&min_vaddr_in_file,
&file_offset);
if (status != ElfStatus::NO_ERROR) {
LOG(ERROR) << "ReadMinExecutableVirtualAddress failed, status = " << status;
return nullptr;
}
// min_vaddr_in_file is the min vaddr of executable segments. It may not be page aligned.
// And dynamic linker will create map mapping to (segment.p_vaddr & PAGE_MASK).
uint64_t aligned_segment_vaddr = min_vaddr_in_file & PAGE_MASK;
const char* jit_str = "__jit_debug_descriptor";
const char* dex_str = "__dex_debug_descriptor";
uint64_t jit_addr = 0u;
uint64_t dex_addr = 0u;
auto callback = [&](const ElfFileSymbol& symbol) {
if (symbol.name == jit_str) {
jit_addr = symbol.vaddr - aligned_segment_vaddr;
} else if (symbol.name == dex_str) {
dex_addr = symbol.vaddr - aligned_segment_vaddr;
}
};
if (ParseDynamicSymbolsFromElfFile(art_lib_path, callback) != ElfStatus::NO_ERROR) {
return nullptr;
}
if (jit_addr == 0u || dex_addr == 0u) {
return nullptr;
}
location.relative_addr = std::min(jit_addr, dex_addr);
location.size = std::max(jit_addr, dex_addr) +
(is_64bit ? sizeof(JITDescriptor64) : sizeof(JITDescriptor32)) - location.relative_addr;
if (location.size >= 4096u) {
PLOG(WARNING) << "The descriptors_size is unexpected large: " << location.size;
}
if (descriptors_buf_.size() < location.size) {
descriptors_buf_.resize(location.size);
}
location.jit_descriptor_offset = jit_addr - location.relative_addr;
location.dex_descriptor_offset = dex_addr - location.relative_addr;
return &location;
}
bool JITDebugReader::ReadRemoteMem(Process& process, uint64_t remote_addr, uint64_t size,
void* data) {
iovec local_iov;
local_iov.iov_base = data;
local_iov.iov_len = size;
iovec remote_iov;
remote_iov.iov_base = reinterpret_cast<void*>(static_cast<uintptr_t>(remote_addr));
remote_iov.iov_len = size;
ssize_t result = process_vm_readv(process.pid, &local_iov, 1, &remote_iov, 1, 0);
if (static_cast<size_t>(result) != size) {
PLOG(DEBUG) << "ReadRemoteMem(" << " pid " << process.pid << ", addr " << std::hex
<< remote_addr << ", size " << size << ") failed";
process.died = true;
return false;
}
return true;
}
bool JITDebugReader::ReadDescriptors(Process& process, Descriptor* jit_descriptor,
Descriptor* dex_descriptor) {
if (!ReadRemoteMem(process, process.descriptors_addr, process.descriptors_size,
descriptors_buf_.data())) {
return false;
}
return LoadDescriptor(process.is_64bit, &descriptors_buf_[process.jit_descriptor_offset],
jit_descriptor) &&
LoadDescriptor(process.is_64bit, &descriptors_buf_[process.dex_descriptor_offset],
dex_descriptor);
}
bool JITDebugReader::LoadDescriptor(bool is_64bit, const char* data, Descriptor* descriptor) {
if (is_64bit) {
return LoadDescriptorImpl<JITDescriptor64>(data, descriptor);
}
return LoadDescriptorImpl<JITDescriptor32>(data, descriptor);
}
template <typename DescriptorT>
bool JITDebugReader::LoadDescriptorImpl(const char* data, Descriptor* descriptor) {
DescriptorT raw_descriptor;
MoveFromBinaryFormat(raw_descriptor, data);
if (!raw_descriptor.Valid()) {
return false;
}
descriptor->action_seqlock = raw_descriptor.action_seqlock;
descriptor->action_timestamp = raw_descriptor.action_timestamp;
descriptor->first_entry_addr = raw_descriptor.first_entry_addr;
descriptor->version = raw_descriptor.AndroidVersion();
return true;
}
// Read new code entries with timestamp > last_action_timestamp.
// Since we don't stop the app process while reading code entries, it is possible we are reading
// broken data. So return false once we detect that the data is broken.
bool JITDebugReader::ReadNewCodeEntries(Process& process, const Descriptor& descriptor,
uint64_t last_action_timestamp, uint32_t read_entry_limit,
std::vector<CodeEntry>* new_code_entries) {
if (descriptor.version == 1) {
if (process.is_64bit) {
return ReadNewCodeEntriesImpl<JITCodeEntry64>(
process, descriptor, last_action_timestamp, read_entry_limit, new_code_entries);
}
return ReadNewCodeEntriesImpl<JITCodeEntry32>(
process, descriptor, last_action_timestamp, read_entry_limit, new_code_entries);
}
if (descriptor.version == 2) {
if (process.is_64bit) {
return ReadNewCodeEntriesImplV2<JITCodeEntry64V2>(
process, descriptor, last_action_timestamp, read_entry_limit, new_code_entries);
}
return ReadNewCodeEntriesImplV2<JITCodeEntry32V2>(
process, descriptor, last_action_timestamp, read_entry_limit, new_code_entries);
}
return false;
}
template <typename CodeEntryT>
bool JITDebugReader::ReadNewCodeEntriesImpl(Process& process, const Descriptor& descriptor,
uint64_t last_action_timestamp,
uint32_t read_entry_limit,
std::vector<CodeEntry>* new_code_entries) {
uint64_t current_entry_addr = descriptor.first_entry_addr;
uint64_t prev_entry_addr = 0u;
std::unordered_set<uint64_t> entry_addr_set;
for (size_t i = 0u; i < read_entry_limit && current_entry_addr != 0u; ++i) {
if (entry_addr_set.find(current_entry_addr) != entry_addr_set.end()) {
// We enter a loop, which means a broken linked list.
return false;
}
CodeEntryT entry;
if (!ReadRemoteMem(process, current_entry_addr, sizeof(entry), &entry)) {
return false;
}
if (entry.prev_addr != prev_entry_addr || !entry.Valid()) {
// A broken linked list
return false;
}
if (entry.register_timestamp <= last_action_timestamp) {
// The linked list has entries with timestamp in decreasing order. So stop searching
// once we hit an entry with timestamp <= last_action_timestmap.
break;
}
if (entry.symfile_size > 0) {
CodeEntry code_entry;
code_entry.addr = current_entry_addr;
code_entry.symfile_addr = entry.symfile_addr;
code_entry.symfile_size = entry.symfile_size;
code_entry.timestamp = entry.register_timestamp;
new_code_entries->push_back(code_entry);
}
entry_addr_set.insert(current_entry_addr);
prev_entry_addr = current_entry_addr;
current_entry_addr = entry.next_addr;
}
return true;
}
// Temporary work around for patch "JIT mini-debug-info: Append packed entries towards end.", which
// adds new entries at the end of the list and forces simpleperf to read the whole list.
template <typename CodeEntryT>
bool JITDebugReader::ReadNewCodeEntriesImplV2(Process& process, const Descriptor& descriptor,
uint64_t last_action_timestamp,
uint32_t /* read_entry_limit */,
std::vector<CodeEntry>* new_code_entries) {
uint64_t current_entry_addr = descriptor.first_entry_addr;
uint64_t prev_entry_addr = 0u;
std::unordered_set<uint64_t> entry_addr_set;
const size_t READ_ENTRY_LIMIT = 10000; // to avoid endless loop
for (size_t i = 0u; i < READ_ENTRY_LIMIT && current_entry_addr != 0u; ++i) {
if (entry_addr_set.find(current_entry_addr) != entry_addr_set.end()) {
// We enter a loop, which means a broken linked list.
return false;
}
CodeEntryT entry;
if (!ReadRemoteMem(process, current_entry_addr, sizeof(entry), &entry)) {
return false;
}
if (entry.prev_addr != prev_entry_addr || !entry.Valid()) {
// A broken linked list
return false;
}
if (entry.symfile_size > 0 && entry.register_timestamp > last_action_timestamp) {
CodeEntry code_entry;
code_entry.addr = current_entry_addr;
code_entry.symfile_addr = entry.symfile_addr;
code_entry.symfile_size = entry.symfile_size;
code_entry.timestamp = entry.register_timestamp;
new_code_entries->push_back(code_entry);
}
entry_addr_set.insert(current_entry_addr);
prev_entry_addr = current_entry_addr;
current_entry_addr = entry.next_addr;
}
return true;
}
void JITDebugReader::ReadJITCodeDebugInfo(Process& process,
const std::vector<CodeEntry>& jit_entries,
std::vector<JITDebugInfo>* debug_info) {
std::vector<char> data;
for (auto& jit_entry : jit_entries) {
if (jit_entry.symfile_size > MAX_JIT_SYMFILE_SIZE) {
continue;
}
if (data.size() < jit_entry.symfile_size) {
data.resize(jit_entry.symfile_size);
}
if (!ReadRemoteMem(process, jit_entry.symfile_addr, jit_entry.symfile_size, data.data())) {
continue;
}
if (!IsValidElfFileMagic(data.data(), jit_entry.symfile_size)) {
continue;
}
std::unique_ptr<TemporaryFile> tmp_file = ScopedTempFiles::CreateTempFile(!keep_symfiles_);
if (tmp_file == nullptr || !android::base::WriteFully(tmp_file->fd, data.data(),
jit_entry.symfile_size)) {
continue;
}
if (keep_symfiles_) {
tmp_file->DoNotRemove();
}
auto callback = [&](const ElfFileSymbol& symbol) {
if (symbol.len == 0) { // Some arm labels can have zero length.
return;
}
LOG(VERBOSE) << "JITSymbol " << symbol.name << " at [" << std::hex << symbol.vaddr
<< " - " << (symbol.vaddr + symbol.len) << " with size " << symbol.len;
debug_info->emplace_back(process.pid, jit_entry.timestamp, symbol.vaddr, symbol.len,
tmp_file->path);
};
ParseSymbolsFromElfFileInMemory(data.data(), jit_entry.symfile_size, callback);
}
}
void JITDebugReader::ReadDexFileDebugInfo(Process& process,
const std::vector<CodeEntry>& dex_entries,
std::vector<JITDebugInfo>* debug_info) {
std::vector<ThreadMmap> thread_mmaps;
if (!GetThreadMmapsInProcess(process.pid, &thread_mmaps)) {
process.died = true;
return;
}
auto comp = [](const ThreadMmap& map, uint64_t addr) {
return map.start_addr <= addr;
};
for (auto& dex_entry : dex_entries) {
auto it = std::lower_bound(thread_mmaps.begin(), thread_mmaps.end(),
dex_entry.symfile_addr, comp);
if (it == thread_mmaps.begin()) {
continue;
}
--it;
if (it->start_addr + it->len < dex_entry.symfile_addr + dex_entry.symfile_size) {
continue;
}
std::string file_path;
std::string zip_path;
std::string entry_path;
std::shared_ptr<ThreadMmap> extracted_dex_file_map;
if (ParseExtractedInMemoryPath(it->name, &zip_path, &entry_path)) {
file_path = GetUrlInApk(zip_path, entry_path);
extracted_dex_file_map = std::make_shared<ThreadMmap>(*it);
} else {
if (!IsRegularFile(it->name)) {
// TODO: read dex file only exist in memory?
continue;
}
file_path = it->name;
}
// Offset of dex file in .vdex file or .apk file.
uint64_t dex_file_offset = dex_entry.symfile_addr - it->start_addr + it->pgoff;
debug_info->emplace_back(process.pid, dex_entry.timestamp, dex_file_offset, file_path,
extracted_dex_file_map);
LOG(VERBOSE) << "DexFile " << file_path << "+" << std::hex << dex_file_offset
<< " in map [" << it->start_addr << " - " << (it->start_addr + it->len)
<< "] with size " << dex_entry.symfile_size;
}
}
bool JITDebugReader::AddDebugInfo(const std::vector<JITDebugInfo>& debug_info,
bool sync_kernel_records) {
if (!debug_info.empty()) {
if (sync_with_records_) {
for (auto& info : debug_info) {
debug_info_q_.push(std::move(info));
}
} else {
return debug_info_callback_(debug_info, sync_kernel_records);
}
}
return true;
}
} // namespace simpleperf