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