1 /* 2 * Copyright (c) Huawei Technologies Co., Ltd. 2021. All rights reserved. 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 #if defined(is_ohos) && is_ohos 23 #include "call_stack.h" 24 #endif 25 #include "hashlistpp.h" 26 #include "perf_event_record.h" 27 #include "symbols_file.h" 28 #include "virtual_thread.h" 29 #include "native_hook_config.pb.h" 30 31 namespace OHOS { 32 namespace Developtools { 33 namespace NativeDaemon { 34 /* 35 This Class contains userspace thread objects. and kernel space objects 36 It represents a virtual operating environment, mainly referring to the relationship between pid, 37 mmaps, and symbols. 38 39 It mainly receives data is ip pointer (virtual address), pid 40 According to these data, it will find the corresponding mmap and its corresponding elf (also called 41 DSO) 42 43 Then find the corresponding symbol in the corresponding elf symbol file according to the offset 44 recorded in the corresponding mmap. 45 */ 46 47 class VirtualRuntime { 48 public: 49 VirtualRuntime() = default; 50 VirtualRuntime(const NativeHookConfig& hookConfig); 51 virtual ~VirtualRuntime(); 52 // thread need hook the record 53 // from the record , it will call back to write some Simulated Record 54 // case 1. some mmap will be create when it read mmaps for each new process (from record sample) 55 56 // set symbols path , it will send to every symobile file for search 57 bool SetSymbolsPaths(const std::vector<std::string> &symbolsPaths); 58 59 // any mode 60 static_assert(sizeof(pid_t) == sizeof(int)); 61 GetSymbolsFiles()62 const std::unordered_map<std::string, std::unique_ptr<SymbolsFile>> &GetSymbolsFiles() const 63 { 64 return symbolsFiles_; 65 } 66 67 const DfxSymbol GetSymbol(CallFrame& callFrame, pid_t pid, pid_t tid, 68 const perf_callchain_context &context = PERF_CONTEXT_MAX); 69 70 VirtualThread &GetThread(pid_t pid, pid_t tid); GetThreads()71 const std::map<pid_t, VirtualThread> &GetThreads() const 72 { 73 return userSpaceThreadMap_; 74 } 75 76 bool UnwindStack(std::vector<u64>& regs, 77 const u8* stack_addr, 78 int stack_size, 79 pid_t pid, 80 pid_t tid, 81 std::vector<CallFrame>& callFrames, 82 size_t maxStackLevel); 83 bool GetSymbolName(pid_t pid, pid_t tid, std::vector<CallFrame>& callFrames, int offset, bool first); 84 void ClearMaps(); 85 void FillMapsCache(std::string& currentFileName, std::shared_ptr<DfxMap> mapItem); 86 void HandleMapInfo(uint64_t begin, uint64_t length, uint32_t flags, uint64_t offset, const std::string& filePath); 87 void RemoveMaps(uint64_t addr); 88 // threads 89 VirtualThread &UpdateThread(pid_t pid, pid_t tid, const std::string name = ""); GetOfflineMaps()90 std::vector<uint64_t>& GetOfflineMaps() 91 { 92 return offlineMapAddr_; 93 } 94 ClearOfflineMaps()95 void ClearOfflineMaps() 96 { 97 offlineMapAddr_.clear(); 98 } 99 GetMapsCache()100 std::map<uint64_t, std::shared_ptr<MemMaps>>& GetMapsCache() 101 { 102 return mapsCache_; 103 } 104 105 std::pair<std::shared_ptr<MemMaps>, uint32_t> FindMap(uint64_t addr); 106 uint64_t soBegin_ {0}; 107 // debug time 108 #ifdef HIPERF_DEBUG_TIME 109 std::chrono::microseconds updateSymbolsTimes_ = std::chrono::microseconds::zero(); 110 std::chrono::microseconds unwindFromRecordTimes_ = std::chrono::microseconds::zero(); 111 std::chrono::microseconds unwindCallStackTimes_ = std::chrono::microseconds::zero(); 112 std::chrono::microseconds symbolicRecordTimes_ = std::chrono::microseconds::zero(); 113 std::chrono::microseconds updateThreadTimes_ = std::chrono::microseconds::zero(); 114 #endif 115 const bool loadSymboleWhenNeeded_ = true; // thie is a feature config 116 void UpdateSymbols(std::string filename); 117 // we don't know whether hap vma mapping is stand for a so 118 // thus we need try to parse it first 119 bool UpdateHapSymbols(std::shared_ptr<DfxMap> map); 120 bool IsSymbolExist(const std::string& fileName); 121 void DelSymbolFile(const std::string& fileName); 122 void UpdateMaps(pid_t pid, pid_t tid); GetProcessMaps()123 std::vector<std::shared_ptr<DfxMap>>& GetProcessMaps() 124 { 125 return processMaps_; 126 } 127 128 public: 129 enum SymbolCacheLimit : std::size_t { 130 USER_SYMBOL_CACHE_LIMIT = 10000, 131 }; 132 133 private: 134 struct SymbolCacheKey : public std::pair<uint64_t, uint32_t> { 135 uint64_t& ip = first; 136 uint32_t& filePathId = second; 137 explicit SymbolCacheKey() = default; 138 virtual ~SymbolCacheKey() = default; 139 SymbolCacheKey(const SymbolCacheKey &) = default; 140 SymbolCacheKey& operator=(const SymbolCacheKey& sym) 141 { 142 ip = sym.ip; 143 filePathId = sym.filePathId; 144 return *this; 145 } SymbolCacheKeySymbolCacheKey146 SymbolCacheKey(const std::pair<uint64_t, uint32_t>& arg) : pair(arg), ip(first), filePathId(second) {} SymbolCacheKeySymbolCacheKey147 SymbolCacheKey(uint64_t ip, uint32_t filePathId) : pair(ip, filePathId), ip(first), filePathId(second) {} 148 }; 149 150 // boost library recommendation algorithm to reduce hash collisions. 151 struct HashPair { operatorHashPair152 size_t operator() (const SymbolCacheKey& key) const 153 { 154 std::hash<uint64_t> hasher; 155 size_t seed = 0; 156 // 6 and 2 is the number of displacements 157 seed ^= hasher(key.ip) + 0x9e3779b9 + (seed << 6) + (seed >> 2); 158 seed ^= hasher(key.filePathId) + 0x9e3779b9 + (seed << 6) + (seed >> 2); 159 return seed; 160 } 161 }; 162 #if defined(is_ohos) && is_ohos 163 CallStack callstack_; 164 #endif 165 // pid map with user space thread 166 pthread_mutex_t threadMapsLock_; 167 std::map<pid_t, VirtualThread> userSpaceThreadMap_; 168 // not pid , just map 169 std::vector<DfxMap> kernelSpaceMaps_; 170 pthread_mutex_t processSymbolsFileLock_; 171 std::unordered_set<uint32_t> fileSet_; // for mapItem filePathId_ 172 std::unordered_map<std::string, uint32_t> functionMap_; 173 std::unordered_map<std::string, std::unique_ptr<SymbolsFile>> symbolsFiles_; 174 std::unordered_map<SymbolCacheKey, DfxSymbol, HashPair> userSymbolCache_; 175 bool GetSymbolCache(uint64_t ip, DfxSymbol &symbol, const VirtualThread &thread); 176 void UpdateSymbolCache(uint64_t ip, DfxSymbol &symbol, HashList<uint64_t, DfxSymbol> &cache); 177 178 // find synbols function name 179 void MakeCallFrame(DfxSymbol &symbol, CallFrame &callFrame); 180 181 std::string ReadThreadName(pid_t tid); 182 VirtualThread &CreateThread(pid_t pid, pid_t tid); 183 184 const DfxSymbol GetKernelSymbol(uint64_t ip, const std::vector<std::shared_ptr<DfxMap>> &maps, 185 const VirtualThread &thread); 186 const DfxSymbol GetUserSymbol(uint64_t ip, const VirtualThread &thread); 187 void FillSymbolNameId(CallFrame& callFrame, DfxSymbol& symbol); 188 void FillFileSet(CallFrame& callFrame, const DfxSymbol& symbol); 189 190 std::vector<std::string> symbolsPaths_; 191 192 friend class VirtualRuntimeTest; 193 friend class VirtualThread; 194 std::vector<std::shared_ptr<DfxMap>> processMaps_; 195 std::unordered_set<uint64_t> failedIPs_; 196 const NativeHookConfig hookConfig_; 197 uint32_t memMapFilePathId_ = 0; 198 std::map<uint64_t, std::shared_ptr<MemMaps>> mapsCache_; // key is memMap soBegin, value is MemMaps 199 std::vector<uint64_t> offlineMapAddr_; // element is memMap soBegin 200 }; 201 } // namespace NativeDaemon 202 } // namespace Developtools 203 } // namespace OHOS 204 #endif