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
28namespace OHOS {
29namespace Developtools {
30namespace HiPerf {
31/*
32This Class contains userspace thread objects. and kernel space objects
33It represents a virtual operating environment, mainly referring to the relationship between pid,
34mmaps, and symbols.
35
36It mainly receives data is ip pointer (virtual address), pid
37According to these data, it will find the corresponding mmap and its corresponding elf (also called
38DSO)
39
40Then find the corresponding symbol in the corresponding elf symbol file according to the offset
41recorded in the corresponding mmap.
42*/
43using kSymbolsHits = std::unordered_set<uint64_t>;
44using uSymbolsHits = std::unordered_map<pid_t, std::unordered_set<uint64_t>>;
45
46class VirtualRuntime {
47public:
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
90    const std::vector<std::unique_ptr<SymbolsFile>> &GetSymbolsFiles() const
91    {
92        return symbolsFiles_;
93    }
94
95    const ProcessStackMap* GetUniStackTable()
96    {
97        return &processStackMap_;
98    }
99
100    void SetCallStackExpend(size_t mergeLevel = 0)
101    {
102        callstackMergeLevel_ = mergeLevel;
103    }
104
105    void SetDisableUnwind(bool disableUnwind)
106    {
107        HLOGV("disableUnwind change to %d", disableUnwind);
108        disableUnwind_ = disableUnwind;
109    }
110
111    void EnableDebugInfoSymbolic(bool enable)
112    {
113        enableDebugInfoSymbolic_ = enable;
114    }
115    void SetDedupStack()
116    {
117        dedupStack_ = true;
118    }
119
120    void ImportUniqueStackNodes(const std::vector<UniStackTableInfo>&);
121
122    bool isHM_ = false;
123    void SetHM(bool isHM)
124    {
125        isHM_ = isHM;
126    }
127
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 = "");
136    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
167private:
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