/* * 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 #include #include #include #include #include #include #include #include #include #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"; inline constexpr const char* kDexFileInMemoryPrefix = "dexfile_in_memory"; namespace JITDebugReader_impl { 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; // remote addr of jit descriptor uint64_t jit_descriptor_addr = 0; // remote addr of dex descriptor uint64_t dex_descriptor_addr = 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> jit_zygote_cache_ranges_; }; } // namespace JITDebugReader_impl // 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; // dex_file_map may be a dex file extracted in memory. On Android >= Q, ART may extract dex files // in apk files directly into memory, and name it using prctl(). The kernel doesn't generate a // new mmap record for it. So we need to dump it manually. // Or dex_file_map may be a dex file created in memory. In that case, symbols are read from the // dex file. std::shared_ptr dex_file_map; std::vector symbols; 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& dex_file_map, std::vector symbols) : type(JIT_DEBUG_DEX_FILE), pid(pid), timestamp(timestamp), dex_file_offset(dex_file_offset), file_path(file_path), file_offset(0), dex_file_map(dex_file_map), symbols(std::move(symbols)) {} 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: using Descriptor = JITDebugReader_impl::Descriptor; using CodeEntry = JITDebugReader_impl::CodeEntry; using Process = JITDebugReader_impl::Process; 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)> 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; } // exported for testing void ReadDexFileDebugInfo(Process& process, const std::vector& dex_entries, std::vector* debug_info); private: // The location of descriptors in libart.so. struct DescriptorsLocation { bool is_64bit = false; uint64_t jit_descriptor_addr = 0; uint64_t dex_descriptor_addr = 0; }; bool ReadProcess(Process& process, std::vector* debug_info); bool ReadDebugInfo(Process& process, Descriptor& new_descriptor, std::vector* debug_info); bool IsDescriptorChanged(Process& process, Descriptor& old_descriptor); bool InitializeProcess(Process& process); const DescriptorsLocation* GetDescriptorsLocation(const std::string& art_lib_path); bool ReadRemoteMem(Process& process, uint64_t remote_addr, uint64_t size, void* data); bool ReadDescriptors(Process& process, Descriptor* jit_descriptor, Descriptor* dex_descriptor); template bool ReadDescriptorsImpl(Process& process, Descriptor* jit_descriptor, Descriptor* dex_descriptor); template bool ParseDescriptor(const DescriptorT& raw_descriptor, Descriptor* descriptor); bool ReadNewCodeEntries(Process& process, const Descriptor& descriptor, uint64_t last_action_timestamp, uint32_t read_entry_limit, std::vector* new_code_entries); template bool ReadNewCodeEntriesImpl(Process& process, const Descriptor& descriptor, uint64_t last_action_timestamp, uint32_t read_entry_limit, std::vector* new_code_entries); bool ReadJITCodeDebugInfo(Process& process, const std::vector& jit_entries, std::vector* debug_info); TempSymFile* GetTempSymFile(Process& process, const CodeEntry& jit_entry); std::vector ReadDexFileSymbolsInMemory(Process& process, uint64_t addr, uint64_t size); bool AddDebugInfo(std::vector 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 pids_with_art_lib_; // All monitored processes std::unordered_map processes_; std::unordered_map descriptors_location_cache_; std::priority_queue, std::greater> debug_info_q_; // temporary files used to store jit symfiles created by the app process and the zygote process. std::unique_ptr app_symfile_; std::unique_ptr zygote_symfile_; }; } // namespace simpleperf #endif // SIMPLE_PERF_JIT_DEBUG_READER_H_