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 20 #include "callstack.h" 21 #include "perf_event_record.h" 22 #include "symbols_file.h" 23 #include "virtual_thread.h" 24 25 namespace OHOS { 26 namespace Developtools { 27 namespace HiPerf { 28 /* 29 This Class contains userspace thread objects. and kernel space objects 30 It represents a virtual operating environment, mainly referring to the relationship between pid, 31 mmaps, and symbols. 32 33 It mainly receives data is ip pointer (virtual address), pid 34 According to these data, it will find the corresponding mmap and its corresponding elf (also called 35 DSO) 36 37 Then find the corresponding symbol in the corresponding elf symbol file according to the offset 38 recorded in the corresponding mmap. 39 */ 40 41 class VirtualRuntime { 42 public: 43 VirtualRuntime(bool onDevice = true); 44 45 // thread need hook the record 46 // from the record , it will call back to write some Simulated Record 47 // case 1. some mmap will be create when it read mmaps for each new process (from record sample) 48 49 using RecordCallBack = std::function<bool(std::unique_ptr<PerfEventRecord>)>; 50 void SetRecordMode(RecordCallBack recordCallBack); 51 52 // this both used in report and record follow 53 // it process the record, and rebuild the trhread maps 54 // It internally determines whether to go to the Record process (which will generate virtual 55 // events) or the Report process by judging whether SetRecordMode has been passed. 56 void UpdateFromRecord(PerfEventRecord &reocrd); 57 58 // in reocrd mode 59 // we make a kernel symbols from some proc file 60 void UpdateKernelSpaceMaps(); 61 void UpdateKernelModulesSpaceMaps(); 62 // load vdso 63 void LoadVdso(); 64 65 void UpdateKernelSymbols(); 66 void UpdateKernelModulesSymbols(); 67 68 // set symbols path , it will send to every symobile file for search 69 bool SetSymbolsPaths(const std::vector<std::string> &symbolsPaths); 70 71 // any mode 72 static_assert(sizeof(pid_t) == sizeof(int)); 73 GetSymbolsFiles()74 const std::vector<std::unique_ptr<SymbolsFile>> &GetSymbolsFiles() const 75 { 76 return symbolsFiles_; 77 } 78 79 void SetCallStackExpend(size_t mergeLevel = 0) 80 { 81 callstackMergeLevel_ = mergeLevel; 82 } 83 SetDisableUnwind(bool disableUnwind)84 void SetDisableUnwind(bool disableUnwind) 85 { 86 HLOGV("disableUnwind change to %d", disableUnwind); 87 disableUnwind_ = disableUnwind; 88 } 89 90 const Symbol GetSymbol(uint64_t ip, pid_t pid, pid_t tid, 91 const perf_callchain_context &context = PERF_CONTEXT_MAX); 92 93 VirtualThread &GetThread(pid_t pid, pid_t tid); GetThreads()94 const std::map<pid_t, VirtualThread> &GetThreads() const 95 { 96 return userSpaceThreadMap_; 97 } 98 void SymbolicRecord(PerfRecordSample &recordSample); 99 100 // report use 101 void UpdateFromPerfData(const std::vector<SymbolFileStruct> &); 102 void UnwindFromRecord(PerfRecordSample &recordSample); 103 std::string ReadThreadName(pid_t tid); 104 105 // debug time 106 #ifdef HIPERF_DEBUG_TIME 107 std::chrono::microseconds updateSymbolsTimes_ = std::chrono::microseconds::zero(); 108 std::chrono::microseconds unwindFromRecordTimes_ = std::chrono::microseconds::zero(); 109 std::chrono::microseconds unwindCallStackTimes_ = std::chrono::microseconds::zero(); 110 std::chrono::microseconds symbolicRecordTimes_ = std::chrono::microseconds::zero(); 111 std::chrono::microseconds updateThreadTimes_ = std::chrono::microseconds::zero(); 112 std::chrono::microseconds prcessSampleRecordTimes_ = std::chrono::microseconds::zero(); 113 std::chrono::microseconds prcessMmapRecordTimes_ = std::chrono::microseconds::zero(); 114 std::chrono::microseconds prcessMmap2RecordTimes_ = std::chrono::microseconds::zero(); 115 std::chrono::microseconds prcessCommRecordTimes_ = std::chrono::microseconds::zero(); 116 std::chrono::microseconds threadParseMapsTimes_ = std::chrono::microseconds::zero(); 117 std::chrono::microseconds threadCreateMmapTimes_ = std::chrono::microseconds::zero(); 118 #endif 119 const bool loadSymboleWhenNeeded_ = true; // this is a feature config 120 121 private: 122 bool disableUnwind_ = true; 123 size_t callstackMergeLevel_ = 1; 124 CallStack callstack_; 125 // pid map with user space thread 126 std::map<pid_t, VirtualThread> userSpaceThreadMap_; 127 // not pid , just memmap 128 std::vector<MemMapItem> kernelSpaceMemMaps_; 129 RecordCallBack recordCallBack_; 130 std::vector<std::unique_ptr<SymbolsFile>> symbolsFiles_; 131 enum SymbolCacheLimit : std::size_t { 132 KERNEL_SYMBOL_CACHE_LIMIT = 4000, 133 THREAD_SYMBOL_CACHE_LIMIT = 2000, 134 }; 135 std::unordered_map<pid_t, HashList<uint64_t, Symbol>> threadSymbolCache_; 136 HashList<uint64_t, Symbol> kernelSymbolCache_ {KERNEL_SYMBOL_CACHE_LIMIT}; 137 bool GetSymbolCache(uint64_t ip, pid_t pid, pid_t tid, Symbol &symbol, 138 const perf_callchain_context &context); 139 // find synbols function name 140 void MakeCallFrame(Symbol &symbol, CallFrame &callFrame); 141 // records 142 void UpdateSymbols(std::string filename); 143 void UpdateFromRecord(PerfRecordSample &recordSample); 144 void UpdateFromRecord(PerfRecordMmap &recordMmap); 145 void UpdateFromRecord(PerfRecordMmap2 &recordMmap2); 146 void UpdateFromRecord(PerfRecordComm &recordComm); 147 148 // threads 149 VirtualThread &UpdateThread(pid_t pid, pid_t tid, const std::string name = ""); 150 VirtualThread &CreateThread(pid_t pid, pid_t tid); 151 152 // maps 153 void UpdateThreadMaps(pid_t pid, pid_t tid, const std::string filename, uint64_t begin, 154 uint64_t len, uint64_t offset); 155 void UpdatekernelMap(uint64_t begin, uint64_t end, uint64_t offset, std::string filename); 156 157 const Symbol GetKernelSymbol(uint64_t ip, const std::vector<MemMapItem> &memMaps, 158 const VirtualThread &thread); 159 const Symbol GetUserSymbol(uint64_t ip, const VirtualThread &thread); 160 #ifdef HIPERF_DEBUG 161 std::unordered_set<uint64_t> missedRuntimeVaddr_; 162 #endif 163 void SymbolicCallFrame(PerfRecordSample &recordSample, uint64_t ip, 164 perf_callchain_context context); 165 166 std::vector<std::string> symbolsPaths_; 167 168 FRIEND_TEST(VirtualRuntimeTest, SetRecordMode); 169 FRIEND_TEST(VirtualRuntimeTest, UpdateKernelSymbols); 170 FRIEND_TEST(VirtualRuntimeTest, UpdateKernelModulesSymbols); 171 FRIEND_TEST(VirtualRuntimeTest, SetCallStackExpend); 172 FRIEND_TEST(VirtualRuntimeTest, SetDisableUnwind); 173 FRIEND_TEST(VirtualRuntimeTest, UnwindFromRecord); 174 friend class VirtualRuntimeTest; 175 }; 176 } // namespace HiPerf 177 } // namespace Developtools 178 } // namespace OHOS 179 #endif // HIPERF_VIRTUAL_RUNTIME_H 180