1 /* 2 * Copyright (c) 2021 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 #ifndef HIPERF_VIRTUAL_RUNTIME_H 16 #define HIPERF_VIRTUAL_RUNTIME_H 17 #include <unistd.h> 18 #include <sys/types.h> 19 #include <pthread.h> 20 #include <functional> 21 #include <map> 22 #include "call_stack.h" 23 #include "perf_event_record.h" 24 #include "symbols_file.h" 25 #include "virtual_thread.h" 26 #include "native_hook_config.pb.h" 27 28 namespace OHOS { 29 namespace Developtools { 30 namespace NativeDaemon { 31 /* 32 This Class contains userspace thread objects. and kernel space objects 33 It represents a virtual operating environment, mainly referring to the relationship between pid, 34 mmaps, and symbols. 35 36 It mainly receives data is ip pointer (virtual address), pid 37 According to these data, it will find the corresponding mmap and its corresponding elf (also called 38 DSO) 39 40 Then find the corresponding symbol in the corresponding elf symbol file according to the offset 41 recorded in the corresponding mmap. 42 */ 43 44 class VirtualRuntime { 45 public: 46 VirtualRuntime() = default; 47 VirtualRuntime(const NativeHookConfig& hookConfig); 48 virtual ~VirtualRuntime(); 49 // thread need hook the record 50 // from the record , it will call back to write some Simulated Record 51 // case 1. some mmap will be create when it read mmaps for each new process (from record sample) 52 53 // set symbols path , it will send to every symobile file for search 54 bool SetSymbolsPaths(const std::vector<std::string> &symbolsPaths); 55 56 // any mode 57 static_assert(sizeof(pid_t) == sizeof(int)); 58 GetSymbolsFiles()59 const std::unordered_map<std::string, std::unique_ptr<SymbolsFile>> &GetSymbolsFiles() const 60 { 61 return symbolsFiles_; 62 } 63 64 const Symbol GetSymbol(CallFrame& callFrame, pid_t pid, pid_t tid, 65 const perf_callchain_context &context = PERF_CONTEXT_MAX); 66 67 VirtualThread &GetThread(pid_t pid, pid_t tid); GetThreads()68 const std::map<pid_t, VirtualThread> &GetThreads() const 69 { 70 return userSpaceThreadMap_; 71 } 72 73 bool UnwindStack(std::vector<u64>& regs, 74 const u8* stack_addr, 75 int stack_size, 76 pid_t pid, 77 pid_t tid, 78 std::vector<CallFrame>& callFrames, 79 size_t maxStackLevel); 80 bool GetSymbolName(pid_t pid, pid_t tid, std::vector<CallFrame>& callFrames, int offset, bool first); 81 void ClearMaps(); 82 void FillMapsCache(std::string& currentFileName, MemMapItem& memMapItem); 83 void HandleMapInfo(uint64_t begin, uint64_t length, uint32_t flags, uint64_t offset, const std::string& filePath); 84 void RemoveMaps(uint64_t addr); GetOfflineMaps()85 std::vector<uint64_t>& GetOfflineMaps() 86 { 87 return offlineMapAddr_; 88 } 89 ClearOfflineMaps()90 void ClearOfflineMaps() 91 { 92 offlineMapAddr_.clear(); 93 } 94 GetMapsCache()95 std::map<uint64_t, MemMaps>& GetMapsCache() 96 { 97 return mapsCache_; 98 } 99 100 std::pair<MemMaps*, uint32_t> FindMap(uint64_t addr); 101 uint64_t soBegin_ {0}; 102 // debug time 103 #ifdef HIPERF_DEBUG_TIME 104 std::chrono::microseconds updateSymbolsTimes_ = std::chrono::microseconds::zero(); 105 std::chrono::microseconds unwindFromRecordTimes_ = std::chrono::microseconds::zero(); 106 std::chrono::microseconds unwindCallStackTimes_ = std::chrono::microseconds::zero(); 107 std::chrono::microseconds symbolicRecordTimes_ = std::chrono::microseconds::zero(); 108 std::chrono::microseconds updateThreadTimes_ = std::chrono::microseconds::zero(); 109 #endif 110 const bool loadSymboleWhenNeeded_ = true; // thie is a feature config 111 void UpdateSymbols(std::string filename); 112 bool IsSymbolExist(const std::string& fileName); 113 void DelSymbolFile(const std::string& fileName); 114 void UpdateMaps(pid_t pid, pid_t tid); GetProcessMaps()115 std::vector<MemMapItem>& GetProcessMaps() 116 { 117 return processMemMaps_; 118 } 119 120 public: 121 enum SymbolCacheLimit : std::size_t { 122 USER_SYMBOL_CACHE_LIMIT = 10000, 123 }; 124 125 private: 126 struct SymbolCacheKey : public std::pair<uint64_t, uint32_t> { 127 uint64_t& ip = first; 128 uint32_t& filePathId = second; 129 explicit SymbolCacheKey() = default; 130 virtual ~SymbolCacheKey() = default; 131 SymbolCacheKey(const SymbolCacheKey &) = default; 132 SymbolCacheKey& operator=(const SymbolCacheKey& sym) 133 { 134 ip = sym.ip; 135 filePathId = sym.filePathId; 136 return *this; 137 } SymbolCacheKeySymbolCacheKey138 SymbolCacheKey(const std::pair<uint64_t, uint32_t>& arg) : pair(arg), ip(first), filePathId(second) {} SymbolCacheKeySymbolCacheKey139 SymbolCacheKey(uint64_t ip, uint32_t filePathId) : pair(ip, filePathId), ip(first), filePathId(second) {} 140 }; 141 142 // boost library recommendation algorithm to reduce hash collisions. 143 struct HashPair { operatorHashPair144 size_t operator() (const SymbolCacheKey& key) const 145 { 146 std::hash<uint64_t> hasher; 147 size_t seed = 0; 148 // 6 and 2 is the number of displacements 149 seed ^= hasher(key.ip) + 0x9e3779b9 + (seed << 6) + (seed >> 2); 150 seed ^= hasher(key.filePathId) + 0x9e3779b9 + (seed << 6) + (seed >> 2); 151 return seed; 152 } 153 }; 154 CallStack callstack_; 155 // pid map with user space thread 156 pthread_mutex_t threadMapsLock_; 157 std::map<pid_t, VirtualThread> userSpaceThreadMap_; 158 // not pid , just memmap 159 std::vector<MemMapItem> kernelSpaceMemMaps_; 160 pthread_mutex_t processSymbolsFileLock_; 161 std::unordered_set<uint32_t> fileSet_; // for memMpaItem filePathId_ 162 std::unordered_map<std::string, uint32_t> functionMap_; 163 std::unordered_map<std::string, std::unique_ptr<SymbolsFile>> symbolsFiles_; 164 std::unordered_map<SymbolCacheKey, Symbol, HashPair> userSymbolCache_; 165 bool GetSymbolCache(uint64_t ip, Symbol &symbol, const VirtualThread &thread); 166 void UpdateSymbolCache(uint64_t ip, Symbol &symbol, HashList<uint64_t, Symbol> &cache); 167 168 // find synbols function name 169 void MakeCallFrame(Symbol &symbol, CallFrame &callFrame); 170 171 // threads 172 VirtualThread &UpdateThread(pid_t pid, pid_t tid, const std::string name = ""); 173 std::string ReadThreadName(pid_t tid); 174 VirtualThread &CreateThread(pid_t pid, pid_t tid); 175 176 const Symbol GetKernelSymbol(uint64_t ip, const std::vector<MemMapItem> &memMaps, 177 const VirtualThread &thread); 178 const Symbol GetUserSymbol(uint64_t ip, const VirtualThread &thread); 179 void FillSymbolNameId(CallFrame& callFrame, Symbol& symbol); 180 void FillFileSet(CallFrame& callFrame, const Symbol& symbol); 181 182 std::vector<std::string> symbolsPaths_; 183 184 friend class VirtualRuntimeTest; 185 friend class VirtualThread; 186 std::vector<MemMapItem> processMemMaps_; 187 std::unordered_set<uint64_t> failedIPs_; 188 const NativeHookConfig hookConfig_; 189 uint32_t memMapFilePathId_ = 0; 190 std::map<uint64_t, MemMaps> mapsCache_; // key is memMap soBegin, value is MemMaps 191 std::vector<uint64_t> offlineMapAddr_; // element is memMap soBegin 192 }; 193 } // namespace NativeDaemon 194 } // namespace Developtools 195 } // namespace OHOS 196 #endif