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