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(const 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(const RecordCallBack &recordCallBack); 58 void SetCollectSymbolCallBack(const 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(const pid_t devhost); 82 void FixHMBundleMmap(char *filename, const 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(const size_t mergeLevel = 0) 101 { 102 callstackMergeLevel_ = mergeLevel; 103 } 104 SetDisableUnwind(const bool disableUnwind)105 void SetDisableUnwind(const bool disableUnwind) 106 { 107 HLOGV("disableUnwind change to %d", disableUnwind); 108 disableUnwind_ = disableUnwind; 109 } 110 EnableDebugInfoSymbolic(const bool enable)111 void EnableDebugInfoSymbolic(const 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(const bool kernelCallChain)128 void SetNeedKernelCallChain(const bool kernelCallChain) 129 { 130 needkernelCallChain_ = kernelCallChain; 131 } 132 DfxSymbol GetSymbol(const uint64_t ip, const pid_t pid, const pid_t tid, 133 const perf_callchain_context &context = PERF_CONTEXT_MAX); 134 void ClearSymbolCache(); 135 136 VirtualThread &GetThread(const pid_t pid, const 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(const pid_t tid, const bool isThread); 148 std::string ReadFromSavedCmdLines(const pid_t tid); 149 bool IsKernelThread(const 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(const 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, const 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(const pid_t pid, const pid_t tid, const std::string name = ""); 210 VirtualThread &CreateThread(const pid_t pid, const pid_t tid, const std::string name = ""); 211 212 // maps 213 std::shared_ptr<DfxMap> UpdateThreadMaps(const pid_t pid, const pid_t tid, const std::string filename, 214 const uint64_t begin, const uint64_t len, const uint64_t offset, 215 const uint32_t prot = 0); 216 void UpdatekernelMap(const uint64_t begin, const uint64_t end, const uint64_t offset, const std::string &filename); 217 218 const DfxSymbol GetKernelSymbol(const uint64_t ip, const std::vector<DfxMap> &memMaps, 219 const VirtualThread &thread); 220 const DfxSymbol GetUserSymbol(const uint64_t ip, const VirtualThread &thread); 221 const DfxSymbol GetKernelThreadSymbol(const uint64_t ip, const VirtualThread &thread); 222 #ifdef HIPERF_DEBUG 223 std::unordered_set<uint64_t> missedRuntimeVaddr_; 224 #endif 225 void SymbolicCallFrame(PerfRecordSample &recordSample, const uint64_t ip, 226 const pid_t serverPid, const perf_callchain_context context); 227 bool RecoverCallStack(PerfRecordSample &recordSample); 228 std::vector<std::string> symbolsPaths_; 229 230 // kernel thread 231 void UpdateKernelThreadMap(const pid_t pid, const uint64_t begin, const uint64_t len, 232 const uint64_t offset, const std::string &filename); 233 bool CheckValidSandBoxMmap(PerfRecordMmap2 &recordMmap2); 234 void ProcessKernelCallChain(PerfRecordSample &sample); 235 void AdjustCallChain(PerfRecordSample &sample); 236 }; 237 } // namespace HiPerf 238 } // namespace Developtools 239 } // namespace OHOS 240 #endif // HIPERF_VIRTUAL_RUNTIME_H 241