• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 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 #define HILOG_TAG "Runtime"
16 
17 #include "virtual_runtime.h"
18 
19 #include <cinttypes>
20 #include <iostream>
21 #include <sstream>
22 #if !is_mingw
23 #include <sys/mman.h>
24 #endif
25 
26 #include "register.h"
27 #include "symbols.h"
28 #include "utilities.h"
29 
30 using namespace std::chrono;
31 namespace OHOS {
32 namespace Developtools {
33 namespace NativeDaemon {
34 // we unable to access 'swapper' from /proc/0/
ClearMaps()35 void VirtualRuntime::ClearMaps()
36 {
37     processMemMaps_.clear();
38 }
39 
VirtualRuntime()40 VirtualRuntime::VirtualRuntime()
41 {
42     threadMapsLock_ = PTHREAD_MUTEX_INITIALIZER;
43     threadMemMapsLock_ = PTHREAD_MUTEX_INITIALIZER;
44     UpdateThread(0, 0, "swapper");
45 }
~VirtualRuntime()46 VirtualRuntime::~VirtualRuntime()
47 {
48     ClearMaps();
49 }
ReadThreadName(pid_t tid)50 std::string VirtualRuntime::ReadThreadName(pid_t tid)
51 {
52     std::string comm = ReadFileToString(StringPrintf("/proc/%d/comm", tid)).c_str();
53     comm.erase(std::remove(comm.begin(), comm.end(), '\r'), comm.end());
54     comm.erase(std::remove(comm.begin(), comm.end(), '\n'), comm.end());
55     return comm;
56 }
UpdateThread(pid_t pid,pid_t tid,const std::string name)57 VirtualThread &VirtualRuntime::UpdateThread(pid_t pid, pid_t tid, const std::string name)
58 {
59 #ifdef HIPERF_DEBUG_TIME
60     const auto startTime = steady_clock::now();
61 #endif
62     VirtualThread &thread = GetThread(pid, tid);
63     if (!name.empty()) {
64         thread.name_ = name;
65     }
66 #ifdef HIPERF_DEBUG_TIME
67     updateThreadTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
68 #endif
69     return thread;
70 }
71 
CreateThread(pid_t pid,pid_t tid)72 VirtualThread &VirtualRuntime::CreateThread(pid_t pid, pid_t tid)
73 {
74     // make a new one
75     userSpaceThreadMap_.emplace(std::piecewise_construct, std::forward_as_tuple(tid),
76                                 std::forward_as_tuple(pid, tid, symbolsFiles_, this));
77     VirtualThread& thr = userSpaceThreadMap_.at(tid);
78     return thr;
79 }
80 
GetThread(pid_t pid,pid_t tid)81 VirtualThread &VirtualRuntime::GetThread(pid_t pid, pid_t tid)
82 {
83     HLOGV("find thread %u:%u", pid, tid);
84     pthread_mutex_lock(&threadMapsLock_);
85     auto it = userSpaceThreadMap_.find(tid);
86     if (it == userSpaceThreadMap_.end()) {
87         // we also need thread
88         VirtualThread& thr = CreateThread(pid, tid);
89         pthread_mutex_unlock(&threadMapsLock_);
90         return thr;
91     } else {
92         VirtualThread& thr = it->second;
93         pthread_mutex_unlock(&threadMapsLock_);
94         return thr;
95     }
96 }
97 
MakeCallFrame(Symbol & symbol,CallFrame & callFrame)98 void VirtualRuntime::MakeCallFrame(Symbol &symbol, CallFrame &callFrame)
99 {
100     callFrame.vaddrInFile_ = symbol.vaddr_;
101     callFrame.symbolName_ = symbol.GetName();
102     callFrame.symbolIndex_ = symbol.index_;
103     callFrame.filePath_ = symbol.module_;
104     callFrame.symbolOffset_ = symbol.offset_;
105     if (symbol.ipVaddr_ != 0) {
106         callFrame.offset_ = symbol.ipVaddr_;
107     } else {
108         callFrame.offset_ = callFrame.ip_;
109     }
110 }
111 
GetSymbolName(pid_t pid,pid_t tid,std::vector<CallFrame> & callsFrames)112 void VirtualRuntime::GetSymbolName(pid_t pid, pid_t tid, std::vector<CallFrame>& callsFrames)
113 {
114 #ifdef HIPERF_DEBUG_TIME
115     const auto startTime = steady_clock::now();
116 #endif
117     // Symbolic the Call Stack
118     HLOGV("total %zu frames", callsFrames.size());
119 
120     perf_callchain_context perfCallchainContext = PERF_CONTEXT_MAX;
121     for (auto callFrameIt = callsFrames.begin(); callFrameIt != callsFrames.end(); ++callFrameIt) {
122         auto &callFrame = callFrameIt.operator*();
123         if (callFrame.ip_ >= PERF_CONTEXT_MAX) {
124             // dont care, this is not issue.
125             HLOGV("%s", UpdatePerfContext(callFrame.ip_, perfCallchainContext).c_str());
126             callFrameIt = callsFrames.erase(callFrameIt);
127             continue;
128         }
129         auto symbol = GetSymbol(callFrame.ip_, pid, tid,
130             perfCallchainContext);
131         if (symbol.isValid()) {
132             MakeCallFrame(symbol, callFrame);
133         } else {
134             // we need found out thre reason here.
135             HLOGV("symbol is not found, remove it. addr is %" PRIx64 "", callFrame.ip_);
136             callFrameIt = callsFrames.erase(callFrameIt);
137             continue;
138         }
139         int index = callFrameIt - callsFrames.begin();
140         HLOGV(" (%u)unwind symbol: %*s%s", index, index, "", callFrame.ToSymbolString().c_str());
141     }
142 #ifdef HIPERF_DEBUG_TIME
143     auto usedTime = duration_cast<microseconds>(steady_clock::now() - startTime);
144     if (usedTime.count() != 0) {
145         HLOGV("cost %0.3f ms to symbolic ", usedTime.count() / MS_DUARTION);
146     }
147     symbolicRecordTimes_ += usedTime;
148 #endif
149 }
150 
UnwindStack(std::vector<u64> regs,const u8 * stack_addr,int stack_size,pid_t pid,pid_t tid,std::vector<CallFrame> & callsFrames,size_t maxStackLevel)151 void VirtualRuntime::UnwindStack(std::vector<u64> regs,
152                                  const u8* stack_addr,
153                                  int stack_size,
154                                  pid_t pid,
155                                  pid_t tid,
156                                  std::vector<CallFrame>& callsFrames,
157                                  size_t maxStackLevel)
158 {
159 #ifdef HIPERF_DEBUG_TIME
160     const auto startTime = steady_clock::now();
161 #endif
162     // if we have userstack ?
163     if (stack_size > 0) {
164         auto &thread = UpdateThread(pid, tid);
165         std::vector<char> stack(stack_addr,
166                             stack_addr + stack_size);
167         callstack_.UnwindCallStack(thread, regs, stack, callsFrames, maxStackLevel);
168 #ifdef HIPERF_DEBUG_TIME
169         unwindCallStackTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
170 #endif
171     }
172 
173 #ifdef HIPERF_DEBUG_TIME
174     unwindFromRecordTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
175 #endif
176     GetSymbolName(pid, tid, callsFrames);
177 }
178 
UpdateSymbols(std::string fileName)179 void VirtualRuntime::UpdateSymbols(std::string fileName)
180 {
181     HLOGD("try to find symbols for file: %s", fileName.c_str());
182 #ifdef HIPERF_DEBUG_TIME
183     const auto startTime = steady_clock::now();
184 #endif
185 
186     for (auto &symbolsFile : symbolsFiles_) {
187         if (symbolsFile->filePath_ == fileName) {
188             HLOGV("already have '%s'", fileName.c_str());
189             return;
190         }
191     }
192     // found it by name
193     auto symbolsFile = SymbolsFile::CreateSymbolsFile(fileName);
194 
195     // set sybol path If it exists
196     if (symbolsPaths_.size() > 0) {
197         symbolsFile->setSymbolsFilePath(symbolsPaths_); // also load from search path
198     }
199     if (loadSymboleWhenNeeded_) {
200         // load it when we need it
201         symbolsFiles_.insert(std::move(symbolsFile));
202     } else if (symbolsFile->LoadSymbols()) {
203         symbolsFiles_.insert(std::move(symbolsFile));
204     } else {
205         HLOGW("symbols file for '%s' not found.", fileName.c_str());
206     }
207 #ifdef HIPERF_DEBUG_TIME
208     auto usedTime = duration_cast<microseconds>(steady_clock::now() - startTime);
209     if (usedTime.count() != 0) {
210         HLOGV("cost %0.3f ms to load '%s'", usedTime.count() / MS_DUARTION, fileName.c_str());
211     }
212     updateSymbolsTimes_ += usedTime;
213 #endif
214 }
215 
GetKernelSymbol(uint64_t ip,const std::vector<MemMapItem> & memMaps)216 const Symbol VirtualRuntime::GetKernelSymbol(uint64_t ip, const std::vector<MemMapItem> &memMaps)
217 {
218     Symbol vaddrSymbol;
219     for (auto map : memMaps) {
220         if (ip > map.begin_ && ip < map.end_) {
221             HLOGM("found addr 0x%" PRIx64 " in kernel map 0x%" PRIx64 " - 0x%" PRIx64 " from %s",
222                 ip, map.begin_, map.end_, map.name_.c_str());
223             vaddrSymbol.module_ = map.name_;
224             // found symbols by file name
225             for (auto &symbolsFile : symbolsFiles_) {
226                 if (symbolsFile->filePath_ == map.name_) {
227                     vaddrSymbol.ipVaddr_ =
228                         symbolsFile->GetVaddrInSymbols(ip, map.begin_, map.pageoffset_);
229                     HLOGM("check vaddr 0x%" PRIx64 " in symbols for addr 0x%" PRIx64 " at '%s'",
230                         vaddrSymbol.ipVaddr_, ip, map.name_.c_str());
231                     Symbol foundSymbols = symbolsFile->GetSymbolWithVaddr(vaddrSymbol.ipVaddr_);
232                     return foundSymbols.isValid() ? foundSymbols : vaddrSymbol;
233                 }
234             }
235             HLOGW("addr 0x%" PRIx64 " in map but NOT found the symbol file %s", ip,
236                 map.name_.c_str());
237         } else {
238             HLOGM("addr 0x%" PRIx64 " not in map 0x%" PRIx64 " - 0x%" PRIx64 " from %s", ip,
239                 map.begin_, map.end_, map.name_.c_str());
240         }
241     }
242     return vaddrSymbol;
243 }
244 
GetUserSymbol(uint64_t ip,const VirtualThread & thread)245 const Symbol VirtualRuntime::GetUserSymbol(uint64_t ip, const VirtualThread &thread)
246 {
247     Symbol vaddrSymbol;
248     MemMapItem mmap;
249     if (thread.FindMapByAddr(ip, mmap)) {
250         SymbolsFile *symbolsFile = thread.FindSymbolsFileByMap(mmap);
251         if (symbolsFile != nullptr) {
252             vaddrSymbol.ipVaddr_ =
253                 symbolsFile->GetVaddrInSymbols(ip, mmap.begin_, mmap.pageoffset_);
254             vaddrSymbol.module_ = mmap.name_;
255             HLOGM("check vaddr 0x%" PRIx64 " in symbols for addr 0x%" PRIx64 " at '%s'",
256                 vaddrSymbol.ipVaddr_, ip, mmap.name_.c_str());
257             Symbol foundSymbols = symbolsFile->GetSymbolWithVaddr(vaddrSymbol.ipVaddr_);
258             if (!foundSymbols.isValid()) {
259                 HLOGW("addr 0x%" PRIx64 " vaddr  0x%" PRIx64 " NOT found in symbol file %s", ip,
260                     vaddrSymbol.ipVaddr_, mmap.name_.c_str());
261                 return vaddrSymbol;
262             }
263             return foundSymbols;
264         } else {
265             HLOGW("addr 0x%" PRIx64 " in map but NOT found the symbol file %s", ip,
266                 mmap.name_.c_str());
267         }
268     } else {
269         HLOGM("addr 0x%" PRIx64 " not in any user map", ip);
270     }
271     return vaddrSymbol;
272 }
GetSymbolCache(uint64_t ip,pid_t pid,pid_t tid,Symbol & symbol,const perf_callchain_context & context)273 bool VirtualRuntime::GetSymbolCache(uint64_t ip, pid_t pid, pid_t tid, Symbol &symbol,
274     const perf_callchain_context &context)
275 {
276     if (context != PERF_CONTEXT_USER and kernelSymbolCache_.count(ip)) {
277         if (kernelSymbolCache_.find(ip) == kernelSymbolCache_.end()) {
278             return false;
279         }
280         Symbol &foundSymbol = kernelSymbolCache_[ip];
281         foundSymbol.hit_++;
282         HLOGM("hit kernel cache 0x%" PRIx64 " %d", ip, foundSymbol.hit_);
283         symbol = foundSymbol;
284         return true;
285     } else if (threadSymbolCache_[tid].count(ip)) {
286         Symbol &foundSymbol = threadSymbolCache_[tid][ip];
287         foundSymbol.hit_++;
288         HLOGM("hit user cache 0x%" PRIx64 " %d", ip, foundSymbol.hit_);
289         symbol = foundSymbol;
290         return true;
291     } else {
292         HLOGM("cache miss k %zu u %zu", kernelSymbolCache_.size(), threadSymbolCache_[tid].size());
293     }
294     return false;
295 }
296 
297 // void VirtualRuntime::UpdateSymbolCache(uint64_t ip, Symbol &symbol,
298 //     std::map<uint64_t, Symbol> &cache)
UpdateSymbolCache(uint64_t ip,Symbol & symbol,HashList<uint64_t,Symbol> & cache)299 void VirtualRuntime::UpdateSymbolCache(uint64_t ip, Symbol &symbol,
300     HashList<uint64_t, Symbol> &cache)
301 {
302     // review change to LRU for memmory
303     HLOG_ASSERT_MESSAGE(cache.count(ip) == 0, "already have cached ip 0x%" PRIx64 "", ip);
304     cache[ip] = symbol;
305 }
306 
GetSymbol(uint64_t ip,pid_t pid,pid_t tid,const perf_callchain_context & context)307 const Symbol VirtualRuntime::GetSymbol(uint64_t ip, pid_t pid, pid_t tid,
308     const perf_callchain_context &context)
309 {
310     HLOGM("try find tid %u ip 0x%" PRIx64 " in %zu symbolsFiles ", tid, ip, symbolsFiles_.size());
311     Symbol symbol;
312     if (GetSymbolCache(ip, pid, tid, symbol, context)) {
313         return symbol;
314     }
315 
316     if (context != PERF_CONTEXT_USER) {
317         // check kernelspace
318         HLOGM("try found addr in kernelspace %zu maps ", kernelSpaceMemMaps_.size());
319         symbol = GetKernelSymbol(ip, kernelSpaceMemMaps_);
320     }
321 
322     if (!symbol.isValid()) {
323         // check userspace memmap
324         symbol = GetUserSymbol(ip, GetThread(pid, tid));
325         UpdateSymbolCache(ip, symbol, threadSymbolCache_[tid]);
326     } else {
327         HLOGM("add addr to kernel cache 0x%" PRIx64 " cache size %zu ", ip,
328             kernelSymbolCache_.size());
329         UpdateSymbolCache(ip, symbol, kernelSymbolCache_);
330     }
331     return symbol;
332 }
333 
SetSymbolsPaths(const std::vector<std::string> & symbolsPaths)334 bool VirtualRuntime::SetSymbolsPaths(const std::vector<std::string> &symbolsPaths)
335 {
336     std::unique_ptr<SymbolsFile> symbolsFile = SymbolsFile::CreateSymbolsFile(SYMBOL_UNKNOW_FILE);
337     // we need check if the path is accessable
338     bool accessable = symbolsFile->setSymbolsFilePath(symbolsPaths);
339     if (accessable) {
340         symbolsPaths_ = symbolsPaths;
341     }
342     return accessable;
343 }
344 } // namespace NativeDaemon
345 } // namespace Developtools
346 } // namespace OHOS