You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
242 lines
9.1 KiB
242 lines
9.1 KiB
/*
|
|
* 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.
|
|
*/
|
|
|
|
#ifndef SIMPLE_PERF_JIT_DEBUG_READER_H_
|
|
#define SIMPLE_PERF_JIT_DEBUG_READER_H_
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <functional>
|
|
#include <memory>
|
|
#include <queue>
|
|
#include <stack>
|
|
#include <unordered_map>
|
|
#include <unordered_set>
|
|
#include <vector>
|
|
|
|
#include <android-base/file.h>
|
|
#include <android-base/logging.h>
|
|
|
|
#include "IOEventLoop.h"
|
|
#include "environment.h"
|
|
#include "record.h"
|
|
|
|
namespace simpleperf {
|
|
|
|
inline constexpr const char* kJITAppCacheFile = "jit_app_cache";
|
|
inline constexpr const char* kJITZygoteCacheFile = "jit_zygote_cache";
|
|
|
|
// JITDebugInfo represents the debug info of a JITed Java method or a dex file.
|
|
struct JITDebugInfo {
|
|
enum {
|
|
JIT_DEBUG_JIT_CODE,
|
|
JIT_DEBUG_DEX_FILE,
|
|
} type;
|
|
pid_t pid; // Process of the debug info
|
|
uint64_t timestamp; // Monotonic timestamp for the creation of the debug info
|
|
union {
|
|
struct {
|
|
uint64_t jit_code_addr; // The start addr of the JITed code
|
|
uint64_t jit_code_len; // The end addr of the JITed code
|
|
};
|
|
uint64_t dex_file_offset; // The offset of the dex file in the file containing it
|
|
};
|
|
// For JITed code, it is the path of a temporary ELF file storing its debug info.
|
|
// For dex file, it is the path of the file containing the dex file.
|
|
std::string file_path;
|
|
uint64_t file_offset;
|
|
|
|
// The map for dex file extracted in memory. On Android Q, ART extracts dex files in apk files
|
|
// directly into memory, and names it using prctl(). The kernel doesn't generate a new mmap
|
|
// record for it. So we need to dump it manually.
|
|
std::shared_ptr<ThreadMmap> extracted_dex_file_map;
|
|
|
|
JITDebugInfo(pid_t pid, uint64_t timestamp, uint64_t jit_code_addr, uint64_t jit_code_len,
|
|
const std::string& file_path, uint64_t file_offset)
|
|
: type(JIT_DEBUG_JIT_CODE),
|
|
pid(pid),
|
|
timestamp(timestamp),
|
|
jit_code_addr(jit_code_addr),
|
|
jit_code_len(jit_code_len),
|
|
file_path(file_path),
|
|
file_offset(file_offset) {}
|
|
|
|
JITDebugInfo(pid_t pid, uint64_t timestamp, uint64_t dex_file_offset,
|
|
const std::string& file_path,
|
|
const std::shared_ptr<ThreadMmap>& extracted_dex_file_map)
|
|
: type(JIT_DEBUG_DEX_FILE),
|
|
pid(pid),
|
|
timestamp(timestamp),
|
|
dex_file_offset(dex_file_offset),
|
|
file_path(file_path),
|
|
file_offset(0),
|
|
extracted_dex_file_map(extracted_dex_file_map) {}
|
|
|
|
bool operator>(const JITDebugInfo& other) const { return timestamp > other.timestamp; }
|
|
};
|
|
|
|
class TempSymFile;
|
|
|
|
// JITDebugReader reads debug info of JIT code and dex files of processes using ART. The
|
|
// corresponding debug interface in ART is at art/runtime/jit/debugger_interface.cc.
|
|
class JITDebugReader {
|
|
public:
|
|
enum class SymFileOption {
|
|
kDropSymFiles, // JIT symfiles are dropped after recording.
|
|
kKeepSymFiles, // JIT symfiles are kept after recording, usually for debug unwinding.
|
|
};
|
|
|
|
enum class SyncOption {
|
|
kNoSync, // Don't sync debug info with records.
|
|
kSyncWithRecords, // Sync debug info with records based on monotonic timestamp.
|
|
};
|
|
|
|
// symfile_prefix: JITDebugReader creates temporary file to store symfiles for JIT code. Add this
|
|
// prefix to avoid conflicts.
|
|
JITDebugReader(const std::string& symfile_prefix, SymFileOption symfile_option,
|
|
SyncOption sync_option);
|
|
|
|
~JITDebugReader();
|
|
|
|
bool SyncWithRecords() const { return sync_option_ == SyncOption::kSyncWithRecords; }
|
|
|
|
typedef std::function<bool(const std::vector<JITDebugInfo>&, bool)> debug_info_callback_t;
|
|
bool RegisterDebugInfoCallback(IOEventLoop* loop, const debug_info_callback_t& callback);
|
|
|
|
// There are two ways to select which processes to monitor. One is using MonitorProcess(), the
|
|
// other is finding all processes having libart.so using records.
|
|
bool MonitorProcess(pid_t pid);
|
|
bool UpdateRecord(const Record* record);
|
|
|
|
// Read new debug info from all monitored processes.
|
|
bool ReadAllProcesses();
|
|
// Read new debug info from one process.
|
|
bool ReadProcess(pid_t pid);
|
|
|
|
// Flush all debug info registered before timestamp.
|
|
bool FlushDebugInfo(uint64_t timestamp);
|
|
|
|
static bool IsPathInJITSymFile(const std::string& path) {
|
|
return path.find(std::string("_") + kJITAppCacheFile + ":") != path.npos ||
|
|
path.find(std::string("_") + kJITZygoteCacheFile + ":") != path.npos;
|
|
}
|
|
|
|
private:
|
|
enum class DescriptorType {
|
|
kDEX,
|
|
kJIT,
|
|
};
|
|
|
|
// An arch-independent representation of JIT/dex debug descriptor.
|
|
struct Descriptor {
|
|
DescriptorType type;
|
|
int version = 0;
|
|
uint32_t action_seqlock = 0; // incremented before and after any modification
|
|
uint64_t action_timestamp = 0; // CLOCK_MONOTONIC time of last action
|
|
uint64_t first_entry_addr = 0;
|
|
};
|
|
|
|
// An arch-independent representation of JIT/dex code entry.
|
|
struct CodeEntry {
|
|
uint64_t addr;
|
|
uint64_t symfile_addr;
|
|
uint64_t symfile_size;
|
|
uint64_t timestamp; // CLOCK_MONOTONIC time of last action
|
|
};
|
|
|
|
struct Process {
|
|
pid_t pid = -1;
|
|
bool initialized = false;
|
|
bool died = false;
|
|
bool is_64bit = false;
|
|
// The jit descriptor and dex descriptor can be read in one process_vm_readv() call.
|
|
uint64_t descriptors_addr = 0;
|
|
uint64_t descriptors_size = 0;
|
|
// offset relative to descriptors_addr
|
|
uint64_t jit_descriptor_offset = 0;
|
|
// offset relative to descriptors_addr
|
|
uint64_t dex_descriptor_offset = 0;
|
|
|
|
// The state we know about the remote jit debug descriptor.
|
|
Descriptor last_jit_descriptor;
|
|
// The state we know about the remote dex debug descriptor.
|
|
Descriptor last_dex_descriptor;
|
|
|
|
// memory space for /memfd:jit-zygote-cache
|
|
std::vector<std::pair<uint64_t, uint64_t>> jit_zygote_cache_ranges_;
|
|
};
|
|
|
|
// The location of descriptors in libart.so.
|
|
struct DescriptorsLocation {
|
|
uint64_t relative_addr = 0;
|
|
uint64_t size = 0;
|
|
uint64_t jit_descriptor_offset = 0;
|
|
uint64_t dex_descriptor_offset = 0;
|
|
};
|
|
|
|
bool ReadProcess(Process& process, std::vector<JITDebugInfo>* debug_info);
|
|
bool ReadDebugInfo(Process& process, Descriptor& new_descriptor,
|
|
std::vector<JITDebugInfo>* debug_info);
|
|
bool IsDescriptorChanged(Process& process, Descriptor& old_descriptor);
|
|
bool InitializeProcess(Process& process);
|
|
const DescriptorsLocation* GetDescriptorsLocation(const std::string& art_lib_path, bool is_64bit);
|
|
bool ReadRemoteMem(Process& process, uint64_t remote_addr, uint64_t size, void* data);
|
|
bool ReadDescriptors(Process& process, Descriptor* jit_descriptor, Descriptor* dex_descriptor);
|
|
bool LoadDescriptor(bool is_64bit, const char* data, Descriptor* descriptor);
|
|
template <typename DescriptorT>
|
|
bool LoadDescriptorImpl(const char* data, Descriptor* descriptor);
|
|
|
|
bool ReadNewCodeEntries(Process& process, const Descriptor& descriptor,
|
|
uint64_t last_action_timestamp, uint32_t read_entry_limit,
|
|
std::vector<CodeEntry>* new_code_entries);
|
|
template <typename CodeEntryT>
|
|
bool ReadNewCodeEntriesImpl(Process& process, const Descriptor& descriptor,
|
|
uint64_t last_action_timestamp, uint32_t read_entry_limit,
|
|
std::vector<CodeEntry>* new_code_entries);
|
|
|
|
bool ReadJITCodeDebugInfo(Process& process, const std::vector<CodeEntry>& jit_entries,
|
|
std::vector<JITDebugInfo>* debug_info);
|
|
TempSymFile* GetTempSymFile(Process& process, const CodeEntry& jit_entry);
|
|
void ReadDexFileDebugInfo(Process& process, const std::vector<CodeEntry>& dex_entries,
|
|
std::vector<JITDebugInfo>* debug_info);
|
|
bool AddDebugInfo(const std::vector<JITDebugInfo>& debug_info, bool sync_kernel_records);
|
|
|
|
const std::string symfile_prefix_;
|
|
SymFileOption symfile_option_;
|
|
SyncOption sync_option_;
|
|
IOEventRef read_event_ = nullptr;
|
|
debug_info_callback_t debug_info_callback_;
|
|
|
|
// Keys are pids of processes having libart.so, values show whether a process has been monitored.
|
|
std::unordered_map<pid_t, bool> pids_with_art_lib_;
|
|
|
|
// All monitored processes
|
|
std::unordered_map<pid_t, Process> processes_;
|
|
std::unordered_map<std::string, DescriptorsLocation> descriptors_location_cache_;
|
|
std::vector<char> descriptors_buf_;
|
|
|
|
std::priority_queue<JITDebugInfo, std::vector<JITDebugInfo>, std::greater<JITDebugInfo>>
|
|
debug_info_q_;
|
|
|
|
// temporary files used to store jit symfiles created by the app process and the zygote process.
|
|
std::unique_ptr<TempSymFile> app_symfile_;
|
|
std::unique_ptr<TempSymFile> zygote_symfile_;
|
|
};
|
|
|
|
} // namespace simpleperf
|
|
|
|
#endif // SIMPLE_PERF_JIT_DEBUG_READER_H_
|