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(std::unique_ptr<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() const90 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 SetCallStackExpend(size_t mergeLevel = 0)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 135 VirtualThread &GetThread(pid_t pid, pid_t tid, const std::string name = ""); GetThreads() const136 const std::map<pid_t, VirtualThread> &GetThreads() const 137 { 138 return userSpaceThreadMap_; 139 } 140 void SymbolicRecord(PerfRecordSample &recordSample); 141 void SymbolSpeRecord(PerfRecordAuxtrace &recordAuxTrace); 142 143 // report use 144 void UpdateFromPerfData(const std::vector<SymbolFileStruct> &); 145 void UnwindFromRecord(PerfRecordSample &recordSample); 146 std::string ReadThreadName(pid_t tid, bool isThread); 147 std::string ReadFromSavedCmdLines(pid_t tid); 148 bool IsKernelThread(pid_t pid); 149 void CollectDedupSymbol(kSymbolsHits &kernelSymbolsHits, 150 uSymbolsHits &userSymbolsHits); 151 // debug time 152 #ifdef HIPERF_DEBUG_TIME 153 std::chrono::microseconds updateSymbolsTimes_ = std::chrono::microseconds::zero(); 154 std::chrono::microseconds unwindFromRecordTimes_ = std::chrono::microseconds::zero(); 155 std::chrono::microseconds unwindCallStackTimes_ = std::chrono::microseconds::zero(); 156 std::chrono::microseconds symbolicRecordTimes_ = std::chrono::microseconds::zero(); 157 std::chrono::microseconds updateThreadTimes_ = std::chrono::microseconds::zero(); 158 std::chrono::microseconds processSampleRecordTimes_ = std::chrono::microseconds::zero(); 159 std::chrono::microseconds processMmapRecordTimes_ = std::chrono::microseconds::zero(); 160 std::chrono::microseconds processMmap2RecordTimes_ = std::chrono::microseconds::zero(); 161 std::chrono::microseconds processCommRecordTimes_ = std::chrono::microseconds::zero(); 162 std::chrono::microseconds threadParseMapsTimes_ = std::chrono::microseconds::zero(); 163 std::chrono::microseconds threadCreateMmapTimes_ = std::chrono::microseconds::zero(); 164 #endif 165 const bool loadSymboleWhenNeeded_ = true; // this is a feature config 166 167 private: 168 bool needkernelCallChain_ = false; 169 bool disableUnwind_ = true; 170 bool enableDebugInfoSymbolic_ = false; 171 bool dedupStack_ = false; 172 size_t callstackMergeLevel_ = 1; 173 std::ifstream savedCmdLines_; 174 #if defined(is_ohos) && is_ohos 175 CallStack callstack_; 176 #endif 177 // pid map with user space thread 178 std::map<pid_t, VirtualThread> userSpaceThreadMap_; 179 // not pid , just memmap 180 std::vector<DfxMap> kernelSpaceMemMaps_; 181 ProcessStackMap processStackMap_; 182 RecordCallBack recordCallBack_; 183 CollectSymbolCallBack collectSymbolCallBack_; 184 std::vector<std::unique_ptr<SymbolsFile>> symbolsFiles_; 185 enum SymbolCacheLimit : std::size_t { 186 KERNEL_SYMBOL_CACHE_LIMIT = 4000, 187 USER_SYMBOL_CACHE_LIMIT = 4000, 188 }; 189 HashList<uint64_t, DfxSymbol> userSymbolCache_; 190 HashList<uint64_t, DfxSymbol> kernelSymbolCache_ {KERNEL_SYMBOL_CACHE_LIMIT}; 191 HashList<uint64_t, DfxSymbol> kThreadSymbolCache_ {KERNEL_SYMBOL_CACHE_LIMIT}; 192 bool GetSymbolCache(uint64_t fileVaddr, DfxSymbol &symbol, 193 const perf_callchain_context &context); 194 // find synbols function name 195 void MakeCallFrame(DfxSymbol &symbol, DfxFrame &callFrame); 196 void UpdateSymbols(std::shared_ptr<DfxMap> map, pid_t pid); 197 // we don't know whether hap vma mapping is stand for a so 198 // thus we need try to parse it first 199 bool UpdateHapSymbols(std::shared_ptr<DfxMap> map); 200 void UpdateFromRecord(PerfRecordSample &recordSample); 201 void UpdateFromRecord(PerfRecordMmap &recordMmap); 202 void UpdateFromRecord(PerfRecordMmap2 &recordMmap2); 203 void UpdateFromRecord(PerfRecordComm &recordComm); 204 void DedupFromRecord(PerfRecordSample *recordSample); 205 void UpdateFromRecord(PerfRecordAuxtrace &recordAuxTrace); 206 // threads 207 VirtualThread &UpdateThread(pid_t pid, pid_t tid, const std::string name = ""); 208 VirtualThread &CreateThread(pid_t pid, pid_t tid, const std::string name = ""); 209 210 // maps 211 std::shared_ptr<DfxMap> UpdateThreadMaps(pid_t pid, pid_t tid, const std::string filename, uint64_t begin, 212 uint64_t len, uint64_t offset, uint32_t prot = 0); 213 void UpdatekernelMap(uint64_t begin, uint64_t end, uint64_t offset, std::string filename); 214 215 const DfxSymbol GetKernelSymbol(uint64_t ip, const std::vector<DfxMap> &memMaps, 216 const VirtualThread &thread); 217 const DfxSymbol GetUserSymbol(uint64_t ip, const VirtualThread &thread); 218 const DfxSymbol GetKernelThreadSymbol(uint64_t ip, const VirtualThread &thread); 219 #ifdef HIPERF_DEBUG 220 std::unordered_set<uint64_t> missedRuntimeVaddr_; 221 #endif 222 void SymbolicCallFrame(PerfRecordSample &recordSample, uint64_t ip, 223 pid_t serverPid, perf_callchain_context context); 224 bool RecoverCallStack(PerfRecordSample &recordSample); 225 std::vector<std::string> symbolsPaths_; 226 227 // kernel thread 228 void UpdateKernelThreadMap(pid_t pid, uint64_t begin, uint64_t len, std::string filename); 229 230 FRIEND_TEST(VirtualRuntimeTest, SetRecordMode); 231 FRIEND_TEST(VirtualRuntimeTest, UpdateKernelSymbols); 232 FRIEND_TEST(VirtualRuntimeTest, UpdateKernelModulesSymbols); 233 FRIEND_TEST(VirtualRuntimeTest, SetCallStackExpend); 234 FRIEND_TEST(VirtualRuntimeTest, SetDisableUnwind); 235 FRIEND_TEST(VirtualRuntimeTest, UnwindFromRecord); 236 friend class VirtualRuntimeTest; 237 238 bool CheckValidSandBoxMmap(PerfRecordMmap2 &recordMmap2); 239 }; 240 } // namespace HiPerf 241 } // namespace Developtools 242 } // namespace OHOS 243 #endif // HIPERF_VIRTUAL_RUNTIME_H 244