1 /*
2  * Copyright (c) Huawei Technologies Co., Ltd. 2021. All rights reserved.
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 #include <unistd.h>
18 #include <sys/types.h>
19 #include <pthread.h>
20 #include <functional>
21 #include <map>
22 #if defined(is_ohos) && is_ohos
23 #include "call_stack.h"
24 #endif
25 #include "hashlistpp.h"
26 #include "perf_event_record.h"
27 #include "symbols_file.h"
28 #include "virtual_thread.h"
29 #include "native_hook_config.pb.h"
30 
31 namespace OHOS {
32 namespace Developtools {
33 namespace NativeDaemon {
34 /*
35 This Class contains userspace thread objects. and kernel space objects
36 It represents a virtual operating environment, mainly referring to the relationship between pid,
37 mmaps, and symbols.
38 
39 It mainly receives data is ip pointer (virtual address), pid
40 According to these data, it will find the corresponding mmap and its corresponding elf (also called
41 DSO)
42 
43 Then find the corresponding symbol in the corresponding elf symbol file according to the offset
44 recorded in the corresponding mmap.
45 */
46 
47 class VirtualRuntime {
48 public:
49     VirtualRuntime() = default;
50     VirtualRuntime(const NativeHookConfig& hookConfig);
51     virtual ~VirtualRuntime();
52     // thread need hook the record
53     // from the record , it will call back to write some Simulated Record
54     // case 1. some mmap will be create when it read mmaps for each new process (from record sample)
55 
56     // set symbols path , it will send to every symobile file for search
57     bool SetSymbolsPaths(const std::vector<std::string> &symbolsPaths);
58 
59     // any mode
60     static_assert(sizeof(pid_t) == sizeof(int));
61 
GetSymbolsFiles() const62     const std::unordered_map<std::string, std::unique_ptr<SymbolsFile>> &GetSymbolsFiles() const
63     {
64         return symbolsFiles_;
65     }
66 
67     const DfxSymbol GetSymbol(CallFrame& callFrame, pid_t pid, pid_t tid,
68                            const perf_callchain_context &context = PERF_CONTEXT_MAX);
69 
70     VirtualThread &GetThread(pid_t pid, pid_t tid);
GetThreads() const71     const std::map<pid_t, VirtualThread> &GetThreads() const
72     {
73         return userSpaceThreadMap_;
74     }
75 
76     bool UnwindStack(std::vector<u64>& regs,
77                      const u8* stack_addr,
78                      int stack_size,
79                      pid_t pid,
80                      pid_t tid,
81                      std::vector<CallFrame>& callFrames,
82                      size_t maxStackLevel);
83     bool GetSymbolName(pid_t pid, pid_t tid, std::vector<CallFrame>& callFrames, int offset, bool first,
84                        bool onlyjs = false);
85     void ClearMaps();
86     void FillMapsCache(std::string& currentFileName, std::shared_ptr<DfxMap> mapItem);
87     void HandleMapInfo(std::vector<uint64_t> info, const std::string& filePath, pid_t pid, pid_t tid);
88     void RemoveMaps(uint64_t addr);
89       // threads
90     VirtualThread &UpdateThread(pid_t pid, pid_t tid, const std::string name = "");
91     void FillSymbolNameId(CallFrame& callFrame, DfxSymbol& symbol);
92     void FillFileSet(CallFrame& callFrame, const DfxSymbol& symbol);
93     uint32_t FillArkTsFilePath(std::string_view& jstr);
94     uint32_t FindArkTsFilePath(std::string_view& jstr);
95     bool ArktsGetSymbolCache(CallFrame& callFrame, DfxSymbol &symbol);
96     uint32_t GetJsSymbolCacheSize();
97     void FillJsSymbolCache(CallFrame& callFrame, const DfxSymbol& symbol);
GetOfflineMaps()98     std::vector<uint64_t>& GetOfflineMaps()
99     {
100         return offlineMapAddr_;
101     }
102 
ClearOfflineMaps()103     void ClearOfflineMaps()
104     {
105         offlineMapAddr_.clear();
106     }
107 
GetMapsCache()108     std::map<uint64_t, std::shared_ptr<MemMaps>>& GetMapsCache()
109     {
110         return mapsCache_;
111     }
112 
113     std::pair<std::shared_ptr<MemMaps>, uint32_t> FindMap(uint64_t addr);
114     uint64_t soBegin_ {0};
115     // debug time
116 #ifdef HIPERF_DEBUG_TIME
117     std::chrono::microseconds updateSymbolsTimes_ = std::chrono::microseconds::zero();
118     std::chrono::microseconds unwindFromRecordTimes_ = std::chrono::microseconds::zero();
119     std::chrono::microseconds unwindCallStackTimes_ = std::chrono::microseconds::zero();
120     std::chrono::microseconds symbolicRecordTimes_ = std::chrono::microseconds::zero();
121     std::chrono::microseconds updateThreadTimes_ = std::chrono::microseconds::zero();
122 #endif
123     const bool loadSymboleWhenNeeded_ = true; // thie is a feature config
124     void UpdateSymbols(std::string filename, std::shared_ptr<DfxMap> map);
125     // we don't know whether hap vma mapping is stand for a so
126     // thus we need try to parse it first
127     bool UpdateHapSymbols(std::shared_ptr<DfxMap> map);
128     bool IsSymbolExist(const std::string& fileName);
129     void DelSymbolFile(const std::string& fileName);
130     void UpdateMaps(pid_t pid, pid_t tid);
GetProcessMaps()131     std::vector<std::shared_ptr<DfxMap>>& GetProcessMaps()
132     {
133         return processMaps_;
134     }
135 
136 public:
137     enum SymbolCacheLimit : std::size_t {
138         USER_SYMBOL_CACHE_LIMIT = 10000,
139     };
140 
141 private:
142     struct SymbolCacheKey : public std::pair<uint64_t, uint32_t> {
143         uint64_t& ip = first;
144         uint32_t& filePathId = second;
145         explicit SymbolCacheKey() = default;
146         virtual ~SymbolCacheKey() = default;
147         SymbolCacheKey(const SymbolCacheKey &) = default;
operator =OHOS::Developtools::NativeDaemon::VirtualRuntime::SymbolCacheKey148         SymbolCacheKey& operator=(const SymbolCacheKey& sym)
149         {
150             ip = sym.ip;
151             filePathId = sym.filePathId;
152             return *this;
153         }
SymbolCacheKeyOHOS::Developtools::NativeDaemon::VirtualRuntime::SymbolCacheKey154         SymbolCacheKey(const std::pair<uint64_t, uint32_t>& arg) : pair(arg), ip(first), filePathId(second) {}
SymbolCacheKeyOHOS::Developtools::NativeDaemon::VirtualRuntime::SymbolCacheKey155         SymbolCacheKey(uint64_t ip, uint32_t filePathId) : pair(ip, filePathId), ip(first), filePathId(second) {}
156     };
157 
158     // boost library recommendation algorithm to reduce hash collisions.
159     struct HashPair {
operator ()OHOS::Developtools::NativeDaemon::VirtualRuntime::HashPair160         size_t operator() (const SymbolCacheKey& key) const
161         {
162             std::hash<uint64_t> hasher;
163             size_t seed = 0;
164             // 6 and 2 is the number of displacements
165             seed ^= hasher(key.ip) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
166             seed ^= hasher(key.filePathId) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
167             return seed;
168         }
169     };
170 #if defined(is_ohos) && is_ohos
171     CallStack callstack_;
172 #endif
173     // pid map with user space thread
174     pid_t pid_ = 0;
175     pthread_mutex_t threadMapsLock_;
176     std::map<pid_t, VirtualThread> userSpaceThreadMap_;
177     // not pid , just map
178     std::vector<DfxMap> kernelSpaceMaps_;
179     pthread_mutex_t processSymbolsFileLock_;
180     std::unordered_set<uint32_t> fileSet_; // for mapItem filePathId_
181     std::unordered_map<std::string, uint32_t> functionMap_;
182     std::unordered_map<std::string, std::unique_ptr<SymbolsFile>> symbolsFiles_;
183     std::unordered_map<SymbolCacheKey, DfxSymbol, HashPair> userSymbolCache_;
184     bool GetSymbolCache(uint64_t ip, DfxSymbol &symbol, const VirtualThread &thread);
185     void UpdateSymbolCache(uint64_t ip, DfxSymbol &symbol, HashList<uint64_t, DfxSymbol> &cache);
186 
187     // find synbols function name
188     void MakeCallFrame(DfxSymbol &symbol, CallFrame &callFrame);
189 
190     std::string ReadThreadName(pid_t tid);
191     VirtualThread &CreateThread(pid_t pid, pid_t tid);
192 
193     const DfxSymbol GetKernelSymbol(uint64_t ip, const std::vector<std::shared_ptr<DfxMap>> &maps,
194                                  const VirtualThread &thread);
195     const DfxSymbol GetUserSymbol(uint64_t ip, const VirtualThread &thread);
196 
197     std::vector<std::string> symbolsPaths_;
198 
199     friend class VirtualRuntimeTest;
200     friend class VirtualThread;
201     std::vector<std::shared_ptr<DfxMap>> processMaps_;
202     std::unordered_set<uint64_t> failedIPs_;
203     const NativeHookConfig hookConfig_;
204     uint32_t memMapFilePathId_ = 0;
205     std::map<uint64_t, std::shared_ptr<MemMaps>> mapsCache_; // key is memMap soBegin, value is MemMaps
206     std::vector<uint64_t> offlineMapAddr_; // element is memMap soBegin
207     std::unordered_map<std::string_view, uint32_t> jsUrlMap_; // Key is js url , value is filePathId
208 };
209 } // namespace NativeDaemon
210 } // namespace Developtools
211 } // namespace OHOS
212 #endif