• 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 #include <unistd.h>
23 #if !is_mingw
24 #include <sys/mman.h>
25 #endif
26 
27 #include "register.h"
28 #include "symbols_file.h"
29 #include "utilities.h"
30 
31 using namespace std::chrono;
32 namespace OHOS {
33 namespace Developtools {
34 namespace NativeDaemon {
35 // we unable to access 'swapper' from /proc/0/
ClearMaps()36 void VirtualRuntime::ClearMaps()
37 {
38     processMemMaps_.clear();
39 }
40 
VirtualRuntime(bool onDevice)41 VirtualRuntime::VirtualRuntime(bool onDevice)
42 {
43     threadMapsLock_ = PTHREAD_MUTEX_INITIALIZER;
44     threadMemMapsLock_ = PTHREAD_MUTEX_INITIALIZER;
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 }
57 
UpdateThread(pid_t pid,pid_t tid,const std::string name)58 VirtualThread &VirtualRuntime::UpdateThread(pid_t pid, pid_t tid, const std::string name)
59 {
60 #ifdef HIPERF_DEBUG_TIME
61     const auto startTime = steady_clock::now();
62 #endif
63     VirtualThread &thread = GetThread(pid, tid);
64     if (!name.empty()) {
65         thread.name_ = name;
66     }
67 #ifdef HIPERF_DEBUG_TIME
68     updateThreadTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
69 #endif
70     return thread;
71 }
72 
CreateThread(pid_t pid,pid_t tid)73 VirtualThread &VirtualRuntime::CreateThread(pid_t pid, pid_t tid)
74 {
75     // make a new one
76     userSpaceThreadMap_.emplace(std::piecewise_construct, std::forward_as_tuple(tid),
77                                 std::forward_as_tuple(pid, tid, symbolsFiles_, this));
78     VirtualThread& thr = userSpaceThreadMap_.at(tid);
79     return thr;
80 }
81 
GetThread(pid_t pid,pid_t tid)82 VirtualThread &VirtualRuntime::GetThread(pid_t pid, pid_t tid)
83 {
84     HLOGV("find thread %u:%u", pid, tid);
85     pthread_mutex_lock(&threadMapsLock_);
86     auto it = userSpaceThreadMap_.find(tid);
87     if (it == userSpaceThreadMap_.end()) {
88         // we also need thread
89         VirtualThread& thr = CreateThread(pid, tid);
90         pthread_mutex_unlock(&threadMapsLock_);
91         return thr;
92     } else {
93         VirtualThread& thr = it->second;
94         pthread_mutex_unlock(&threadMapsLock_);
95         return thr;
96     }
97 }
98 
MakeCallFrame(Symbol & symbol,CallFrame & callFrame)99 void VirtualRuntime::MakeCallFrame(Symbol &symbol, CallFrame &callFrame)
100 {
101     callFrame.vaddrInFile_ = symbol.funcVaddr_;
102     callFrame.symbolName_ = symbol.symbolName_;
103     callFrame.symbolIndex_ = symbol.index_;
104     callFrame.filePath_ = symbol.module_.empty() ? symbol.comm_ : symbol.module_;
105     callFrame.symbolOffset_ = symbol.offset_;
106     if (symbol.funcVaddr_ != 0) {
107         callFrame.offset_ = symbol.funcVaddr_;
108     } else {
109         callFrame.offset_ = callFrame.ip_;
110     }
111 }
112 
GetSymbolName(pid_t pid,pid_t tid,std::vector<CallFrame> & callsFrames,int offset,bool first)113 bool VirtualRuntime::GetSymbolName(pid_t pid, pid_t tid, std::vector<CallFrame>& callsFrames, int offset, bool first)
114 {
115 #ifdef HIPERF_DEBUG_TIME
116     const auto startTime = steady_clock::now();
117 #endif
118     // Symbolic the Call Stack
119     HLOGV("total %zu frames", callsFrames.size());
120 
121     perf_callchain_context perfCallchainContext = PERF_CONTEXT_MAX;
122     for (auto callFrameIt = callsFrames.begin() + offset; callFrameIt != callsFrames.end(); ++callFrameIt) {
123         auto &callFrame = callFrameIt.operator*();
124         if (callFrame.ip_ >= PERF_CONTEXT_MAX) {
125             // dont care, this is not issue.
126             HLOGV("%s", UpdatePerfContext(callFrame.ip_, perfCallchainContext).c_str());
127             continue;
128         }
129         auto symbol = GetSymbol(callFrame.ip_, pid, tid,
130             perfCallchainContext);
131         if (symbol.isValid()) {
132             MakeCallFrame(symbol, callFrame);
133         } else {
134 #ifdef TRY_UNWIND_TWICE
135             if (first) {
136                 if (failedIPs_.find(callFrame.ip_) == failedIPs_.end()) {
137                     return false;
138                 } else {
139                     callsFrames.erase(callFrameIt, callsFrames.end());
140                     return true;
141                 }
142             } else {
143                 failedIPs_.insert(callFrame.ip_);
144                 callsFrames.erase(callFrameIt, callsFrames.end());
145                 return true;
146             }
147 #else
148             callsFrames.erase(callFrameIt, callsFrames.end());
149             return true;
150 #endif
151         }
152         int index = callFrameIt - callsFrames.begin();
153         HLOGV(" (%u)unwind symbol: %*s%s", index, index, "", callFrame.ToSymbolString().c_str());
154     }
155 #ifdef HIPERF_DEBUG_TIME
156     auto usedTime = duration_cast<microseconds>(steady_clock::now() - startTime);
157     if (usedTime.count() != 0) {
158         HLOGV("cost %0.3f ms to symbolic ", usedTime.count() / MS_DUARTION);
159     }
160     symbolicRecordTimes_ += usedTime;
161 #endif
162     return true;
163 }
164 
UpdateMaps(pid_t pid,pid_t tid)165 void VirtualRuntime::UpdateMaps(pid_t pid, pid_t tid)
166 {
167     auto &thread = UpdateThread(pid, tid);
168     if (thread.ParseMap(processMemMaps_, true)) {
169         HILOG_INFO(LOG_CORE, "voluntarily update maps succeed");
170     } else {
171         HILOG_INFO(LOG_CORE, "voluntarily update maps ignore");
172     }
173 }
174 
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)175 bool VirtualRuntime::UnwindStack(std::vector<u64> regs,
176                                  const u8* stack_addr,
177                                  int stack_size,
178                                  pid_t pid,
179                                  pid_t tid,
180                                  std::vector<CallFrame>& callsFrames,
181                                  size_t maxStackLevel)
182 {
183 #ifdef HIPERF_DEBUG_TIME
184     const auto startTime = steady_clock::now();
185 #endif
186     // if we have userstack ?
187     int offset = 0;
188     auto &thread = UpdateThread(pid, tid);
189     if (stack_size > 0) {
190         callstack_.UnwindCallStack(thread, &regs[0], regs.size(), stack_addr, stack_size, callsFrames, maxStackLevel);
191         if (callsFrames.size() <= FILTER_STACK_DEPTH) {
192             callsFrames.clear();
193             return false;
194         }
195         // Do not symbolize the first two frame, cause the two frame implement by tool itself
196         offset = FILTER_STACK_DEPTH;
197 #ifdef HIPERF_DEBUG_TIME
198         unwindCallStackTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
199 #endif
200     }
201 #ifdef HIPERF_DEBUG_TIME
202     unwindFromRecordTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
203 #endif
204     if (!GetSymbolName(pid, tid, callsFrames, offset, true)) {
205 #ifdef TRY_UNWIND_TWICE
206         HLOGD("clear and unwind one more time");
207         if (!thread.ParseMap(processMemMaps_, true)) {
208             GetSymbolName(pid, tid, callsFrames, offset, false);
209             return false;
210         }
211         if (stack_size > 0) {
212             callsFrames.clear();
213             callstack_.UnwindCallStack(thread, &regs[0], regs.size(), stack_addr,
214                 stack_size, callsFrames, maxStackLevel);
215         }
216         if (callsFrames.size() <= FILTER_STACK_DEPTH) {
217             callsFrames.clear();
218             return false;
219         }
220         if (!GetSymbolName(pid, tid, callsFrames, offset, false)) {
221             return false;
222         }
223 #endif
224     }
225     return true;
226 }
227 
IsSymbolExist(std::string fileName)228 bool VirtualRuntime::IsSymbolExist(std::string fileName)
229 {
230     for (auto &symbolsFile : symbolsFiles_) {
231         if (symbolsFile->filePath_ == fileName) {
232             HLOGV("already have '%s'", fileName.c_str());
233             return true;
234         }
235     }
236     return false;
237 }
238 
239 
UpdateSymbols(std::string fileName)240 void VirtualRuntime::UpdateSymbols(std::string fileName)
241 {
242     HLOGD("try to find symbols for file: %s", fileName.c_str());
243 #ifdef HIPERF_DEBUG_TIME
244     const auto startTime = steady_clock::now();
245 #endif
246     for (auto &symbolsFile : symbolsFiles_) {
247         if (symbolsFile->filePath_ == fileName) {
248             HLOGV("already have '%s'", fileName.c_str());
249             return;
250         }
251     }
252     // found it by name
253     auto symbolsFile = SymbolsFile::CreateSymbolsFile(fileName);
254 
255     // set sybol path If it exists
256     if (symbolsPaths_.size() > 0) {
257         symbolsFile->setSymbolsFilePath(symbolsPaths_); // also load from search path
258     }
259     if (loadSymboleWhenNeeded_) {
260         // load it when we need it
261         symbolsFiles_.insert(std::move(symbolsFile));
262     } else if (symbolsFile->LoadSymbols()) {
263         symbolsFiles_.insert(std::move(symbolsFile));
264     } else {
265         HLOGW("symbols file for '%s' not found.", fileName.c_str());
266     }
267 #ifdef HIPERF_DEBUG_TIME
268     auto usedTime = duration_cast<microseconds>(steady_clock::now() - startTime);
269     if (usedTime.count() != 0) {
270         HLOGV("cost %0.3f ms to load '%s'", usedTime.count() / MS_DUARTION, fileName.c_str());
271     }
272     updateSymbolsTimes_ += usedTime;
273 #endif
274 }
275 
GetKernelSymbol(uint64_t ip,const std::vector<MemMapItem> & memMaps,const VirtualThread & thread)276 const Symbol VirtualRuntime::GetKernelSymbol(uint64_t ip, const std::vector<MemMapItem> &memMaps,
277                                              const VirtualThread &thread)
278 {
279     Symbol vaddrSymbol(ip, thread.name_);
280     for (auto &map : memMaps) {
281         if (ip > map.begin_ && ip < map.end_) {
282             HLOGM("found addr 0x%" PRIx64 " in kernel map 0x%" PRIx64 " - 0x%" PRIx64 " from %s",
283                   ip, map.begin_, map.end_, map.name_.c_str());
284             vaddrSymbol.module_ = map.name_;
285             // found symbols by file name
286             for (auto &symbolsFile : symbolsFiles_) {
287                 if (symbolsFile->filePath_ == map.name_) {
288                     vaddrSymbol.fileVaddr_ =
289                         symbolsFile->GetVaddrInSymbols(ip, map.begin_, map.pageoffset_);
290                     HLOGV("found symbol vaddr 0x%" PRIx64 " for runtime vaddr 0x%" PRIx64
291                           " at '%s'",
292                           vaddrSymbol.fileVaddr_, ip, map.name_.c_str());
293                     if (!symbolsFile->SymbolsLoaded()) {
294                         symbolsFile->LoadSymbols();
295                     }
296                     Symbol foundSymbols = symbolsFile->GetSymbolWithVaddr(vaddrSymbol.fileVaddr_);
297                     foundSymbols.taskVaddr_ = ip;
298                     if (!foundSymbols.isValid()) {
299                         HLOGW("addr 0x%" PRIx64 " vaddr  0x%" PRIx64 " NOT found in symbol file %s",
300                               ip, vaddrSymbol.fileVaddr_, map.name_.c_str());
301                         return vaddrSymbol;
302                     } else {
303                         return foundSymbols;
304                     }
305                 }
306             }
307             HLOGW("addr 0x%" PRIx64 " in map but NOT found the symbol file %s", ip,
308                   map.name_.c_str());
309         } else {
310             HLOGM("addr 0x%" PRIx64 " not in map 0x%" PRIx64 " - 0x%" PRIx64 " from %s", ip,
311                   map.begin_, map.end_, map.name_.c_str());
312         }
313     }
314     return vaddrSymbol;
315 }
316 
GetUserSymbol(uint64_t ip,const VirtualThread & thread)317 const Symbol VirtualRuntime::GetUserSymbol(uint64_t ip, const VirtualThread &thread)
318 {
319     Symbol vaddrSymbol(ip, thread.name_);
320     const MemMapItem *mmap = thread.FindMapByAddr(ip);
321     if (mmap != nullptr) {
322         SymbolsFile *symbolsFile = thread.FindSymbolsFileByMap(*mmap);
323         if (symbolsFile != nullptr) {
324             vaddrSymbol.fileVaddr_ =
325                 symbolsFile->GetVaddrInSymbols(ip, mmap->begin_, mmap->pageoffset_);
326             vaddrSymbol.module_ = mmap->nameHold_;
327             vaddrSymbol.symbolName_ = vaddrSymbol.Name();
328             HLOGV("found symbol vaddr 0x%" PRIx64 " for runtime vaddr 0x%" PRIx64 " at '%s'",
329                   vaddrSymbol.fileVaddr_, ip, mmap->name_.c_str());
330             if (!symbolsFile->SymbolsLoaded()) {
331                 symbolsFile->LoadSymbols();
332             }
333             Symbol foundSymbols = symbolsFile->GetSymbolWithVaddr(vaddrSymbol.fileVaddr_);
334             foundSymbols.taskVaddr_ = ip;
335             foundSymbols.symbolName_ = foundSymbols.Name();
336             if (!foundSymbols.isValid()) {
337                 HLOGW("addr 0x%" PRIx64 " vaddr  0x%" PRIx64 " NOT found in symbol file %s", ip,
338                       vaddrSymbol.fileVaddr_, mmap->name_.c_str());
339                 return vaddrSymbol;
340             } else {
341                 return foundSymbols;
342             }
343         } else {
344             HLOGW("addr 0x%" PRIx64 " in map but NOT found the symbol file %s", ip,
345                   mmap->name_.c_str());
346         }
347     } else {
348         HLOGW("ReportVaddrMapMiss");
349 #ifdef HIPERF_DEBUG
350         thread.ReportVaddrMapMiss(ip);
351 #endif
352     }
353     return vaddrSymbol;
354 }
355 
GetSymbolCache(uint64_t ip,pid_t pid,pid_t tid,Symbol & symbol,const perf_callchain_context & context)356 bool VirtualRuntime::GetSymbolCache(uint64_t ip, pid_t pid, pid_t tid, Symbol &symbol,
357                                     const perf_callchain_context &context)
358 {
359     if (context != PERF_CONTEXT_USER and kernelSymbolCache_.count(ip)) {
360         if (kernelSymbolCache_.find(ip) == kernelSymbolCache_.end()) {
361             return false;
362         }
363         Symbol &foundSymbol = kernelSymbolCache_[ip];
364         foundSymbol.hit_++;
365         HLOGM("hit kernel cache 0x%" PRIx64 " %d", ip, foundSymbol.hit_);
366         symbol = foundSymbol;
367         return true;
368     } else if (threadSymbolCache_[tid].count(ip)) {
369         Symbol &foundSymbol = threadSymbolCache_[tid][ip];
370         foundSymbol.hit_++;
371         HLOGM("hit user cache 0x%" PRIx64 " %d", ip, foundSymbol.hit_);
372         symbol = foundSymbol;
373         return true;
374     } else {
375         HLOGM("cache miss k %zu u %zu", kernelSymbolCache_.size(), threadSymbolCache_[tid].size());
376     }
377     return false;
378 }
379 
UpdateSymbolCache(uint64_t ip,Symbol & symbol,HashList<uint64_t,Symbol> & cache)380 void VirtualRuntime::UpdateSymbolCache(uint64_t ip, Symbol &symbol,
381     HashList<uint64_t, Symbol> &cache)
382 {
383     // review change to LRU for memmory
384     HLOG_ASSERT_MESSAGE(cache.count(ip) == 0, "already have cached ip 0x%" PRIx64 "", ip);
385     cache[ip] = symbol;
386 }
387 
GetSymbol(uint64_t ip,pid_t pid,pid_t tid,const perf_callchain_context & context)388 const Symbol VirtualRuntime::GetSymbol(uint64_t ip, pid_t pid, pid_t tid,
389                                        const perf_callchain_context &context)
390 {
391     HLOGM("try find tid %u ip 0x%" PRIx64 " in %zu symbolsFiles ", tid, ip, symbolsFiles_.size());
392     Symbol symbol;
393     if (!threadSymbolCache_.count(tid)) {
394         threadSymbolCache_[tid].reserve(THREAD_SYMBOL_CACHE_LIMIT);
395     }
396     if (GetSymbolCache(ip, pid, tid, symbol, context)) {
397         return symbol;
398     }
399     if (context == PERF_CONTEXT_USER or (context == PERF_CONTEXT_MAX and !symbol.isValid())) {
400         // check userspace memmap
401         symbol = GetUserSymbol(ip, GetThread(pid, tid));
402         if (symbol.isValid()) {
403             HLOGM("GetUserSymbol valid tid = %d ip = 0x%" PRIx64 "", tid, ip);
404             threadSymbolCache_[tid][ip] = symbol;
405         } else {
406             HLOGM("GetUserSymbol invalid!");
407         }
408     }
409 
410     return symbol;
411 }
412 
SetSymbolsPaths(const std::vector<std::string> & symbolsPaths)413 bool VirtualRuntime::SetSymbolsPaths(const std::vector<std::string> &symbolsPaths)
414 {
415     std::unique_ptr<SymbolsFile> symbolsFile = SymbolsFile::CreateSymbolsFile(SYMBOL_UNKNOW_FILE);
416     // we need check if the path is accessable
417     bool accessable = symbolsFile->setSymbolsFilePath(symbolsPaths);
418     if (accessable) {
419         symbolsPaths_ = symbolsPaths;
420     } else {
421         if (!symbolsPaths.empty()) {
422             printf("some symbols path unable access\n");
423         }
424     }
425     return accessable;
426 }
427 } // namespace NativeDaemon
428 } // namespace Developtools
429 } // namespace OHOS