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