Yabin Cui | 9e43e9f | 2018-03-09 15:57:13 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2018 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | #ifndef SIMPLE_PERF_JIT_DEBUG_READER_H_ |
| 18 | #define SIMPLE_PERF_JIT_DEBUG_READER_H_ |
| 19 | |
| 20 | #include <unistd.h> |
| 21 | |
| 22 | #include <functional> |
| 23 | #include <memory> |
Yabin Cui | 5d5c01a | 2018-08-27 10:30:20 -0700 | [diff] [blame] | 24 | #include <queue> |
Yabin Cui | 9e43e9f | 2018-03-09 15:57:13 -0800 | [diff] [blame] | 25 | #include <stack> |
Yabin Cui | c8571d4 | 2018-06-06 11:20:39 -0700 | [diff] [blame] | 26 | #include <unordered_map> |
Yabin Cui | 9e43e9f | 2018-03-09 15:57:13 -0800 | [diff] [blame] | 27 | #include <unordered_set> |
| 28 | #include <vector> |
| 29 | |
Mark Salyzyn | 499a017 | 2018-11-12 12:58:06 -0800 | [diff] [blame] | 30 | #include <android-base/file.h> |
Yabin Cui | 9e43e9f | 2018-03-09 15:57:13 -0800 | [diff] [blame] | 31 | #include <android-base/logging.h> |
Yabin Cui | 9e43e9f | 2018-03-09 15:57:13 -0800 | [diff] [blame] | 32 | |
Yabin Cui | c8571d4 | 2018-06-06 11:20:39 -0700 | [diff] [blame] | 33 | #include "IOEventLoop.h" |
Thiébaud Weksteen | 4848ee0 | 2020-10-23 16:06:59 +0200 | [diff] [blame] | 34 | #include "environment.h" |
Yabin Cui | c8571d4 | 2018-06-06 11:20:39 -0700 | [diff] [blame] | 35 | #include "record.h" |
| 36 | |
Yabin Cui | 9e43e9f | 2018-03-09 15:57:13 -0800 | [diff] [blame] | 37 | namespace simpleperf { |
| 38 | |
Yabin Cui | 075dd18 | 2020-08-05 19:51:36 +0000 | [diff] [blame] | 39 | inline constexpr const char* kJITAppCacheFile = "jit_app_cache"; |
| 40 | inline constexpr const char* kJITZygoteCacheFile = "jit_zygote_cache"; |
Yabin Cui | 8b8ed0f | 2023-10-24 16:18:38 -0700 | [diff] [blame] | 41 | inline constexpr const char* kDexFileInMemoryPrefix = "dexfile_in_memory"; |
Yabin Cui | 075dd18 | 2020-08-05 19:51:36 +0000 | [diff] [blame] | 42 | |
Yabin Cui | 42eb3d2 | 2023-10-25 11:59:09 -0700 | [diff] [blame] | 43 | namespace JITDebugReader_impl { |
| 44 | |
| 45 | enum class DescriptorType { |
| 46 | kDEX, |
| 47 | kJIT, |
| 48 | }; |
| 49 | |
| 50 | // An arch-independent representation of JIT/dex debug descriptor. |
| 51 | struct Descriptor { |
| 52 | DescriptorType type; |
| 53 | int version = 0; |
| 54 | uint32_t action_seqlock = 0; // incremented before and after any modification |
| 55 | uint64_t action_timestamp = 0; // CLOCK_MONOTONIC time of last action |
| 56 | uint64_t first_entry_addr = 0; |
| 57 | }; |
| 58 | |
| 59 | // An arch-independent representation of JIT/dex code entry. |
| 60 | struct CodeEntry { |
| 61 | uint64_t addr; |
| 62 | uint64_t symfile_addr; |
| 63 | uint64_t symfile_size; |
| 64 | uint64_t timestamp; // CLOCK_MONOTONIC time of last action |
| 65 | }; |
| 66 | |
| 67 | struct Process { |
| 68 | pid_t pid = -1; |
| 69 | bool initialized = false; |
| 70 | bool died = false; |
| 71 | bool is_64bit = false; |
| 72 | // remote addr of jit descriptor |
| 73 | uint64_t jit_descriptor_addr = 0; |
| 74 | // remote addr of dex descriptor |
| 75 | uint64_t dex_descriptor_addr = 0; |
| 76 | |
| 77 | // The state we know about the remote jit debug descriptor. |
| 78 | Descriptor last_jit_descriptor; |
| 79 | // The state we know about the remote dex debug descriptor. |
| 80 | Descriptor last_dex_descriptor; |
| 81 | |
| 82 | // memory space for /memfd:jit-zygote-cache |
| 83 | std::vector<std::pair<uint64_t, uint64_t>> jit_zygote_cache_ranges_; |
| 84 | }; |
| 85 | |
| 86 | } // namespace JITDebugReader_impl |
| 87 | |
Yabin Cui | 5d5c01a | 2018-08-27 10:30:20 -0700 | [diff] [blame] | 88 | // JITDebugInfo represents the debug info of a JITed Java method or a dex file. |
| 89 | struct JITDebugInfo { |
| 90 | enum { |
| 91 | JIT_DEBUG_JIT_CODE, |
| 92 | JIT_DEBUG_DEX_FILE, |
| 93 | } type; |
| 94 | pid_t pid; // Process of the debug info |
| 95 | uint64_t timestamp; // Monotonic timestamp for the creation of the debug info |
Yabin Cui | 8b8ed0f | 2023-10-24 16:18:38 -0700 | [diff] [blame] | 96 | |
Yabin Cui | 5d5c01a | 2018-08-27 10:30:20 -0700 | [diff] [blame] | 97 | union { |
| 98 | struct { |
| 99 | uint64_t jit_code_addr; // The start addr of the JITed code |
| 100 | uint64_t jit_code_len; // The end addr of the JITed code |
| 101 | }; |
| 102 | uint64_t dex_file_offset; // The offset of the dex file in the file containing it |
| 103 | }; |
| 104 | // For JITed code, it is the path of a temporary ELF file storing its debug info. |
| 105 | // For dex file, it is the path of the file containing the dex file. |
| 106 | std::string file_path; |
Yabin Cui | e32ed2b | 2020-07-23 15:30:14 -0700 | [diff] [blame] | 107 | uint64_t file_offset; |
Yabin Cui | c8571d4 | 2018-06-06 11:20:39 -0700 | [diff] [blame] | 108 | |
Yabin Cui | 8b8ed0f | 2023-10-24 16:18:38 -0700 | [diff] [blame] | 109 | // dex_file_map may be a dex file extracted in memory. On Android >= Q, ART may extract dex files |
| 110 | // in apk files directly into memory, and name it using prctl(). The kernel doesn't generate a |
| 111 | // new mmap record for it. So we need to dump it manually. |
| 112 | // Or dex_file_map may be a dex file created in memory. In that case, symbols are read from the |
| 113 | // dex file. |
| 114 | std::shared_ptr<ThreadMmap> dex_file_map; |
| 115 | std::vector<Symbol> symbols; |
Yabin Cui | 400892e | 2019-05-31 10:39:56 -0700 | [diff] [blame] | 116 | |
Yabin Cui | 5d5c01a | 2018-08-27 10:30:20 -0700 | [diff] [blame] | 117 | JITDebugInfo(pid_t pid, uint64_t timestamp, uint64_t jit_code_addr, uint64_t jit_code_len, |
Yabin Cui | e32ed2b | 2020-07-23 15:30:14 -0700 | [diff] [blame] | 118 | const std::string& file_path, uint64_t file_offset) |
| 119 | : type(JIT_DEBUG_JIT_CODE), |
| 120 | pid(pid), |
| 121 | timestamp(timestamp), |
| 122 | jit_code_addr(jit_code_addr), |
| 123 | jit_code_len(jit_code_len), |
| 124 | file_path(file_path), |
| 125 | file_offset(file_offset) {} |
Yabin Cui | 9e43e9f | 2018-03-09 15:57:13 -0800 | [diff] [blame] | 126 | |
Yabin Cui | 5d5c01a | 2018-08-27 10:30:20 -0700 | [diff] [blame] | 127 | JITDebugInfo(pid_t pid, uint64_t timestamp, uint64_t dex_file_offset, |
Yabin Cui | 8b8ed0f | 2023-10-24 16:18:38 -0700 | [diff] [blame] | 128 | const std::string& file_path, const std::shared_ptr<ThreadMmap>& dex_file_map, |
| 129 | std::vector<Symbol> symbols) |
Yabin Cui | e32ed2b | 2020-07-23 15:30:14 -0700 | [diff] [blame] | 130 | : type(JIT_DEBUG_DEX_FILE), |
| 131 | pid(pid), |
| 132 | timestamp(timestamp), |
| 133 | dex_file_offset(dex_file_offset), |
| 134 | file_path(file_path), |
| 135 | file_offset(0), |
Yabin Cui | 8b8ed0f | 2023-10-24 16:18:38 -0700 | [diff] [blame] | 136 | dex_file_map(dex_file_map), |
| 137 | symbols(std::move(symbols)) {} |
Yabin Cui | 2a53ff3 | 2018-05-21 17:37:00 -0700 | [diff] [blame] | 138 | |
Thiébaud Weksteen | 4848ee0 | 2020-10-23 16:06:59 +0200 | [diff] [blame] | 139 | bool operator>(const JITDebugInfo& other) const { return timestamp > other.timestamp; } |
Yabin Cui | 9e43e9f | 2018-03-09 15:57:13 -0800 | [diff] [blame] | 140 | }; |
| 141 | |
Yabin Cui | e32ed2b | 2020-07-23 15:30:14 -0700 | [diff] [blame] | 142 | class TempSymFile; |
| 143 | |
Yabin Cui | c8571d4 | 2018-06-06 11:20:39 -0700 | [diff] [blame] | 144 | // JITDebugReader reads debug info of JIT code and dex files of processes using ART. The |
| 145 | // corresponding debug interface in ART is at art/runtime/jit/debugger_interface.cc. |
Yabin Cui | 9e43e9f | 2018-03-09 15:57:13 -0800 | [diff] [blame] | 146 | class JITDebugReader { |
| 147 | public: |
Yabin Cui | 42eb3d2 | 2023-10-25 11:59:09 -0700 | [diff] [blame] | 148 | using Descriptor = JITDebugReader_impl::Descriptor; |
| 149 | using CodeEntry = JITDebugReader_impl::CodeEntry; |
| 150 | using Process = JITDebugReader_impl::Process; |
| 151 | |
Yabin Cui | e32ed2b | 2020-07-23 15:30:14 -0700 | [diff] [blame] | 152 | enum class SymFileOption { |
| 153 | kDropSymFiles, // JIT symfiles are dropped after recording. |
| 154 | kKeepSymFiles, // JIT symfiles are kept after recording, usually for debug unwinding. |
| 155 | }; |
| 156 | |
| 157 | enum class SyncOption { |
Thiébaud Weksteen | 4848ee0 | 2020-10-23 16:06:59 +0200 | [diff] [blame] | 158 | kNoSync, // Don't sync debug info with records. |
Yabin Cui | e32ed2b | 2020-07-23 15:30:14 -0700 | [diff] [blame] | 159 | kSyncWithRecords, // Sync debug info with records based on monotonic timestamp. |
| 160 | }; |
| 161 | |
| 162 | // symfile_prefix: JITDebugReader creates temporary file to store symfiles for JIT code. Add this |
| 163 | // prefix to avoid conflicts. |
| 164 | JITDebugReader(const std::string& symfile_prefix, SymFileOption symfile_option, |
| 165 | SyncOption sync_option); |
| 166 | |
| 167 | ~JITDebugReader(); |
Yabin Cui | 9e43e9f | 2018-03-09 15:57:13 -0800 | [diff] [blame] | 168 | |
Thiébaud Weksteen | 4848ee0 | 2020-10-23 16:06:59 +0200 | [diff] [blame] | 169 | bool SyncWithRecords() const { return sync_option_ == SyncOption::kSyncWithRecords; } |
Yabin Cui | 5d5c01a | 2018-08-27 10:30:20 -0700 | [diff] [blame] | 170 | |
Yabin Cui | 8b8ed0f | 2023-10-24 16:18:38 -0700 | [diff] [blame] | 171 | typedef std::function<bool(std::vector<JITDebugInfo>, bool)> debug_info_callback_t; |
Yabin Cui | 5d5c01a | 2018-08-27 10:30:20 -0700 | [diff] [blame] | 172 | bool RegisterDebugInfoCallback(IOEventLoop* loop, const debug_info_callback_t& callback); |
Yabin Cui | 9e43e9f | 2018-03-09 15:57:13 -0800 | [diff] [blame] | 173 | |
Yabin Cui | c8571d4 | 2018-06-06 11:20:39 -0700 | [diff] [blame] | 174 | // There are two ways to select which processes to monitor. One is using MonitorProcess(), the |
| 175 | // other is finding all processes having libart.so using records. |
| 176 | bool MonitorProcess(pid_t pid); |
| 177 | bool UpdateRecord(const Record* record); |
| 178 | |
| 179 | // Read new debug info from all monitored processes. |
| 180 | bool ReadAllProcesses(); |
Yabin Cui | 5d5c01a | 2018-08-27 10:30:20 -0700 | [diff] [blame] | 181 | // Read new debug info from one process. |
| 182 | bool ReadProcess(pid_t pid); |
| 183 | |
| 184 | // Flush all debug info registered before timestamp. |
| 185 | bool FlushDebugInfo(uint64_t timestamp); |
Yabin Cui | 9e43e9f | 2018-03-09 15:57:13 -0800 | [diff] [blame] | 186 | |
Yabin Cui | 9ba4d94 | 2020-09-08 16:12:46 -0700 | [diff] [blame] | 187 | static bool IsPathInJITSymFile(const std::string& path) { |
| 188 | return path.find(std::string("_") + kJITAppCacheFile + ":") != path.npos || |
| 189 | path.find(std::string("_") + kJITZygoteCacheFile + ":") != path.npos; |
| 190 | } |
| 191 | |
Yabin Cui | 42eb3d2 | 2023-10-25 11:59:09 -0700 | [diff] [blame] | 192 | // exported for testing |
| 193 | void ReadDexFileDebugInfo(Process& process, const std::vector<CodeEntry>& dex_entries, |
| 194 | std::vector<JITDebugInfo>* debug_info); |
| 195 | |
Yabin Cui | 9e43e9f | 2018-03-09 15:57:13 -0800 | [diff] [blame] | 196 | private: |
Yabin Cui | c8571d4 | 2018-06-06 11:20:39 -0700 | [diff] [blame] | 197 | // The location of descriptors in libart.so. |
| 198 | struct DescriptorsLocation { |
Yabin Cui | e5defe1 | 2022-03-23 15:55:50 -0700 | [diff] [blame] | 199 | bool is_64bit = false; |
| 200 | uint64_t jit_descriptor_addr = 0; |
| 201 | uint64_t dex_descriptor_addr = 0; |
Yabin Cui | c8571d4 | 2018-06-06 11:20:39 -0700 | [diff] [blame] | 202 | }; |
| 203 | |
Yabin Cui | e32ed2b | 2020-07-23 15:30:14 -0700 | [diff] [blame] | 204 | bool ReadProcess(Process& process, std::vector<JITDebugInfo>* debug_info); |
| 205 | bool ReadDebugInfo(Process& process, Descriptor& new_descriptor, |
| 206 | std::vector<JITDebugInfo>* debug_info); |
| 207 | bool IsDescriptorChanged(Process& process, Descriptor& old_descriptor); |
Yabin Cui | c8571d4 | 2018-06-06 11:20:39 -0700 | [diff] [blame] | 208 | bool InitializeProcess(Process& process); |
Yabin Cui | e5defe1 | 2022-03-23 15:55:50 -0700 | [diff] [blame] | 209 | const DescriptorsLocation* GetDescriptorsLocation(const std::string& art_lib_path); |
Yabin Cui | c8571d4 | 2018-06-06 11:20:39 -0700 | [diff] [blame] | 210 | bool ReadRemoteMem(Process& process, uint64_t remote_addr, uint64_t size, void* data); |
| 211 | bool ReadDescriptors(Process& process, Descriptor* jit_descriptor, Descriptor* dex_descriptor); |
Yabin Cui | 62fa6ee | 2019-10-07 15:15:12 -0700 | [diff] [blame] | 212 | template <typename DescriptorT> |
Yabin Cui | e5defe1 | 2022-03-23 15:55:50 -0700 | [diff] [blame] | 213 | bool ReadDescriptorsImpl(Process& process, Descriptor* jit_descriptor, |
| 214 | Descriptor* dex_descriptor); |
| 215 | template <typename DescriptorT> |
| 216 | bool ParseDescriptor(const DescriptorT& raw_descriptor, Descriptor* descriptor); |
Yabin Cui | 9e43e9f | 2018-03-09 15:57:13 -0800 | [diff] [blame] | 217 | |
Yabin Cui | c8571d4 | 2018-06-06 11:20:39 -0700 | [diff] [blame] | 218 | bool ReadNewCodeEntries(Process& process, const Descriptor& descriptor, |
| 219 | uint64_t last_action_timestamp, uint32_t read_entry_limit, |
Yabin Cui | 9e43e9f | 2018-03-09 15:57:13 -0800 | [diff] [blame] | 220 | std::vector<CodeEntry>* new_code_entries); |
Yabin Cui | 62fa6ee | 2019-10-07 15:15:12 -0700 | [diff] [blame] | 221 | template <typename CodeEntryT> |
Yabin Cui | c8571d4 | 2018-06-06 11:20:39 -0700 | [diff] [blame] | 222 | bool ReadNewCodeEntriesImpl(Process& process, const Descriptor& descriptor, |
| 223 | uint64_t last_action_timestamp, uint32_t read_entry_limit, |
Yabin Cui | 9e43e9f | 2018-03-09 15:57:13 -0800 | [diff] [blame] | 224 | std::vector<CodeEntry>* new_code_entries); |
| 225 | |
Yabin Cui | e32ed2b | 2020-07-23 15:30:14 -0700 | [diff] [blame] | 226 | bool ReadJITCodeDebugInfo(Process& process, const std::vector<CodeEntry>& jit_entries, |
Thiébaud Weksteen | 4848ee0 | 2020-10-23 16:06:59 +0200 | [diff] [blame] | 227 | std::vector<JITDebugInfo>* debug_info); |
Yabin Cui | 7f94b47 | 2020-08-03 21:11:32 +0000 | [diff] [blame] | 228 | TempSymFile* GetTempSymFile(Process& process, const CodeEntry& jit_entry); |
Yabin Cui | 8b8ed0f | 2023-10-24 16:18:38 -0700 | [diff] [blame] | 229 | std::vector<Symbol> ReadDexFileSymbolsInMemory(Process& process, uint64_t addr, uint64_t size); |
| 230 | bool AddDebugInfo(std::vector<JITDebugInfo> debug_info, bool sync_kernel_records); |
Yabin Cui | 9e43e9f | 2018-03-09 15:57:13 -0800 | [diff] [blame] | 231 | |
Yabin Cui | e32ed2b | 2020-07-23 15:30:14 -0700 | [diff] [blame] | 232 | const std::string symfile_prefix_; |
| 233 | SymFileOption symfile_option_; |
| 234 | SyncOption sync_option_; |
Yabin Cui | c8571d4 | 2018-06-06 11:20:39 -0700 | [diff] [blame] | 235 | IOEventRef read_event_ = nullptr; |
Yabin Cui | 5d5c01a | 2018-08-27 10:30:20 -0700 | [diff] [blame] | 236 | debug_info_callback_t debug_info_callback_; |
Yabin Cui | 9e43e9f | 2018-03-09 15:57:13 -0800 | [diff] [blame] | 237 | |
Yabin Cui | c8571d4 | 2018-06-06 11:20:39 -0700 | [diff] [blame] | 238 | // Keys are pids of processes having libart.so, values show whether a process has been monitored. |
| 239 | std::unordered_map<pid_t, bool> pids_with_art_lib_; |
| 240 | |
| 241 | // All monitored processes |
| 242 | std::unordered_map<pid_t, Process> processes_; |
| 243 | std::unordered_map<std::string, DescriptorsLocation> descriptors_location_cache_; |
Yabin Cui | 5d5c01a | 2018-08-27 10:30:20 -0700 | [diff] [blame] | 244 | |
| 245 | std::priority_queue<JITDebugInfo, std::vector<JITDebugInfo>, std::greater<JITDebugInfo>> |
| 246 | debug_info_q_; |
Yabin Cui | e32ed2b | 2020-07-23 15:30:14 -0700 | [diff] [blame] | 247 | |
| 248 | // temporary files used to store jit symfiles created by the app process and the zygote process. |
| 249 | std::unique_ptr<TempSymFile> app_symfile_; |
Yabin Cui | 7f94b47 | 2020-08-03 21:11:32 +0000 | [diff] [blame] | 250 | std::unique_ptr<TempSymFile> zygote_symfile_; |
Yabin Cui | 9e43e9f | 2018-03-09 15:57:13 -0800 | [diff] [blame] | 251 | }; |
| 252 | |
Thiébaud Weksteen | 4848ee0 | 2020-10-23 16:06:59 +0200 | [diff] [blame] | 253 | } // namespace simpleperf |
Yabin Cui | 9e43e9f | 2018-03-09 15:57:13 -0800 | [diff] [blame] | 254 | |
Thiébaud Weksteen | 4848ee0 | 2020-10-23 16:06:59 +0200 | [diff] [blame] | 255 | #endif // SIMPLE_PERF_JIT_DEBUG_READER_H_ |