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