1 /* 2 * Copyright (c) 2021-2022 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 18 #include <functional> 19 #include <fstream> 20 #if defined(is_ohos) && is_ohos 21 #include "callstack.h" 22 #endif 23 #include "hashlist.h" 24 #include "perf_event_record.h" 25 #include "symbols_file.h" 26 #include "virtual_thread.h" 27 28 namespace OHOS { 29 namespace Developtools { 30 namespace HiPerf { 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 using kSymbolsHits = std::unordered_set<uint64_t>; 44 using uSymbolsHits = std::unordered_map<pid_t, std::unordered_set<uint64_t>>; 45 46 class VirtualRuntime { 47 public: 48 explicit VirtualRuntime(bool onDevice = true); 49 ~VirtualRuntime(); 50 // thread need hook the record 51 // from the record , it will call back to write some Simulated Record 52 // case 1. some mmap will be create when it read mmaps for each new process (from record sample) 53 54 using RecordCallBack = std::function<bool(PerfEventRecord&)>; 55 using CollectSymbolCallBack = std::function<void(PerfRecordSample*)>; 56 57 void SetRecordMode(RecordCallBack recordCallBack); 58 void SetCollectSymbolCallBack(CollectSymbolCallBack collectSymbolCallBack); 59 60 // this both used in report and record follow 61 // it process the record, and rebuild the trhread maps 62 // It internally determines whether to go to the Record process (which will generate virtual 63 // events) or the Report process by judging whether SetRecordMode has been passed. 64 void UpdateFromRecord(PerfEventRecord &record); 65 void NeedDropKernelCallChain(PerfRecordSample &sample); 66 // in reocrd mode 67 // we make a kernel symbols from some proc file 68 void UpdateKernelSpaceMaps(); 69 void UpdateKernelModulesSpaceMaps(); 70 void UpdateServiceSpaceMaps(); 71 void UpdateDevhostSpaceMaps(); 72 // load vdso 73 void LoadVdso(); 74 75 void UpdateKernelSymbols(); 76 void UpdateKernelModulesSymbols(); 77 void UpdateServiceSymbols(); 78 void UpdateDevhostSymbols(); 79 80 pid_t devhostPid_ = -1; 81 void SetDevhostPid(pid_t devhost); 82 void FixHMBundleMmap(char *filename, int pid, u16 &headerSize); 83 84 // set symbols path , it will send to every symobile file for search 85 bool SetSymbolsPaths(const std::vector<std::string> &symbolsPaths); 86 87 // any mode 88 static_assert(sizeof(pid_t) == sizeof(int)); 89 GetSymbolsFiles()90 const std::vector<std::unique_ptr<SymbolsFile>> &GetSymbolsFiles() const 91 { 92 return symbolsFiles_; 93 } 94 GetUniStackTable()95 const ProcessStackMap* GetUniStackTable() 96 { 97 return &processStackMap_; 98 } 99 100 void SetCallStackExpend(size_t mergeLevel = 0) 101 { 102 callstackMergeLevel_ = mergeLevel; 103 } 104 SetDisableUnwind(bool disableUnwind)105 void SetDisableUnwind(bool disableUnwind) 106 { 107 HLOGV("disableUnwind change to %d", disableUnwind); 108 disableUnwind_ = disableUnwind; 109 } 110 EnableDebugInfoSymbolic(bool enable)111 void EnableDebugInfoSymbolic(bool enable) 112 { 113 enableDebugInfoSymbolic_ = enable; 114 } SetDedupStack()115 void SetDedupStack() 116 { 117 dedupStack_ = true; 118 } 119 120 void ImportUniqueStackNodes(const std::vector<UniStackTableInfo>&); 121 122 bool isHM_ = false; SetHM(bool isHM)123 void SetHM(bool isHM) 124 { 125 isHM_ = isHM; 126 } 127 SetNeedKernelCallChain(bool kernelCallChain)128 void SetNeedKernelCallChain(bool kernelCallChain) 129 { 130 needkernelCallChain_ = kernelCallChain; 131 } 132 DfxSymbol GetSymbol(uint64_t ip, pid_t pid, pid_t tid, 133 const perf_callchain_context &context = PERF_CONTEXT_MAX); 134 void ClearSymbolCache(); 135 136 VirtualThread &GetThread(pid_t pid, pid_t tid, const std::string name = ""); GetThreads()137 const std::map<pid_t, VirtualThread> &GetThreads() const 138 { 139 return userSpaceThreadMap_; 140 } 141 void SymbolicRecord(PerfRecordSample &recordSample); 142 void SymbolSpeRecord(PerfRecordAuxtrace &recordAuxTrace); 143 144 // report use 145 void UpdateFromPerfData(const std::vector<SymbolFileStruct> &); 146 void UnwindFromRecord(PerfRecordSample &recordSample); 147 std::string ReadThreadName(pid_t tid, bool isThread); 148 std::string ReadFromSavedCmdLines(pid_t tid); 149 bool IsKernelThread(pid_t pid); 150 void CollectDedupSymbol(kSymbolsHits &kernelSymbolsHits, 151 uSymbolsHits &userSymbolsHits); 152 // debug time 153 #ifdef HIPERF_DEBUG_TIME 154 std::chrono::microseconds updateSymbolsTimes_ = std::chrono::microseconds::zero(); 155 std::chrono::microseconds unwindFromRecordTimes_ = std::chrono::microseconds::zero(); 156 std::chrono::microseconds unwindCallStackTimes_ = std::chrono::microseconds::zero(); 157 std::chrono::microseconds symbolicRecordTimes_ = std::chrono::microseconds::zero(); 158 std::chrono::microseconds updateThreadTimes_ = std::chrono::microseconds::zero(); 159 std::chrono::microseconds processSampleRecordTimes_ = std::chrono::microseconds::zero(); 160 std::chrono::microseconds processMmapRecordTimes_ = std::chrono::microseconds::zero(); 161 std::chrono::microseconds processMmap2RecordTimes_ = std::chrono::microseconds::zero(); 162 std::chrono::microseconds processCommRecordTimes_ = std::chrono::microseconds::zero(); 163 std::chrono::microseconds threadParseMapsTimes_ = std::chrono::microseconds::zero(); 164 std::chrono::microseconds threadCreateMmapTimes_ = std::chrono::microseconds::zero(); 165 #endif 166 const bool loadSymboleWhenNeeded_ = true; // this is a feature config 167 168 private: 169 bool needkernelCallChain_ = false; 170 bool disableUnwind_ = true; 171 bool enableDebugInfoSymbolic_ = false; 172 bool dedupStack_ = false; 173 const bool isRoot_ = IsRoot(); 174 size_t callstackMergeLevel_ = 1; 175 std::ifstream savedCmdLines_; 176 #if defined(is_ohos) && is_ohos 177 CallStack callstack_; 178 #endif 179 // pid map with user space thread 180 std::map<pid_t, VirtualThread> userSpaceThreadMap_; 181 // not pid , just memmap 182 std::vector<DfxMap> kernelSpaceMemMaps_; 183 ProcessStackMap processStackMap_; 184 RecordCallBack recordCallBack_; 185 CollectSymbolCallBack collectSymbolCallBack_; 186 std::vector<std::unique_ptr<SymbolsFile>> symbolsFiles_; 187 enum SymbolCacheLimit : std::size_t { 188 KERNEL_SYMBOL_CACHE_LIMIT = 4000, 189 USER_SYMBOL_CACHE_LIMIT = 4000, 190 }; 191 HashList<uint64_t, DfxSymbol> userSymbolCache_; 192 HashList<uint64_t, DfxSymbol> kernelSymbolCache_ {KERNEL_SYMBOL_CACHE_LIMIT}; 193 HashList<uint64_t, DfxSymbol> kThreadSymbolCache_ {KERNEL_SYMBOL_CACHE_LIMIT}; 194 bool GetSymbolCache(uint64_t fileVaddr, DfxSymbol &symbol, 195 const perf_callchain_context &context); 196 // find synbols function name 197 void MakeCallFrame(DfxSymbol &symbol, DfxFrame &callFrame); 198 void UpdateSymbols(std::shared_ptr<DfxMap> map, pid_t pid); 199 // we don't know whether hap vma mapping is stand for a so 200 // thus we need try to parse it first 201 bool UpdateHapSymbols(std::shared_ptr<DfxMap> map); 202 void UpdateFromRecord(PerfRecordSample &recordSample); 203 void UpdateFromRecord(PerfRecordMmap &recordMmap); 204 void UpdateFromRecord(PerfRecordMmap2 &recordMmap2); 205 void UpdateFromRecord(PerfRecordComm &recordComm); 206 void DedupFromRecord(PerfRecordSample *recordSample); 207 void UpdateFromRecord(PerfRecordAuxtrace &recordAuxTrace); 208 // threads 209 VirtualThread &UpdateThread(pid_t pid, pid_t tid, const std::string name = ""); 210 VirtualThread &CreateThread(pid_t pid, pid_t tid, const std::string name = ""); 211 212 // maps 213 std::shared_ptr<DfxMap> UpdateThreadMaps(pid_t pid, pid_t tid, const std::string filename, uint64_t begin, 214 uint64_t len, uint64_t offset, uint32_t prot = 0); 215 void UpdatekernelMap(uint64_t begin, uint64_t end, uint64_t offset, std::string filename); 216 217 const DfxSymbol GetKernelSymbol(uint64_t ip, const std::vector<DfxMap> &memMaps, 218 const VirtualThread &thread); 219 const DfxSymbol GetUserSymbol(uint64_t ip, const VirtualThread &thread); 220 const DfxSymbol GetKernelThreadSymbol(uint64_t ip, const VirtualThread &thread); 221 #ifdef HIPERF_DEBUG 222 std::unordered_set<uint64_t> missedRuntimeVaddr_; 223 #endif 224 void SymbolicCallFrame(PerfRecordSample &recordSample, uint64_t ip, 225 pid_t serverPid, perf_callchain_context context); 226 bool RecoverCallStack(PerfRecordSample &recordSample); 227 std::vector<std::string> symbolsPaths_; 228 229 // kernel thread 230 void UpdateKernelThreadMap(pid_t pid, uint64_t begin, uint64_t len, uint64_t offset, std::string filename); 231 bool CheckValidSandBoxMmap(PerfRecordMmap2 &recordMmap2); 232 void ProcessKernelCallChain(PerfRecordSample &sample); 233 void AdjustCallChain(PerfRecordSample &sample); 234 }; 235 } // namespace HiPerf 236 } // namespace Developtools 237 } // namespace OHOS 238 #endif // HIPERF_VIRTUAL_RUNTIME_H 239