• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #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 HiPerf {
35 // we unable to access 'swapper' from /proc/0/
VirtualRuntime(bool onDevice)36 VirtualRuntime::VirtualRuntime(bool onDevice)
37 {
38     UpdateThread(0, 0, "swapper");
39 }
40 
ReadThreadName(pid_t tid)41 std::string VirtualRuntime::ReadThreadName(pid_t tid)
42 {
43     std::string comm = ReadFileToString(StringPrintf("/proc/%d/comm", tid)).c_str();
44     comm.erase(std::remove(comm.begin(), comm.end(), '\r'), comm.end());
45     comm.erase(std::remove(comm.begin(), comm.end(), '\n'), comm.end());
46     return comm;
47 }
48 
UpdateThread(pid_t pid,pid_t tid,const std::string name)49 VirtualThread &VirtualRuntime::UpdateThread(pid_t pid, pid_t tid, const std::string name)
50 {
51 #ifdef HIPERF_DEBUG_TIME
52     const auto startTime = steady_clock::now();
53 #endif
54     VirtualThread &thread = GetThread(pid, tid);
55     if (!name.empty()) {
56         thread.name_ = name;
57     }
58 #ifdef HIPERF_DEBUG_TIME
59     updateThreadTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
60 #endif
61     return thread;
62 }
63 
CreateThread(pid_t pid,pid_t tid)64 VirtualThread &VirtualRuntime::CreateThread(pid_t pid, pid_t tid)
65 {
66     // make a new one
67     if (pid == tid) {
68         userSpaceThreadMap_.emplace(std::piecewise_construct, std::forward_as_tuple(tid),
69                                     std::forward_as_tuple(pid, symbolsFiles_));
70     } else {
71         // for thread we need give it process info( for same mmap)
72         userSpaceThreadMap_.emplace(
73             std::piecewise_construct, std::forward_as_tuple(tid),
74             std::forward_as_tuple(pid, tid, GetThread(pid, pid), symbolsFiles_));
75     }
76     VirtualThread &thread = userSpaceThreadMap_.at(tid);
77     if (recordCallBack_) {
78         if (pid == tid) {
79 #ifdef HIPERF_DEBUG_TIME
80             const auto startTime = steady_clock::now();
81 #endif
82             thread.ParseMap();
83 #ifdef HIPERF_DEBUG_TIME
84             threadParseMapsTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
85 #endif
86         }
87 #ifdef HIPERF_DEBUG_TIME
88         const auto startCreateMmapTime = steady_clock::now();
89 #endif
90         thread.name_ = ReadThreadName(pid);
91         HLOGD("create a new thread record for %u:%u:%s with %zu dso", pid, tid,
92               thread.name_.c_str(), thread.GetMaps().size());
93         // we need make a PerfRecordComm
94         auto commRecord = std::make_unique<PerfRecordComm>(false, pid, tid, thread.name_);
95         recordCallBack_(std::move(commRecord));
96         // only work for pid
97         if (pid == tid) {
98             for (auto &memMapItem : thread.GetMaps()) {
99                 auto mmapRecord =
100                     std::make_unique<PerfRecordMmap2>(false, thread.pid_, thread.tid_, memMapItem);
101                 HLOGD("make PerfRecordMmap2 %d:%d:%s:%s(0x%" PRIx64 "-0x%" PRIx64 ")@%" PRIx64 " ",
102                       thread.pid_, thread.tid_, thread.name_.c_str(), memMapItem.name_.c_str(),
103                       memMapItem.begin_, memMapItem.end_, memMapItem.pageoffset_);
104                 recordCallBack_(std::move(mmapRecord));
105                 UpdateSymbols(memMapItem.name_);
106             }
107         }
108         HLOGV("thread created");
109 #ifdef HIPERF_DEBUG_TIME
110         threadCreateMmapTimes_ +=
111             duration_cast<microseconds>(steady_clock::now() - startCreateMmapTime);
112 #endif
113     }
114     return thread;
115 }
116 
GetThread(pid_t pid,pid_t tid)117 VirtualThread &VirtualRuntime::GetThread(pid_t pid, pid_t tid)
118 {
119     if (userSpaceThreadMap_.find(pid) == userSpaceThreadMap_.end()) {
120         // no pid found
121         // create process first
122         CreateThread(pid, pid);
123     }
124 
125     auto it = userSpaceThreadMap_.find(tid);
126     if (it == userSpaceThreadMap_.end()) {
127         // we also need thread
128         return CreateThread(pid, tid);
129     } else {
130         return it->second;
131     }
132 }
133 
UpdateThreadMaps(pid_t pid,pid_t tid,const std::string filename,uint64_t begin,uint64_t len,uint64_t offset)134 void VirtualRuntime::UpdateThreadMaps(pid_t pid, pid_t tid, const std::string filename,
135                                       uint64_t begin, uint64_t len, uint64_t offset)
136 {
137     VirtualThread &thread = GetThread(pid, tid);
138     thread.CreateMapItem(filename, begin, len, offset);
139 }
140 
UpdateKernelModulesSpaceMaps()141 void VirtualRuntime::UpdateKernelModulesSpaceMaps()
142 {
143     // found the kernel modules
144     std::vector<MemMapItem> koMaps;
145     std::ifstream ifs("/proc/modules", std::ifstream::in);
146     if (!ifs.is_open()) {
147         perror("kernel modules read failed(/proc/modules)\n");
148         return;
149     }
150     std::string line;
151     while (getline(ifs, line)) {
152         uint64_t addr = 0;
153         uint64_t size = 0;
154         char module[line.size()];
155         /*
156         name       size  load     map
157         hi_mipi_rx 53248 0 - Live 0xbf109000 (O)
158         hi3516cv500_hdmi 237568 0 - Live 0xbf0bb000 (O)
159         hifb 143360 0 - Live 0xbf089000 (O)
160         hi3516cv500_vo_dev 98304 0 - Live 0xbf070000 (O)
161         hi3516cv500_tde 110592 0 - Live 0xbf04a000 (O)
162         hi3516cv500_sys 36864 0 - Live 0xbf03a000 (O)
163         hi3516cv500_base 20480 5
164         hi_mipi_rx,hi3516cv500_hdmi,hifb,hi3516cv500_vo_dev,hi3516cv500_tde,hi3516cv500_sys,
165         hi3516cv500_base,sys_config,hi_proc,hi_irq,Live 0xbf000000 (O)
166         */
167         int ret = sscanf_s(line.c_str(), "%s%" PRIu64 "%*u%*s%*s 0x%" PRIx64 "", module,
168                            sizeof(module), &size, &addr, sizeof(addr));
169         constexpr int numSlices {3};
170         if (ret == numSlices) {
171             MemMapItem &map = koMaps.emplace_back(addr, addr + size, 0, std::string(module));
172             HLOGV("add ko map %s", map.ToString().c_str());
173         } else {
174             HLOGE("unknown line %d: '%s'", ret, line.c_str());
175         }
176     }
177 
178     if (std::all_of(koMaps.begin(), koMaps.end(),
179                     [](const MemMapItem &item) { return item.begin_ == 0; })) {
180         koMaps.clear();
181         HLOGW("no addr found in /proc/modules. remove all the ko");
182     }
183     if (recordCallBack_) {
184         for (MemMapItem &map : koMaps) {
185             auto record = std::make_unique<PerfRecordMmap>(true, 0, 0, map.begin_,
186                                                            map.end_ - map.begin_, 0, map.name_);
187             recordCallBack_(std::move(record));
188         }
189     }
190     std::move(koMaps.begin(), koMaps.end(), std::back_inserter(kernelSpaceMemMaps_));
191 }
192 
UpdateKernelSpaceMaps()193 void VirtualRuntime::UpdateKernelSpaceMaps()
194 {
195     // add kernel first
196     auto &map = kernelSpaceMemMaps_.emplace_back(0, std::numeric_limits<uint64_t>::max(), 0,
197                                                  KERNEL_MMAP_NAME);
198     if (recordCallBack_) {
199         auto record = std::make_unique<PerfRecordMmap>(true, 0, 0, map.begin_,
200                                                        map.end_ - map.begin_, 0, map.name_);
201         recordCallBack_(std::move(record));
202     }
203 }
204 
UpdateKernelModulesSymbols()205 void VirtualRuntime::UpdateKernelModulesSymbols()
206 {
207     HLOGD("load ko symbol and build id");
208     for (MemMapItem &map : kernelSpaceMemMaps_) {
209         if (map.name_ == KERNEL_MMAP_NAME) {
210             continue;
211         }
212         auto kernelModuleFile =
213             SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_MODULE_FILE, map.name_);
214         if (symbolsPaths_.size() > 0) {
215             kernelModuleFile->setSymbolsFilePath(symbolsPaths_); // also load from search path
216         }
217         kernelModuleFile->LoadSymbols();
218         symbolsFiles_.emplace_back(std::move(kernelModuleFile));
219     }
220 }
221 
UpdateKernelSymbols()222 void VirtualRuntime::UpdateKernelSymbols()
223 {
224     HLOGD("create a kernel mmap record");
225     // found kernel source
226     auto kernelFile = SymbolsFile::CreateSymbolsFile(KERNEL_MMAP_NAME);
227     // set sybol path If it exists
228     if (symbolsPaths_.size() > 0) {
229         kernelFile->setSymbolsFilePath(symbolsPaths_); // also load from search path
230     }
231     if (kernelFile->LoadSymbols()) {
232         auto record = std::make_unique<PerfRecordMmap>(
233             true, 0, 0, kernelFile->textExecVaddr_, kernelFile->textExecVaddrRange_,
234             kernelFile->textExecVaddrFileOffset_, KERNEL_MMAP_NAME);
235 
236         if (recordCallBack_) {
237             recordCallBack_(std::move(record));
238         }
239         symbolsFiles_.emplace_back(std::move(kernelFile));
240     } else {
241         HLOGW("kernel symbol not found.\n");
242     }
243 }
244 
UpdatekernelMap(uint64_t begin,uint64_t end,uint64_t offset,std::string filename)245 void VirtualRuntime::UpdatekernelMap(uint64_t begin, uint64_t end, uint64_t offset,
246                                      std::string filename)
247 {
248     HLOGV("update kernel map name:'%s' 0x%" PRIx64 " - 0x%" PRIx64 "@0x%08" PRIx64 "",
249           filename.c_str(), begin, end, offset);
250 
251     HLOG_ASSERT(!filename.empty());
252     auto it = find(kernelSpaceMemMaps_.begin(), kernelSpaceMemMaps_.end(), filename);
253     if (it == kernelSpaceMemMaps_.end()) {
254         kernelSpaceMemMaps_.emplace_back(begin, end, offset, filename);
255     } else {
256         it->begin_ = begin;
257         it->end_ = end;
258         it->pageoffset_ = offset;
259         it->name_ = filename;
260     }
261 }
262 
UpdateFromRecord(PerfEventRecord & record)263 void VirtualRuntime::UpdateFromRecord(PerfEventRecord &record)
264 {
265 #ifdef HIPERF_DEBUG_TIME
266     const auto startTime = steady_clock::now();
267 #endif
268     if (record.GetType() == PERF_RECORD_SAMPLE) {
269         auto recordSample = static_cast<PerfRecordSample *>(&record);
270         UpdateFromRecord(*recordSample);
271 #ifdef HIPERF_DEBUG_TIME
272         prcessSampleRecordTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
273 #endif
274     } else if (record.GetType() == PERF_RECORD_MMAP) {
275         auto recordMmap = static_cast<PerfRecordMmap *>(&record);
276         UpdateFromRecord(*recordMmap);
277 #ifdef HIPERF_DEBUG_TIME
278         prcessMmapRecordTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
279 #endif
280     } else if (record.GetType() == PERF_RECORD_MMAP2) {
281         auto recordMmap2 = static_cast<PerfRecordMmap2 *>(&record);
282         UpdateFromRecord(*recordMmap2);
283 #ifdef HIPERF_DEBUG_TIME
284         prcessMmap2RecordTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
285 #endif
286     } else if (record.GetType() == PERF_RECORD_COMM) {
287         auto recordCommp = static_cast<PerfRecordComm *>(&record);
288         UpdateFromRecord(*recordCommp);
289 #ifdef HIPERF_DEBUG_TIME
290         prcessCommRecordTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
291 #endif
292     } else {
293         HLOGW("skip record type %d", record.GetType());
294     }
295 }
296 
MakeCallFrame(Symbol & symbol,CallFrame & callFrame)297 void VirtualRuntime::MakeCallFrame(Symbol &symbol, CallFrame &callFrame)
298 {
299     callFrame.vaddrInFile_ = symbol.funcVaddr_;
300     callFrame.symbolName_ = symbol.Name();
301     callFrame.symbolIndex_ = symbol.index_;
302     callFrame.filePath_ = symbol.module_.empty() ? symbol.comm_ : symbol.module_;
303     HLOG_ASSERT_MESSAGE(!callFrame.symbolName_.empty(), "%s", symbol.ToDebugString().c_str());
304 }
305 
SymbolicCallFrame(PerfRecordSample & recordSample,uint64_t ip,perf_callchain_context context)306 void VirtualRuntime::SymbolicCallFrame(PerfRecordSample &recordSample, uint64_t ip,
307                                        perf_callchain_context context)
308 {
309     auto symbol = GetSymbol(ip, recordSample.data_.pid, recordSample.data_.tid, context);
310     MakeCallFrame(symbol, recordSample.callFrames_.emplace_back(ip, 0));
311     HLOGV(" (%zu)unwind symbol: %*s%s", recordSample.callFrames_.size(),
312           static_cast<int>(recordSample.callFrames_.size()), "",
313           recordSample.callFrames_.back().ToSymbolString().c_str());
314 }
315 
SymbolicRecord(PerfRecordSample & recordSample)316 void VirtualRuntime::SymbolicRecord(PerfRecordSample &recordSample)
317 {
318 #ifdef HIPERF_DEBUG_TIME
319     const auto startTime = steady_clock::now();
320 #endif
321     // Symbolic the Call Stack
322     recordSample.callFrames_.clear();
323     perf_callchain_context context = PERF_CONTEXT_MAX;
324     if (recordSample.data_.nr == 0) {
325         SymbolicCallFrame(recordSample, recordSample.data_.ip, PERF_CONTEXT_MAX);
326     }
327     for (u64 i = 0; i < recordSample.data_.nr; i++) {
328         uint64_t ip = recordSample.data_.ips[i];
329         if (ip >= PERF_CONTEXT_MAX) {
330             std::string contextName = UpdatePerfContext(ip, context);
331             HLOGV("context switch to %s", contextName.c_str());
332             continue;
333         } else if (ip < BAD_IP_ADDRESS) {
334             // ip 0 or 1 or less than 0
335             continue;
336         }
337         SymbolicCallFrame(recordSample, ip, context);
338     }
339 #ifdef HIPERF_DEBUG_TIME
340     auto usedTime = duration_cast<microseconds>(steady_clock::now() - startTime);
341     if (usedTime.count() != 0) {
342         HLOGV("cost %0.3f ms to symbolic ", usedTime.count() / MS_DUARTION);
343     }
344     symbolicRecordTimes_ += usedTime;
345 #endif
346 }
347 
UnwindFromRecord(PerfRecordSample & recordSample)348 void VirtualRuntime::UnwindFromRecord(PerfRecordSample &recordSample)
349 {
350 #ifdef HIPERF_DEBUG_TIME
351     const auto startTime = steady_clock::now();
352 #endif
353     HLOGV("unwind record (time:%llu)", recordSample.data_.time);
354     // if we have userstack ?
355     if (recordSample.data_.stack_size > 0) {
356         auto &thread = UpdateThread(recordSample.data_.pid, recordSample.data_.tid);
357         callstack_.UnwindCallStack(thread, recordSample.data_.user_abi == PERF_SAMPLE_REGS_ABI_32,
358                                    recordSample.data_.user_regs, recordSample.data_.reg_nr,
359                                    recordSample.data_.stack_data, recordSample.data_.dyn_size,
360                                    recordSample.callFrames_);
361 #ifdef HIPERF_DEBUG_TIME
362         unwindCallStackTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
363 #endif
364         size_t oldSize = recordSample.callFrames_.size();
365         HLOGV("unwind %zu", recordSample.callFrames_.size());
366         callstack_.ExpandCallStack(thread.tid_, recordSample.callFrames_, callstackMergeLevel_);
367         HLOGV("expand %zu (+%zu)", recordSample.callFrames_.size(),
368               recordSample.callFrames_.size() - oldSize);
369 
370         recordSample.ReplaceWithCallStack(oldSize);
371     }
372 
373 #ifdef HIPERF_DEBUG_TIME
374     unwindFromRecordTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
375 #endif
376 
377     // we will not do this in record mode
378     if (recordCallBack_ == nullptr) {
379         // find the symbols , reabuild frame info
380         SymbolicRecord(recordSample);
381     }
382 }
383 
UpdateFromRecord(PerfRecordSample & recordSample)384 void VirtualRuntime::UpdateFromRecord(PerfRecordSample &recordSample)
385 {
386     UpdateThread(recordSample.data_.pid, recordSample.data_.tid);
387     // unwind
388     if (disableUnwind_) {
389         return;
390     } else {
391         UnwindFromRecord(recordSample);
392     }
393 }
394 
UpdateFromRecord(PerfRecordMmap & recordMmap)395 void VirtualRuntime::UpdateFromRecord(PerfRecordMmap &recordMmap)
396 {
397     HLOGV("  MMAP: size %d pid %u tid %u", recordMmap.header.size, recordMmap.data_.pid,
398           recordMmap.data_.tid);
399     HLOGV("  MMAP: %s dso '%s' (0x%llx-0x%llx)@0x%llx", recordMmap.inKernel() ? "kernel" : "user",
400           recordMmap.data_.filename, recordMmap.data_.addr,
401           recordMmap.data_.addr + recordMmap.data_.len, recordMmap.data_.pgoff);
402     // kernel mmap
403     // don't overwrite the vailed mmap , so we also check the recordMmap.data_.len
404     if (recordMmap.inKernel()) {
405         UpdatekernelMap(recordMmap.data_.addr, recordMmap.data_.addr + recordMmap.data_.len,
406                         recordMmap.data_.pgoff, recordMmap.data_.filename);
407     } else {
408         UpdateThreadMaps(recordMmap.data_.pid, recordMmap.data_.tid, recordMmap.data_.filename,
409                          recordMmap.data_.addr, recordMmap.data_.len, recordMmap.data_.pgoff);
410         UpdateSymbols(recordMmap.data_.filename);
411     }
412 }
413 
UpdateFromRecord(PerfRecordMmap2 & recordMmap2)414 void VirtualRuntime::UpdateFromRecord(PerfRecordMmap2 &recordMmap2)
415 {
416     if (!VirtualThread::IsLegalFileName(recordMmap2.data_.filename)) {
417         return;
418     }
419     HLOGV("  MMAP2: size %d pid %u tid %u", recordMmap2.header.size, recordMmap2.data_.pid,
420           recordMmap2.data_.tid);
421     HLOGV("  MMAP2: %s dso '%s' (0x%llx-0x%llx)@0x%llx", recordMmap2.inKernel() ? "kernel" : "user",
422           recordMmap2.data_.filename, recordMmap2.data_.addr,
423           recordMmap2.data_.addr + recordMmap2.data_.len, recordMmap2.data_.pgoff);
424     UpdateThreadMaps(recordMmap2.data_.pid, recordMmap2.data_.tid, recordMmap2.data_.filename,
425                      recordMmap2.data_.addr, recordMmap2.data_.len, recordMmap2.data_.pgoff);
426     UpdateSymbols(recordMmap2.data_.filename);
427 }
428 
UpdateFromRecord(PerfRecordComm & recordComm)429 void VirtualRuntime::UpdateFromRecord(PerfRecordComm &recordComm)
430 {
431     recordComm.DumpLog(__FUNCTION__);
432     UpdateThread(recordComm.data_.pid, recordComm.data_.tid, recordComm.data_.comm);
433 }
434 
SetRecordMode(RecordCallBack recordCallBack)435 void VirtualRuntime::SetRecordMode(RecordCallBack recordCallBack)
436 {
437     recordCallBack_ = recordCallBack;
438 }
439 
UpdateSymbols(std::string fileName)440 void VirtualRuntime::UpdateSymbols(std::string fileName)
441 {
442     HLOGD("try to find symbols for file: %s", fileName.c_str());
443 #ifdef HIPERF_DEBUG_TIME
444     const auto startTime = steady_clock::now();
445 #endif
446     for (auto &symbolsFile : symbolsFiles_) {
447         if (symbolsFile->filePath_ == fileName) {
448             HLOGV("already have '%s'", fileName.c_str());
449             return;
450         }
451     }
452     // found it by name
453     auto symbolsFile = SymbolsFile::CreateSymbolsFile(fileName);
454 
455     // set sybol path If it exists
456     if (symbolsPaths_.size() > 0) {
457         symbolsFile->setSymbolsFilePath(symbolsPaths_); // also load from search path
458     }
459     if (loadSymboleWhenNeeded_) {
460         // load it when we need it
461         symbolsFiles_.emplace_back(std::move(symbolsFile));
462     } else if (symbolsFile->LoadSymbols()) {
463         symbolsFiles_.emplace_back(std::move(symbolsFile));
464     } else {
465         HLOGW("symbols file for '%s' not found.", fileName.c_str());
466     }
467 #ifdef HIPERF_DEBUG_TIME
468     auto usedTime = duration_cast<microseconds>(steady_clock::now() - startTime);
469     if (usedTime.count() != 0) {
470         HLOGV("cost %0.3f ms to load '%s'", usedTime.count() / MS_DUARTION, fileName.c_str());
471     }
472     updateSymbolsTimes_ += usedTime;
473 #endif
474 }
475 
GetKernelSymbol(uint64_t ip,const std::vector<MemMapItem> & memMaps,const VirtualThread & thread)476 const Symbol VirtualRuntime::GetKernelSymbol(uint64_t ip, const std::vector<MemMapItem> &memMaps,
477                                              const VirtualThread &thread)
478 {
479     Symbol vaddrSymbol(ip, thread.name_);
480     for (auto &map : memMaps) {
481         if (ip > map.begin_ && ip < map.end_) {
482             HLOGM("found addr 0x%" PRIx64 " in kernel map 0x%" PRIx64 " - 0x%" PRIx64 " from %s",
483                   ip, map.begin_, map.end_, map.name_.c_str());
484             vaddrSymbol.module_ = map.name_;
485             // found symbols by file name
486             for (auto &symbolsFile : symbolsFiles_) {
487                 if (symbolsFile->filePath_ == map.name_) {
488                     vaddrSymbol.fileVaddr_ =
489                         symbolsFile->GetVaddrInSymbols(ip, map.begin_, map.pageoffset_);
490                     HLOGV("found symbol vaddr 0x%" PRIx64 " for runtime vaddr 0x%" PRIx64
491                           " at '%s'",
492                           vaddrSymbol.fileVaddr_, ip, map.name_.c_str());
493                     if (!symbolsFile->SymbolsLoaded()) {
494                         symbolsFile->LoadSymbols();
495                     }
496                     Symbol foundSymbols = symbolsFile->GetSymbolWithVaddr(vaddrSymbol.fileVaddr_);
497                     foundSymbols.taskVaddr_ = ip;
498                     if (!foundSymbols.isValid()) {
499                         HLOGW("addr 0x%" PRIx64 " vaddr  0x%" PRIx64 " NOT found in symbol file %s",
500                               ip, vaddrSymbol.fileVaddr_, map.name_.c_str());
501                         return vaddrSymbol;
502                     } else {
503                         return foundSymbols;
504                     }
505                 }
506             }
507             HLOGW("addr 0x%" PRIx64 " in map but NOT found the symbol file %s", ip,
508                   map.name_.c_str());
509         } else {
510             HLOGM("addr 0x%" PRIx64 " not in map 0x%" PRIx64 " - 0x%" PRIx64 " from %s", ip,
511                   map.begin_, map.end_, map.name_.c_str());
512         }
513     }
514     return vaddrSymbol;
515 }
516 
GetUserSymbol(uint64_t ip,const VirtualThread & thread)517 const Symbol VirtualRuntime::GetUserSymbol(uint64_t ip, const VirtualThread &thread)
518 {
519     Symbol vaddrSymbol(ip, thread.name_);
520     const MemMapItem *mmap = thread.FindMapByAddr(ip);
521     if (mmap != nullptr) {
522         SymbolsFile *symbolsFile = thread.FindSymbolsFileByMap(*mmap);
523         if (symbolsFile != nullptr) {
524             vaddrSymbol.fileVaddr_ =
525                 symbolsFile->GetVaddrInSymbols(ip, mmap->begin_, mmap->pageoffset_);
526             vaddrSymbol.module_ = mmap->nameHold_;
527             HLOGV("found symbol vaddr 0x%" PRIx64 " for runtime vaddr 0x%" PRIx64 " at '%s'",
528                   vaddrSymbol.fileVaddr_, ip, mmap->name_.c_str());
529             if (!symbolsFile->SymbolsLoaded()) {
530                 symbolsFile->LoadSymbols();
531             }
532             Symbol foundSymbols = symbolsFile->GetSymbolWithVaddr(vaddrSymbol.fileVaddr_);
533             foundSymbols.taskVaddr_ = ip;
534             if (!foundSymbols.isValid()) {
535                 HLOGW("addr 0x%" PRIx64 " vaddr  0x%" PRIx64 " NOT found in symbol file %s", ip,
536                       vaddrSymbol.fileVaddr_, mmap->name_.c_str());
537                 return vaddrSymbol;
538             } else {
539                 return foundSymbols;
540             }
541         } else {
542             HLOGW("addr 0x%" PRIx64 " in map but NOT found the symbol file %s", ip,
543                   mmap->name_.c_str());
544         }
545     } else {
546 #ifdef HIPERF_DEBUG
547         thread.ReportVaddrMapMiss(ip);
548 #endif
549     }
550     return vaddrSymbol;
551 }
552 
GetSymbolCache(uint64_t ip,pid_t pid,pid_t tid,Symbol & symbol,const perf_callchain_context & context)553 bool VirtualRuntime::GetSymbolCache(uint64_t ip, pid_t pid, pid_t tid, Symbol &symbol,
554                                     const perf_callchain_context &context)
555 {
556     if (context != PERF_CONTEXT_USER and kernelSymbolCache_.count(ip)) {
557         if (kernelSymbolCache_.find(ip) == kernelSymbolCache_.end()) {
558             return false;
559         }
560         Symbol &foundSymbol = kernelSymbolCache_[ip];
561         foundSymbol.hit_++;
562         HLOGV("hit kernel cache 0x%" PRIx64 " %d", ip, foundSymbol.hit_);
563         symbol = foundSymbol;
564         return true;
565     } else if (threadSymbolCache_[tid].count(ip) != 0) {
566         Symbol &foundSymbol = threadSymbolCache_[tid][ip];
567         foundSymbol.hit_++;
568         HLOGV("hit user cache 0x%" PRIx64 " %d %s", ip, foundSymbol.hit_,
569               foundSymbol.ToDebugString().c_str());
570         symbol = foundSymbol;
571         return true;
572     } else {
573         HLOGM("cache miss k %zu u %zu", kernelSymbolCache_.size(), threadSymbolCache_[tid].size());
574     }
575     return false;
576 }
577 
GetSymbol(uint64_t ip,pid_t pid,pid_t tid,const perf_callchain_context & context)578 const Symbol VirtualRuntime::GetSymbol(uint64_t ip, pid_t pid, pid_t tid,
579                                        const perf_callchain_context &context)
580 {
581     HLOGV("try find tid %u ip 0x%" PRIx64 " in %zu symbolsFiles ", tid, ip, symbolsFiles_.size());
582     Symbol symbol;
583     if (threadSymbolCache_.find(tid) == threadSymbolCache_.end()) {
584         threadSymbolCache_[tid].reserve(THREAD_SYMBOL_CACHE_LIMIT);
585     }
586     if (GetSymbolCache(ip, pid, tid, symbol, context)) {
587         return symbol;
588     }
589     if (context == PERF_CONTEXT_USER or (context == PERF_CONTEXT_MAX and !symbol.isValid())) {
590         // check userspace memmap
591         symbol = GetUserSymbol(ip, GetThread(pid, tid));
592         threadSymbolCache_[tid][ip] = symbol;
593         HLOGV("cache ip  0x%" PRIx64 " to %s", ip,
594               threadSymbolCache_[tid][ip].ToDebugString().c_str());
595     }
596 
597     if (context == PERF_CONTEXT_KERNEL or (context == PERF_CONTEXT_MAX and !symbol.isValid())) {
598         // check kernelspace
599         HLOGM("try found addr in kernelspace %zu maps ", kernelSpaceMemMaps_.size());
600         symbol = GetKernelSymbol(ip, kernelSpaceMemMaps_, GetThread(pid, tid));
601         HLOGM("add addr to kernel cache 0x%" PRIx64 " cache size %zu ", ip,
602               kernelSymbolCache_.size());
603         kernelSymbolCache_[ip] = symbol;
604     }
605     return symbol;
606 }
607 
SetSymbolsPaths(const std::vector<std::string> & symbolsPaths)608 bool VirtualRuntime::SetSymbolsPaths(const std::vector<std::string> &symbolsPaths)
609 {
610     std::unique_ptr<SymbolsFile> symbolsFile = SymbolsFile::CreateSymbolsFile(SYMBOL_UNKNOW_FILE);
611     // we need check if the path is accessible
612     bool accessible = symbolsFile->setSymbolsFilePath(symbolsPaths);
613     if (accessible) {
614         symbolsPaths_ = symbolsPaths;
615     } else {
616         if (!symbolsPaths.empty()) {
617             printf("some symbols path unable access\n");
618         }
619     }
620     return accessible;
621 }
622 
UpdateFromPerfData(const std::vector<SymbolFileStruct> & symbolFileStructs)623 void VirtualRuntime::UpdateFromPerfData(const std::vector<SymbolFileStruct> &symbolFileStructs)
624 {
625     // review: if we need move to some other place ?
626     HLOG_ASSERT_MESSAGE(symbolsFiles_.size() == 0, " symbolsFiles_ size is %zu",
627                         symbolsFiles_.size());
628     for (const auto &symbolFileStruct : symbolFileStructs) {
629         HLOGV("symbolFileStruct.filePath_:'%s'", symbolFileStruct.filePath_.c_str());
630         HLOGV("symbolFileStruct.buildId_:'%s'", symbolFileStruct.buildId_.c_str());
631         HLOGV("process symbols file:'%s':'%s'", symbolFileStruct.filePath_.c_str(),
632               symbolFileStruct.buildId_.c_str());
633 
634         // load from symbolFileStruct (perf.data)
635         std::unique_ptr<SymbolsFile> symbolsFile =
636             SymbolsFile::LoadSymbolsFromSaved(symbolFileStruct);
637 
638         // reaload from sybol path If it exists
639         if (symbolsPaths_.size() > 0) {
640             HLOGV("try again with symbolsPaths setup");
641             symbolsFile->setSymbolsFilePath(symbolsPaths_); // also load from search path
642             // use give us path ,we must reload it.
643             symbolsFile->LoadSymbols();
644         }
645         symbolsFiles_.emplace_back(std::move(symbolsFile));
646     }
647 }
648 
649 /*
650    ARM functions
651        The table below lists the symbols exported by the vDSO.
652 
653        symbol                 version
654        ────────────────────────────────────────────────────────────
655        __vdso_gettimeofday    LINUX_2.6 (exported since Linux 4.1)
656        __vdso_clock_gettime   LINUX_2.6 (exported since Linux 4.1)
657 
658        Additionally, the ARM port has a code page full of utility
659        functions.  Since it's just a raw page of code, there is no ELF
660        information for doing symbol lookups or versioning.  It does
661        provide support for different versions though.
662 
663        For information on this code page, it's best to refer to the
664        kernel documentation as it's extremely detailed and covers
665        everything you need to know:
666        Documentation/arm/kernel_user_helpers.txt.
667 
668    aarch64 functions
669        The table below lists the symbols exported by the vDSO.
670 
671        symbol                   version
672        ──────────────────────────────────────
673        __kernel_rt_sigreturn    LINUX_2.6.39
674        __kernel_gettimeofday    LINUX_2.6.39
675        __kernel_clock_gettime   LINUX_2.6.39
676        __kernel_clock_getres    LINUX_2.6.39
677 */
LoadVdso()678 void VirtualRuntime::LoadVdso()
679 {
680 #if !is_mingw
681     VirtualThread myThread(getpid(), symbolsFiles_);
682     myThread.ParseMap();
683     for (const auto &map : myThread.GetMaps()) {
684         if (map.name_ == MMAP_VDSO_NAME) {
685             std::string memory(map.end_ - map.begin_, '\0');
686             std::copy(reinterpret_cast<char *>((map.begin_)), reinterpret_cast<char *>((map.end_)),
687                       &memory[0]);
688             std::string tempPath("/data/local/tmp/");
689             std::string tempFileName = tempPath + MMAP_VDSO_NAME;
690             if (!WriteStringToFile(tempFileName, memory)) {
691                 printf("vdso temp file create fail at %s\n", tempFileName.c_str());
692             } else {
693                 HLOGD("vdso temp file create at %s:%zu", tempFileName.c_str(), memory.size());
694                 auto symbolsFile = SymbolsFile::CreateSymbolsFile(MMAP_VDSO_NAME);
695                 symbolsFile->setSymbolsFilePath(tempPath); // also load from search path
696                 symbolsFiles_.emplace_back(std::move(symbolsFile));
697                 return;
698             }
699         }
700     }
701     HLOGD("no vdso found");
702 #endif
703 }
704 } // namespace HiPerf
705 } // namespace Developtools
706 } // namespace OHOS