• 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 "dfx_map.h"
28 #include "register.h"
29 #include "symbols_file.h"
30 #include "utilities.h"
31 
32 using namespace std::chrono;
33 namespace OHOS {
34 namespace Developtools {
35 namespace HiPerf {
36 namespace {
37 // if ip is 0 , 1 both not useful
38 const uint64_t BAD_IP_ADDRESS = 2;
39 }
40 // we unable to access 'swapper' from /proc/0/
VirtualRuntime(bool onDevice)41 VirtualRuntime::VirtualRuntime(bool onDevice)
42 {
43     UpdateThread(0, 0, "swapper");
44 }
45 
~VirtualRuntime()46 VirtualRuntime::~VirtualRuntime()
47 {
48     if (savedCmdLines_.is_open()) {
49         savedCmdLines_.close();
50     }
51 }
52 
ReadFromSavedCmdLines(pid_t tid)53 std::string VirtualRuntime::ReadFromSavedCmdLines(pid_t tid)
54 {
55     if (!savedCmdLines_.is_open()) {
56         savedCmdLines_.open(SAVED_CMDLINES, std::ios::in);
57     }
58     if (!savedCmdLines_.is_open()) {
59         return EMPTY_STRING;
60     }
61     savedCmdLines_.seekg(0, std::ios::beg);
62     std::string line;
63     std::string threadid = std::to_string(tid);
64     while (getline(savedCmdLines_, line)) {
65         if (line.find(threadid) != std::string::npos) {
66             constexpr size_t sizeLimit {2};
67             std::vector<std::string> linesToken = StringSplit(line, " ");
68             if (linesToken.size() < sizeLimit) {
69                 return EMPTY_STRING;
70             }
71             if (threadid != linesToken[0]) {
72                 continue;
73             }
74             return linesToken[1];
75         }
76     }
77     return EMPTY_STRING;
78 }
79 
ReadThreadName(pid_t tid,bool isThread)80 std::string VirtualRuntime::ReadThreadName(pid_t tid, bool isThread)
81 {
82     std::string comm = "";
83     if (tid == SYSMGR_PID) {
84         comm = SYSMGR_NAME;
85     } else if (tid == devhostPid_) {
86         comm = DEVHOST_FILE_NAME;
87     } else if (isThread) {
88         comm = ReadFileToString(StringPrintf("/proc/%d/comm", tid)).c_str();
89     } else {
90         comm = ReadFileToString(StringPrintf("/proc/%d/cmdline", tid)).c_str();
91     }
92     if (comm == EMPTY_STRING) {
93         comm = ReadFromSavedCmdLines(tid);
94     }
95     comm.erase(std::remove(comm.begin(), comm.end(), '\r'), comm.end());
96     comm.erase(std::remove(comm.begin(), comm.end(), '\n'), comm.end());
97     return comm;
98 }
99 
UpdateThread(pid_t pid,pid_t tid,const std::string name)100 VirtualThread &VirtualRuntime::UpdateThread(pid_t pid, pid_t tid, const std::string name)
101 {
102 #ifdef HIPERF_DEBUG_TIME
103     const auto startTime = steady_clock::now();
104 #endif
105     VirtualThread &thread = GetThread(pid, tid, name);
106     if (!name.empty()) {
107         thread.name_ = name;
108     }
109 #ifdef HIPERF_DEBUG_TIME
110     updateThreadTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
111 #endif
112     return thread;
113 }
114 
CreateThread(pid_t pid,pid_t tid,const std::string name)115 VirtualThread &VirtualRuntime::CreateThread(pid_t pid, pid_t tid, const std::string name)
116 {
117     // make a new one
118     if (pid == tid) {
119         userSpaceThreadMap_.emplace(std::piecewise_construct, std::forward_as_tuple(tid),
120                                     std::forward_as_tuple(pid, symbolsFiles_));
121     } else {
122         // for thread we need give it process info( for same mmap)
123         userSpaceThreadMap_.emplace(
124             std::piecewise_construct, std::forward_as_tuple(tid),
125             std::forward_as_tuple(pid, tid, GetThread(pid, pid), symbolsFiles_));
126     }
127     VirtualThread &thread = userSpaceThreadMap_.at(tid);
128     if (recordCallBack_) {
129         if (pid == tid && !IsKernelThread(pid)) {
130 #ifdef HIPERF_DEBUG_TIME
131             const auto startTime = steady_clock::now();
132 #endif
133             thread.ParseMap();
134 #ifdef HIPERF_DEBUG_TIME
135             threadParseMapsTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
136 #endif
137         }
138 #ifdef HIPERF_DEBUG_TIME
139         const auto startCreateMmapTime = steady_clock::now();
140 #endif
141         thread.name_ = name;
142         if (thread.name_.empty()) {
143             thread.name_ = ReadThreadName(tid, pid != tid);
144         }
145         HLOGD("create a new thread record for %u:%u:%s with %zu dso", pid, tid,
146               thread.name_.c_str(), thread.GetMaps().size());
147         // we need make a PerfRecordComm
148         auto commRecord = std::make_unique<PerfRecordComm>(IsKernelThread(pid), pid, tid, thread.name_);
149         recordCallBack_(std::move(commRecord));
150         // only work for pid
151         if (pid == tid) {
152             if (isHM_) {
153                 thread.FixHMBundleMap();
154             }
155             std::shared_ptr<DfxMap> prevMap = nullptr;
156             for (auto &map : thread.GetMaps()) {
157                 // so in hap is load before start perf record
158                 // dynamic load library should be treat in the same way
159                 bool updateNormalSymbol = true;
160                 if (map->name.find(".hap") != std::string::npos && (map->prots & PROT_EXEC)) {
161                     map->prevMap = prevMap;
162                     updateNormalSymbol = !UpdateHapSymbols(map);
163                     HLOGD("UpdateHapSymbols");
164                 }
165                 auto mmapRecord =
166                     std::make_unique<PerfRecordMmap2>(false, thread.pid_, thread.tid_, map);
167                 HLOGD("make PerfRecordMmap2 %d:%d:%s:%s(0x%" PRIx64 "-0x%" PRIx64 ")@%" PRIx64 " ",
168                       thread.pid_, thread.tid_, thread.name_.c_str(), map->name.c_str(),
169                       map->begin, map->end, map->offset);
170                 recordCallBack_(std::move(mmapRecord));
171                 if (updateNormalSymbol) {
172                     UpdateSymbols(map->name);
173                 }
174                 prevMap = map;
175             }
176         }
177         HLOGV("thread created");
178 #ifdef HIPERF_DEBUG_TIME
179         threadCreateMmapTimes_ +=
180             duration_cast<microseconds>(steady_clock::now() - startCreateMmapTime);
181 #endif
182     }
183     return thread;
184 }
185 
UpdateHapSymbols(std::shared_ptr<DfxMap> map)186 bool VirtualRuntime::UpdateHapSymbols(std::shared_ptr<DfxMap> map)
187 {
188     // found it by name
189     auto symbolsFile = SymbolsFile::CreateSymbolsFile(map->name);
190     if (symbolsFile == nullptr) {
191         HLOGV("Failed to load CreateSymbolsFile for exec section in hap(%s)", map->name.c_str());
192         return false;
193     }
194     // update maps name if load debuginfo successfully
195     if (!symbolsFile->LoadDebugInfo(map)) {
196         HLOGV("Failed to load debuginfo for exec section in hap(%s)", map->name.c_str());
197         return false;
198     }
199 
200     if (!loadSymboleWhenNeeded_) { // todo misspelling
201         symbolsFile->LoadSymbols(map);
202     }
203     symbolsFiles_.emplace_back(std::move(symbolsFile));
204     return true;
205 }
206 
GetThread(pid_t pid,pid_t tid,const std::string name)207 VirtualThread &VirtualRuntime::GetThread(pid_t pid, pid_t tid, const std::string name)
208 {
209     if (userSpaceThreadMap_.find(pid) == userSpaceThreadMap_.end()) {
210         // no pid found
211         // create process first
212         CreateThread(pid, pid);
213     }
214 
215     auto it = userSpaceThreadMap_.find(tid);
216     if (it == userSpaceThreadMap_.end()) {
217         // we also need thread
218         return CreateThread(pid, tid, name);
219     } else {
220         return it->second;
221     }
222 }
223 
UpdateThreadMaps(pid_t pid,pid_t tid,const std::string filename,uint64_t begin,uint64_t len,uint64_t offset)224 void VirtualRuntime::UpdateThreadMaps(pid_t pid, pid_t tid, const std::string filename,
225                                       uint64_t begin, uint64_t len, uint64_t offset)
226 {
227     VirtualThread &thread = GetThread(pid, tid);
228     thread.CreateMapItem(filename, begin, len, offset);
229 }
230 
UpdateKernelModulesSpaceMaps()231 void VirtualRuntime::UpdateKernelModulesSpaceMaps()
232 {
233     // found the kernel modules
234     std::vector<DfxMap> koMaps;
235     std::ifstream ifs("/proc/modules", std::ifstream::in);
236     if (!ifs.is_open()) {
237         perror("kernel modules read failed(/proc/modules)\n");
238         return;
239     }
240     std::string line;
241     while (getline(ifs, line)) {
242         uint64_t addr = 0;
243         uint64_t size = 0;
244         char module[line.size()];
245         /*
246         name       size  load     map
247         hi_mipi_rx 53248 0 - Live 0xbf109000 (O)
248         hi3516cv500_hdmi 237568 0 - Live 0xbf0bb000 (O)
249         hifb 143360 0 - Live 0xbf089000 (O)
250         hi3516cv500_vo_dev 98304 0 - Live 0xbf070000 (O)
251         hi3516cv500_tde 110592 0 - Live 0xbf04a000 (O)
252         hi3516cv500_sys 36864 0 - Live 0xbf03a000 (O)
253         hi3516cv500_base 20480 5
254         hi_mipi_rx,hi3516cv500_hdmi,hifb,hi3516cv500_vo_dev,hi3516cv500_tde,hi3516cv500_sys,
255         hi3516cv500_base,sys_config,hi_proc,hi_irq,Live 0xbf000000 (O)
256         */
257         int ret = sscanf_s(line.c_str(), "%s%" PRIu64 "%*u%*s%*s 0x%" PRIx64 "", module,
258                            sizeof(module), &size, &addr, sizeof(addr));
259         constexpr int numSlices {3};
260         if (ret == numSlices) {
261             auto &map = koMaps.emplace_back(addr, addr + size, 0, "", std::string(module));
262             HLOGV("add ko map %s", map.ToString().c_str());
263         } else {
264             HLOGE("unknown line %d: '%s'", ret, line.c_str());
265         }
266     }
267 
268     if (std::all_of(koMaps.begin(), koMaps.end(),
269                     [](const DfxMap &item) { return item.begin == 0; })) {
270         koMaps.clear();
271         HLOGW("no addr found in /proc/modules. remove all the ko");
272     }
273     if (recordCallBack_) {
274         for (const auto &map : koMaps) {
275             auto record = std::make_unique<PerfRecordMmap>(true, 0, 0, map.begin,
276                                                            map.end - map.begin, 0, map.name);
277             recordCallBack_(std::move(record));
278         }
279     }
280     std::move(koMaps.begin(), koMaps.end(), std::back_inserter(kernelSpaceMemMaps_));
281 }
282 
UpdateKernelSpaceMaps()283 void VirtualRuntime::UpdateKernelSpaceMaps()
284 {
285     // add kernel first
286     auto &map = kernelSpaceMemMaps_.emplace_back(0, std::numeric_limits<uint64_t>::max(), 0, "", KERNEL_MMAP_NAME);
287     if (recordCallBack_) {
288         auto record = std::make_unique<PerfRecordMmap>(true, 0, 0, map.begin,
289                                                        map.end - map.begin, 0, map.name);
290         recordCallBack_(std::move(record));
291     }
292 }
293 
UpdateKernelModulesSymbols()294 void VirtualRuntime::UpdateKernelModulesSymbols()
295 {
296     HLOGD("load ko symbol and build id");
297     for (auto &map : kernelSpaceMemMaps_) {
298         if (map.name == KERNEL_MMAP_NAME) {
299             continue;
300         }
301         auto kernelModuleFile = SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_MODULE_FILE, map.name);
302         if (symbolsPaths_.size() > 0) {
303             kernelModuleFile->setSymbolsFilePath(symbolsPaths_); // also load from search path
304         }
305         kernelModuleFile->LoadSymbols();
306         symbolsFiles_.emplace_back(std::move(kernelModuleFile));
307     }
308 }
309 
UpdateKernelSymbols()310 void VirtualRuntime::UpdateKernelSymbols()
311 {
312     HLOGD("create a kernel mmap record");
313     // found kernel source
314     auto kernelFile = SymbolsFile::CreateSymbolsFile(KERNEL_MMAP_NAME);
315     // set symbol path If it exists
316     if (symbolsPaths_.size() > 0) {
317         kernelFile->setSymbolsFilePath(symbolsPaths_); // also load from search path
318     }
319     if (kernelFile->LoadSymbols()) {
320         auto record = std::make_unique<PerfRecordMmap>(
321             true, 0, 0, kernelFile->textExecVaddr_, kernelFile->textExecVaddrRange_,
322             kernelFile->textExecVaddrFileOffset_, KERNEL_MMAP_NAME);
323 
324         if (recordCallBack_) {
325             recordCallBack_(std::move(record));
326         }
327         symbolsFiles_.emplace_back(std::move(kernelFile));
328     } else {
329         HLOGW("kernel symbol not found.\n");
330     }
331 }
332 
UpdatekernelMap(uint64_t begin,uint64_t end,uint64_t offset,std::string filename)333 void VirtualRuntime::UpdatekernelMap(uint64_t begin, uint64_t end, uint64_t offset,
334                                      std::string filename)
335 {
336     HLOGV("update kernel map name:'%s' 0x%" PRIx64 " - 0x%" PRIx64 "@0x%08" PRIx64 "",
337           filename.c_str(), begin, end, offset);
338 
339     HLOG_ASSERT(!filename.empty());
340     auto it = find(kernelSpaceMemMaps_.begin(), kernelSpaceMemMaps_.end(), filename);
341     if (it == kernelSpaceMemMaps_.end()) {
342         kernelSpaceMemMaps_.emplace_back(begin, end, offset, "", filename);
343     } else {
344         it->begin = begin;
345         it->end = end;
346         it->offset = offset;
347         it->name = filename;
348     }
349 }
350 
DedupFromRecord(PerfRecordSample * recordSample)351 void VirtualRuntime::DedupFromRecord(PerfRecordSample *recordSample)
352 {
353     u64 nr = recordSample->data_.nr;
354     if (nr == 0) {
355         collectSymbolCallBack_(recordSample);
356         return;
357     }
358 
359     u32 pid = recordSample->data_.pid;
360     u64 *ips = recordSample->data_.ips;
361     StackId stackId;
362     stackId.value = 0;
363     auto entry = processStackMap_.find(pid);
364     std::shared_ptr<UniqueStackTable> table = nullptr;
365     if (entry != processStackMap_.end()) {
366         table = entry->second;
367     } else {
368         table = std::make_shared<UniqueStackTable>(pid);
369         processStackMap_[pid] = table;
370     }
371 
372     while (table->PutIpsInTable(&stackId, ips, nr) == 0) {
373         // try expand hashtable if collison can not resolved
374         if (!table->Resize()) {
375             HLOGW("Hashtable size limit, ip compress failed!");
376             collectSymbolCallBack_(recordSample);
377             return;
378         }
379     }
380 
381     // callstack dedup success
382     recordSample->stackId_.value = stackId.value;
383     recordSample->header.size -= (sizeof(u64) * nr - sizeof(stackId));
384     recordSample->data_.nr = 0;
385     recordSample->data_.ips = nullptr;
386     recordSample->removeStack_ = true;
387 }
388 
CollectDedupSymbol(kSymbolsHits & kernelSymbolsHits,uSymbolsHits & userSymbolsHits)389 void VirtualRuntime::CollectDedupSymbol(kSymbolsHits &kernelSymbolsHits,
390                                         uSymbolsHits &userSymbolsHits)
391 {
392     Node *node = nullptr;
393     Node *head = nullptr;
394     u32 pid;
395     for (const auto &tableEntry : processStackMap_) {
396         const auto &table = tableEntry.second;
397         pid = table->GetPid();
398         head = table->GetHeadNode();
399         const auto &idxes = table->GetUsedIndexes();
400         for (const auto idx : idxes) {
401             node = head + idx;
402             if (node->value != 0) {
403                 if (node->section.inKernel) {
404                     uint64_t ip = node->section.ip | KERNEL_PREFIX;
405                     if (ip == PERF_CONTEXT_KERNEL || ip == PERF_CONTEXT_USER) {
406                         continue;
407                     }
408                     kernelSymbolsHits.insert(ip);
409                 } else {
410                     userSymbolsHits[pid].insert(node->section.ip);
411                 }
412             } else {
413                 HLOGD("node value error 0x%x", idx);
414             }
415         }
416     }
417 }
418 
UpdateFromRecord(PerfEventRecord & record)419 void VirtualRuntime::UpdateFromRecord(PerfEventRecord &record)
420 {
421 #ifdef HIPERF_DEBUG_TIME
422     const auto startTime = steady_clock::now();
423 #endif
424     if (record.GetType() == PERF_RECORD_SAMPLE) {
425         auto recordSample = static_cast<PerfRecordSample *>(&record);
426         UpdateFromRecord(*recordSample);
427 #ifdef HIPERF_DEBUG_TIME
428         processSampleRecordTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
429 #endif
430     } else if (record.GetType() == PERF_RECORD_MMAP) {
431         auto recordMmap = static_cast<PerfRecordMmap *>(&record);
432         UpdateFromRecord(*recordMmap);
433 #ifdef HIPERF_DEBUG_TIME
434         processMmapRecordTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
435 #endif
436     } else if (record.GetType() == PERF_RECORD_MMAP2) {
437         auto recordMmap2 = static_cast<PerfRecordMmap2 *>(&record);
438         UpdateFromRecord(*recordMmap2);
439 #ifdef HIPERF_DEBUG_TIME
440         processMmap2RecordTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
441 #endif
442     } else if (record.GetType() == PERF_RECORD_COMM) {
443         auto recordComm = static_cast<PerfRecordComm *>(&record);
444         UpdateFromRecord(*recordComm);
445 #ifdef HIPERF_DEBUG_TIME
446         processCommRecordTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
447 #endif
448     } else {
449         HLOGW("skip record type %d", record.GetType());
450     }
451 }
452 
MakeCallFrame(DfxSymbol & symbol,CallFrame & callFrame)453 void VirtualRuntime::MakeCallFrame(DfxSymbol &symbol, CallFrame &callFrame)
454 {
455     callFrame.vaddrInFile_ = symbol.funcVaddr_;
456     callFrame.offsetToVaddr_ = symbol.offsetToVaddr_;
457     callFrame.symbolFileIndex_ = symbol.symbolFileIndex_;
458     callFrame.symbolName_ = symbol.GetName();
459     callFrame.symbolIndex_ = symbol.index_;
460     callFrame.filePath_ = symbol.module_.empty() ? symbol.comm_ : symbol.module_;
461     HLOG_ASSERT_MESSAGE(!callFrame.symbolName_.empty(), "%s", symbol.ToDebugString().c_str());
462 }
463 
SymbolicCallFrame(PerfRecordSample & recordSample,uint64_t ip,pid_t server_pid,perf_callchain_context context)464 void VirtualRuntime::SymbolicCallFrame(PerfRecordSample &recordSample, uint64_t ip,
465                                        pid_t server_pid, perf_callchain_context context)
466 {
467     pid_t pid = static_cast<pid_t>(recordSample.data_.pid);
468     pid_t tid = static_cast<pid_t>(recordSample.data_.tid);
469     if (server_pid != pid) {
470         pid = tid = server_pid;
471     }
472     auto symbol = GetSymbol(ip, pid, tid, context);
473     MakeCallFrame(symbol, recordSample.callFrames_.emplace_back(ip, 0));
474     HLOGV(" (%zu)unwind symbol: %*s%s", recordSample.callFrames_.size(),
475           static_cast<int>(recordSample.callFrames_.size()), "",
476           recordSample.callFrames_.back().ToSymbolString().c_str());
477 }
478 
RecoverCallStack(PerfRecordSample & recordSample)479 bool VirtualRuntime::RecoverCallStack(PerfRecordSample &recordSample)
480 {
481     auto StackTable = processStackMap_.find(recordSample.data_.pid);
482     if (StackTable == processStackMap_.end()) {
483         HLOGV("not found %" PRIu32 " pid", recordSample.data_.pid);
484         return false;
485     }
486     recordSample.ips_.clear();
487     StackTable->second->GetIpsByStackId(recordSample.stackId_, recordSample.ips_);
488     recordSample.RecoverCallStack();
489     return true;
490 }
491 
SymbolicRecord(PerfRecordSample & recordSample)492 void VirtualRuntime::SymbolicRecord(PerfRecordSample &recordSample)
493 {
494 #ifdef HIPERF_DEBUG_TIME
495     const auto startTime = steady_clock::now();
496 #endif
497     // Symbolic the Call Stack
498     recordSample.callFrames_.clear();
499     perf_callchain_context context = PERF_CONTEXT_MAX;
500     pid_t server_pid;
501     if (recordSample.data_.nr == 0) {
502         server_pid = recordSample.GetServerPidof(0);
503         SymbolicCallFrame(recordSample, recordSample.data_.ip, server_pid, PERF_CONTEXT_MAX);
504     }
505     for (u64 i = 0; i < recordSample.data_.nr; i++) {
506         uint64_t ip = recordSample.data_.ips[i];
507         if (ip >= PERF_CONTEXT_MAX) {
508             std::string contextName = UpdatePerfContext(ip, context);
509             HLOGV("context switch to %s", contextName.c_str());
510             continue;
511         } else if (ip < BAD_IP_ADDRESS) {
512             // ip 0 or 1 or less than 0
513             continue;
514         }
515         server_pid = recordSample.GetServerPidof(i);
516         SymbolicCallFrame(recordSample, ip, server_pid, context);
517     }
518 #ifdef HIPERF_DEBUG_TIME
519     auto usedTime = duration_cast<microseconds>(steady_clock::now() - startTime);
520     if (usedTime.count() != 0) {
521         HLOGV("cost %0.3f ms to symbolic ", usedTime.count() / MS_DURATION);
522     }
523     symbolicRecordTimes_ += usedTime;
524 #endif
525 }
526 
NeedDropKernelCallChain(PerfRecordSample & sample)527 void VirtualRuntime::NeedDropKernelCallChain(PerfRecordSample &sample)
528 {
529     // only do this in record mode.
530     if (recordCallBack_ == nullptr || needkernelCallChain_ ||
531         !sample.inKernel() || sample.data_.nr == 0) {
532         return;
533     }
534 
535     u64 skip = 0;
536     u64 skip_pid = 0;
537     u64 *ips = sample.data_.ips;
538     for (; skip < sample.data_.nr; skip++) {
539         if (ips[skip] == PERF_CONTEXT_KERNEL) {
540             skip_pid++;
541         }
542         if (ips[skip] == PERF_CONTEXT_USER) {
543             break;
544         }
545     }
546     sample.skipKernel_ = skip;
547     sample.data_.nr -= skip;
548     sample.header.size -= sizeof(u64) * skip;
549     if (sample.data_.server_nr > 0) {
550         sample.skipPid_ = skip_pid;
551         sample.data_.server_nr -= skip_pid;
552         sample.header.size -= sizeof(u64) * skip_pid;
553     }
554 }
555 
UnwindFromRecord(PerfRecordSample & recordSample)556 void VirtualRuntime::UnwindFromRecord(PerfRecordSample &recordSample)
557 {
558 #if defined(is_ohos) && is_ohos
559 #ifdef HIPERF_DEBUG_TIME
560     const auto startTime = steady_clock::now();
561 #endif
562     HLOGV("unwind record (time:%llu)", recordSample.data_.time);
563     // if we have userstack ?
564     if (recordSample.data_.stack_size > 0) {
565         pid_t server_pid = recordSample.GetUstackServerPid();
566         pid_t pid = static_cast<pid_t>(recordSample.data_.pid);
567         pid_t tid = static_cast<pid_t>(recordSample.data_.tid);
568         if (server_pid != pid) {
569             pid = tid = server_pid;
570         }
571         auto &thread = UpdateThread(pid, tid);
572         callstack_.UnwindCallStack(thread, recordSample.data_.user_abi == PERF_SAMPLE_REGS_ABI_32,
573                                    recordSample.data_.user_regs, recordSample.data_.reg_nr,
574                                    recordSample.data_.stack_data, recordSample.data_.dyn_size,
575                                    recordSample.callFrames_);
576 #ifdef HIPERF_DEBUG_TIME
577         unwindCallStackTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
578 #endif
579         size_t oldSize = recordSample.callFrames_.size();
580         HLOGV("unwind %zu", recordSample.callFrames_.size());
581         callstack_.ExpandCallStack(thread.tid_, recordSample.callFrames_, callstackMergeLevel_);
582         HLOGV("expand %zu (+%zu)", recordSample.callFrames_.size(),
583               recordSample.callFrames_.size() - oldSize);
584 
585         recordSample.ReplaceWithCallStack(oldSize);
586     }
587 
588 #ifdef HIPERF_DEBUG_TIME
589     unwindFromRecordTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
590 #endif
591 
592     NeedDropKernelCallChain(recordSample);
593     // we will not do this in non record mode.
594     if (dedupStack_ && recordCallBack_ != nullptr) {
595         DedupFromRecord(&recordSample);
596     }
597 #endif
598 
599     // we will not do this in record mode
600     if (recordCallBack_ == nullptr) {
601         if (dedupStack_ && recordSample.stackId_.section.id > 0 && recordSample.data_.nr == 0) {
602             RecoverCallStack(recordSample);
603         }
604         // find the symbols , reabuild frame info
605         SymbolicRecord(recordSample);
606     }
607 }
608 
SetCollectSymbolCallBack(CollectSymbolCallBack collectSymbolCallBack)609 void VirtualRuntime::SetCollectSymbolCallBack(CollectSymbolCallBack collectSymbolCallBack)
610 {
611     collectSymbolCallBack_ = collectSymbolCallBack;
612 }
613 
UpdateFromRecord(PerfRecordSample & recordSample)614 void VirtualRuntime::UpdateFromRecord(PerfRecordSample &recordSample)
615 {
616     UpdateThread(recordSample.data_.pid, recordSample.data_.tid);
617     if (recordSample.data_.server_nr) {
618         // update all server threads
619         for (size_t i = 0; i < recordSample.data_.server_nr; i++) {
620             pid_t pid = static_cast<pid_t>(recordSample.data_.server_pids[i]);
621             UpdateThread(pid, pid);
622         }
623     }
624     // unwind
625     if (disableUnwind_) {
626         return;
627     } else {
628         UnwindFromRecord(recordSample);
629     }
630 }
631 
UpdateFromRecord(PerfRecordMmap & recordMmap)632 void VirtualRuntime::UpdateFromRecord(PerfRecordMmap &recordMmap)
633 {
634     HLOGV("  MMAP: size %d pid %u tid %u", recordMmap.header.size, recordMmap.data_.pid,
635           recordMmap.data_.tid);
636     HLOGV("  MMAP: %s dso '%s' (0x%llx-0x%llx)@0x%llx", recordMmap.inKernel() ? "kernel" : "user",
637           recordMmap.data_.filename, recordMmap.data_.addr,
638           recordMmap.data_.addr + recordMmap.data_.len, recordMmap.data_.pgoff);
639     // kernel mmap
640     // don't overwrite the vailed mmap , so we also check the recordMmap.data_.len
641     if (IsKernelThread(recordMmap.data_.pid)) {
642         UpdateKernelThreadMap(recordMmap.data_.pid, recordMmap.data_.addr,
643                               recordMmap.data_.len, recordMmap.data_.filename);
644     } else if (recordMmap.inKernel()) {
645         UpdatekernelMap(recordMmap.data_.addr, recordMmap.data_.addr + recordMmap.data_.len,
646                         recordMmap.data_.pgoff, recordMmap.data_.filename);
647     } else {
648         NeedAdaptSandboxPath(recordMmap.data_.filename, recordMmap.data_.pid, recordMmap.header.size);
649         FixHMBundleMmap(recordMmap.data_.filename, recordMmap.data_.pid, recordMmap.header.size);
650         UpdateThreadMaps(recordMmap.data_.pid, recordMmap.data_.tid, recordMmap.data_.filename,
651                          recordMmap.data_.addr, recordMmap.data_.len, recordMmap.data_.pgoff);
652         UpdateSymbols(recordMmap.data_.filename);
653     }
654 }
655 
CheckValidSandBoxMmap(PerfRecordMmap2 & recordMmap2)656 bool VirtualRuntime::CheckValidSandBoxMmap(PerfRecordMmap2 &recordMmap2)
657 {
658     static std::shared_ptr<DfxMap> prevMap;
659     if ((recordMmap2.data_.prot & PROT_EXEC) != 0) {
660         // fake first segment, when second segment come.
661         auto symFile = SymbolsFile::CreateSymbolsFile(SYMBOL_ELF_FILE, recordMmap2.data_.filename);
662         if (symFile == nullptr) {
663             HLOGD("CheckValidSandBoxMmap Failed to create symbolFile!");
664             return false;
665         }
666 
667         std::shared_ptr<DfxMap> curMap;
668         if (strstr(recordMmap2.data_.filename, ".hap") != nullptr) {
669             curMap = std::make_shared<DfxMap>(
670                 recordMmap2.data_.addr,
671                 recordMmap2.data_.addr + recordMmap2.data_.len,
672                 recordMmap2.data_.pgoff,
673                 "", // prot
674                 recordMmap2.data_.filename
675             );
676             curMap->prevMap = prevMap;
677         }
678 
679         if (!symFile->LoadDebugInfo(curMap)) {
680             HLOGD("CheckValidSandBoxMmap Failed to load debuginfo!");
681             return false;
682         }
683 
684         if (!loadSymboleWhenNeeded_) {
685             symFile->LoadSymbols(curMap);
686         }
687 
688         if (strstr(recordMmap2.data_.filename, ".hap") == nullptr) {
689             auto elfLoadInfoMap = symFile->GetPtLoads();
690             u64 begin = recordMmap2.data_.addr - elfLoadInfoMap[0].mmapLen;
691             u64 len = elfLoadInfoMap[0].mmapLen;
692             u64 pgoff = elfLoadInfoMap[0].offset & (~(elfLoadInfoMap[0].align - 1));
693             std::unique_ptr<PerfRecordMmap2> mmap2FirstSeg =
694                 std::make_unique<PerfRecordMmap2>(recordMmap2.inKernel(), recordMmap2.data_.pid, recordMmap2.data_.tid,
695                 begin, len, pgoff, 0, 0, 0, PROT_READ, 0, std::string(recordMmap2.data_.filename));
696             UpdateThreadMaps(mmap2FirstSeg->data_.pid, mmap2FirstSeg->data_.tid, mmap2FirstSeg->data_.filename,
697                 mmap2FirstSeg->data_.addr, mmap2FirstSeg->data_.len, mmap2FirstSeg->data_.pgoff);
698             recordCallBack_(std::move(mmap2FirstSeg));
699         } else {
700             auto elfLoadInfoMap = symFile->GetPtLoads();
701             u64 begin = recordMmap2.data_.addr - elfLoadInfoMap[0].mmapLen;
702             u64 len = elfLoadInfoMap[0].mmapLen;
703             u64 pgoff = elfLoadInfoMap[0].offset & (~(elfLoadInfoMap[0].align - 1));
704             std::unique_ptr<PerfRecordMmap2> mmap2FirstSeg =
705                 std::make_unique<PerfRecordMmap2>(recordMmap2.inKernel(), recordMmap2.data_.pid, recordMmap2.data_.tid,
706                 begin, len, pgoff, 0, 0, 0, PROT_READ, 0, curMap->name);
707             UpdateThreadMaps(mmap2FirstSeg->data_.pid, mmap2FirstSeg->data_.tid, curMap->name,
708                 mmap2FirstSeg->data_.addr, mmap2FirstSeg->data_.len, mmap2FirstSeg->data_.pgoff);
709             recordCallBack_(std::move(mmap2FirstSeg));
710 
711             std::unique_ptr<PerfRecordMmap2> mmap2SecondSegment =
712                 std::make_unique<PerfRecordMmap2>(recordMmap2.inKernel(), recordMmap2.data_.pid, recordMmap2.data_.tid,
713                 recordMmap2.data_.addr,
714                 recordMmap2.data_.len,
715                 recordMmap2.data_.pgoff - prevMap->offset, // minus load offset of hap
716                 0, 0, 0, recordMmap2.data_.prot, 0, curMap->name);
717             UpdateThreadMaps(mmap2SecondSegment->data_.pid, mmap2SecondSegment->data_.tid, curMap->name,
718                 mmap2SecondSegment->data_.addr, mmap2SecondSegment->data_.len, mmap2SecondSegment->data_.pgoff);
719             recordCallBack_(std::move(mmap2SecondSegment));
720             recordMmap2.discard_ = true;
721         }
722         symbolsFiles_.emplace_back(std::move(symFile));
723         return true;
724     } else if (recordMmap2.data_.pgoff == 0) {
725         recordMmap2.discard_ = true;
726     }
727 
728     if (strstr(recordMmap2.data_.filename, ".hap") != nullptr) {
729         prevMap = std::make_shared<DfxMap>(
730             recordMmap2.data_.addr,
731             recordMmap2.data_.addr + recordMmap2.data_.len,
732             recordMmap2.data_.pgoff,
733             "", // prot
734             recordMmap2.data_.filename
735         );
736         HLOGD("CheckValidSandBoxMmap Update prev map!");
737     }
738     return !recordMmap2.discard_;
739 }
740 
UpdateFromRecord(PerfRecordMmap2 & recordMmap2)741 void VirtualRuntime::UpdateFromRecord(PerfRecordMmap2 &recordMmap2)
742 {
743     if (!OHOS::HiviewDFX::DfxMaps::IsLegalMapItem(recordMmap2.data_.filename)) {
744         return;
745     }
746 
747     HLOGV("  MMAP2: size %d pid %u tid %u", recordMmap2.header.size, recordMmap2.data_.pid,
748           recordMmap2.data_.tid);
749     HLOGV("  MMAP2: %s dso '%s' (0x%llx-0x%llx)@0x%llx prot:%u", recordMmap2.inKernel() ? "kernel" : "user",
750           recordMmap2.data_.filename, recordMmap2.data_.addr,
751           recordMmap2.data_.addr + recordMmap2.data_.len, recordMmap2.data_.pgoff, recordMmap2.data_.prot);
752 
753     if (recordCallBack_) {
754         if (NeedAdaptSandboxPath(recordMmap2.data_.filename, recordMmap2.data_.pid, recordMmap2.header.size)) {
755             FixHMBundleMmap(recordMmap2.data_.filename, recordMmap2.data_.pid, recordMmap2.header.size);
756             if (!CheckValidSandBoxMmap(recordMmap2)) {
757                 return;
758             }
759         }
760     }
761     UpdateThreadMaps(recordMmap2.data_.pid, recordMmap2.data_.tid, recordMmap2.data_.filename,
762                      recordMmap2.data_.addr, recordMmap2.data_.len, recordMmap2.data_.pgoff);
763     UpdateSymbols(recordMmap2.data_.filename);
764 }
765 
UpdateFromRecord(PerfRecordComm & recordComm)766 void VirtualRuntime::UpdateFromRecord(PerfRecordComm &recordComm)
767 {
768     recordComm.DumpLog(__FUNCTION__);
769     UpdateThread(recordComm.data_.pid, recordComm.data_.tid, recordComm.data_.comm);
770 }
771 
SetRecordMode(RecordCallBack recordCallBack)772 void VirtualRuntime::SetRecordMode(RecordCallBack recordCallBack)
773 {
774     recordCallBack_ = recordCallBack;
775 }
776 
UpdateSymbols(std::string fileName)777 void VirtualRuntime::UpdateSymbols(std::string fileName)
778 {
779     HLOGD("try to find symbols for file: %s", fileName.c_str());
780 #ifdef HIPERF_DEBUG_TIME
781     const auto startTime = steady_clock::now();
782 #endif
783     for (const auto &symbolsFile : symbolsFiles_) {
784         if (symbolsFile->filePath_ == fileName) {
785             HLOGV("already have '%s'", fileName.c_str());
786             return;
787         }
788     }
789     // found it by name
790     auto symbolsFile = SymbolsFile::CreateSymbolsFile(fileName);
791     if (enableDebugInfoSymbolic_ && symbolsFile->symbolFileType_ == SymbolsFileType::SYMBOL_ELF_FILE) {
792         symbolsFile->EnableMiniDebugInfo();
793     }
794     // set symbol path If it exists
795     if (symbolsPaths_.size() > 0) {
796         symbolsFile->setSymbolsFilePath(symbolsPaths_); // also load from search path
797     }
798     if (loadSymboleWhenNeeded_) {
799         // load it when we need it
800         symbolsFiles_.emplace_back(std::move(symbolsFile));
801     } else if (symbolsFile->LoadSymbols()) {
802         symbolsFiles_.emplace_back(std::move(symbolsFile));
803     } else {
804         HLOGW("symbols file for '%s' not found.", fileName.c_str());
805     }
806 #ifdef HIPERF_DEBUG_TIME
807     auto usedTime = duration_cast<microseconds>(steady_clock::now() - startTime);
808     if (usedTime.count() != 0) {
809         HLOGV("cost %0.3f ms to load '%s'", usedTime.count() / MS_DURATION, fileName.c_str());
810     }
811     updateSymbolsTimes_ += usedTime;
812 #endif
813 }
814 
GetKernelSymbol(uint64_t ip,const std::vector<DfxMap> & memMaps,const VirtualThread & thread)815 const DfxSymbol VirtualRuntime::GetKernelSymbol(uint64_t ip, const std::vector<DfxMap> &memMaps,
816                                                 const VirtualThread &thread)
817 {
818     DfxSymbol vaddrSymbol(ip, thread.name_);
819     for (auto &map : memMaps) {
820         if (ip > map.begin && ip < map.end) {
821             HLOGM("found addr 0x%" PRIx64 " in kernel map 0x%" PRIx64 " - 0x%" PRIx64 " from %s",
822                   ip, map.begin, map.end, map.name.c_str());
823             vaddrSymbol.module_ = map.name;
824             // found symbols by file name
825             for (auto &symbolsFile : symbolsFiles_) {
826                 if (symbolsFile->filePath_ == map.name) {
827                     vaddrSymbol.symbolFileIndex_ = symbolsFile->id_;
828                     vaddrSymbol.fileVaddr_ =
829                         symbolsFile->GetVaddrInSymbols(ip, map.begin, map.offset);
830                     perf_callchain_context context = PERF_CONTEXT_KERNEL;
831                     if (GetSymbolCache(ip, vaddrSymbol, context)) {
832                         return vaddrSymbol;
833                     }
834                     HLOGV("found symbol vaddr 0x%" PRIx64 " for runtime vaddr 0x%" PRIx64
835                           " at '%s'",
836                           vaddrSymbol.fileVaddr_, ip, map.name.c_str());
837                     if (!symbolsFile->SymbolsLoaded()) {
838                         symbolsFile->LoadSymbols();
839                     }
840                     DfxSymbol foundSymbols = symbolsFile->GetSymbolWithVaddr(vaddrSymbol.fileVaddr_);
841                     foundSymbols.taskVaddr_ = ip;
842                     if (!foundSymbols.IsValid()) {
843                         HLOGW("addr 0x%" PRIx64 " vaddr  0x%" PRIx64 " NOT found in symbol file %s",
844                               ip, vaddrSymbol.fileVaddr_, map.name.c_str());
845                         return vaddrSymbol;
846                     } else {
847                         return foundSymbols;
848                     }
849                 }
850             }
851             HLOGW("addr 0x%" PRIx64 " in map but NOT found the symbol file %s", ip,
852                   map.name.c_str());
853         } else {
854             HLOGM("addr 0x%" PRIx64 " not in map 0x%" PRIx64 " - 0x%" PRIx64 " from %s", ip,
855                   map.begin, map.end, map.name.c_str());
856         }
857     }
858     return vaddrSymbol;
859 }
860 
GetKernelThreadSymbol(uint64_t ip,const VirtualThread & thread)861 const DfxSymbol VirtualRuntime::GetKernelThreadSymbol(uint64_t ip, const VirtualThread &thread)
862 {
863     DfxSymbol vaddrSymbol(ip, thread.name_);
864     int64_t mapIndex = thread.FindMapIndexByAddr(ip);
865     if (mapIndex < 0) {
866 #ifdef HIPERF_DEBUG
867         thread.ReportVaddrMapMiss(ip);
868 #endif
869         return vaddrSymbol;
870     }
871 
872     auto map = thread.GetMaps()[mapIndex];
873     HLOGM("found addr 0x%" PRIx64 " in kthread map 0x%" PRIx64 " - 0x%" PRIx64 " from %s",
874             ip, map->begin, map->end, map->name.c_str());
875     // found symbols by file name
876     for (auto &symbolsFile : symbolsFiles_) {
877         if (symbolsFile->filePath_ == map->name) {
878             vaddrSymbol.symbolFileIndex_ = symbolsFile->id_;
879             vaddrSymbol.module_ = map->name;
880             vaddrSymbol.fileVaddr_ =
881                 symbolsFile->GetVaddrInSymbols(ip, map->begin, map->offset);
882             perf_callchain_context context = PERF_CONTEXT_MAX;
883             if (GetSymbolCache(ip, vaddrSymbol, context)) {
884                 return vaddrSymbol;
885             }
886             HLOGV("found symbol vaddr 0x%" PRIx64 " for runtime vaddr 0x%" PRIx64 " at '%s'",
887                     vaddrSymbol.fileVaddr_, ip, map->name.c_str());
888             if (!symbolsFile->SymbolsLoaded()) {
889                 symbolsFile->LoadDebugInfo();
890                 symbolsFile->LoadSymbols(map);
891             }
892             auto foundSymbols = symbolsFile->GetSymbolWithVaddr(vaddrSymbol.fileVaddr_);
893             foundSymbols.taskVaddr_ = ip;
894             if (!foundSymbols.IsValid()) {
895                 HLOGW("addr 0x%" PRIx64 " vaddr  0x%" PRIx64 " NOT found in symbol file %s",
896                         ip, vaddrSymbol.fileVaddr_, map->name.c_str());
897                 return vaddrSymbol;
898             } else {
899                 return foundSymbols;
900             }
901         }
902     }
903     HLOGW("addr 0x%" PRIx64 " in map but NOT found the symbol file %s", ip,
904             map->name.c_str());
905     return vaddrSymbol;
906 }
907 
GetUserSymbol(uint64_t ip,const VirtualThread & thread)908 const DfxSymbol VirtualRuntime::GetUserSymbol(uint64_t ip, const VirtualThread &thread)
909 {
910     DfxSymbol vaddrSymbol(ip, thread.name_);
911     int64_t mapIndex = thread.FindMapIndexByAddr(ip);
912     if (mapIndex >= 0) {
913         auto map = thread.GetMaps()[mapIndex];
914         SymbolsFile *symbolsFile = thread.FindSymbolsFileByMap(map);
915         if (symbolsFile != nullptr) {
916             vaddrSymbol.symbolFileIndex_ = symbolsFile->id_;
917             vaddrSymbol.module_ = map->name;
918             vaddrSymbol.fileVaddr_ =
919                 symbolsFile->GetVaddrInSymbols(ip, map->begin, map->offset);
920             perf_callchain_context context = PERF_CONTEXT_USER;
921             if (GetSymbolCache(ip, vaddrSymbol, context)) {
922                 return vaddrSymbol;
923             }
924             HLOGV("found symbol vaddr 0x%" PRIx64 " for runtime vaddr 0x%" PRIx64 " at '%s'",
925                   vaddrSymbol.fileVaddr_, ip, map->name.c_str());
926             if (!symbolsFile->SymbolsLoaded()) {
927                 symbolsFile->LoadSymbols(map);
928             }
929             auto foundSymbols = symbolsFile->GetSymbolWithVaddr(vaddrSymbol.fileVaddr_);
930             foundSymbols.taskVaddr_ = ip;
931             if (!foundSymbols.IsValid()) {
932                 HLOGW("addr 0x%" PRIx64 " vaddr  0x%" PRIx64 " NOT found in symbol file %s", ip,
933                       vaddrSymbol.fileVaddr_, map->name.c_str());
934                 return vaddrSymbol;
935             } else {
936                 return foundSymbols;
937             }
938         } else {
939             HLOGW("addr 0x%" PRIx64 " in map but NOT found the symbol file %s", ip,
940                   map->name.c_str());
941         }
942     } else {
943 #ifdef HIPERF_DEBUG
944         thread.ReportVaddrMapMiss(ip);
945 #endif
946     }
947     return vaddrSymbol;
948 }
949 
GetSymbolCache(uint64_t ip,DfxSymbol & symbol,const perf_callchain_context & context)950 bool VirtualRuntime::GetSymbolCache(uint64_t ip, DfxSymbol &symbol,
951                                     const perf_callchain_context &context)
952 {
953     if (context == PERF_CONTEXT_MAX and kThreadSymbolCache_.count(ip)) {
954         if (kThreadSymbolCache_.find(symbol.fileVaddr_) == kThreadSymbolCache_.end()) {
955             return false;
956         }
957         symbol = kThreadSymbolCache_[symbol.fileVaddr_];
958         symbol.hit_++;
959         HLOGV("hit kernel thread cache 0x%" PRIx64 " %d", ip, symbol.hit_);
960         return true;
961     } else if (context != PERF_CONTEXT_USER and kernelSymbolCache_.count(ip)) {
962         if (kernelSymbolCache_.find(symbol.fileVaddr_) == kernelSymbolCache_.end()) {
963             return false;
964         }
965         symbol = kernelSymbolCache_[symbol.fileVaddr_];
966         symbol.hit_++;
967         HLOGV("hit kernel cache 0x%" PRIx64 " %d", ip, symbol.hit_);
968         return true;
969     } else if (userSymbolCache_.count(symbol.fileVaddr_) != 0) {
970         const DfxSymbol &cachedSymbol = userSymbolCache_[symbol.fileVaddr_];
971         // must be the same file
972         if (cachedSymbol.module_ != symbol.module_) {
973             return false;
974         }
975         symbol = cachedSymbol;
976         symbol.hit_++;
977         HLOGV("hit user cache 0x%" PRIx64 " %d %s", ip, symbol.hit_,
978             symbol.ToDebugString().c_str());
979         return true;
980     } else {
981         HLOGM("cache miss k %zu u %zu kt %zu", kernelSymbolCache_.size(),
982               userSymbolCache_.size(), kThreadSymbolCache_.size());
983     }
984     return false;
985 }
986 
GetSymbol(uint64_t ip,pid_t pid,pid_t tid,const perf_callchain_context & context)987 DfxSymbol VirtualRuntime::GetSymbol(uint64_t ip, pid_t pid, pid_t tid, const perf_callchain_context &context)
988 {
989     HLOGV("try find tid %u ip 0x%" PRIx64 " in %zu symbolsFiles\n", tid, ip, symbolsFiles_.size());
990     DfxSymbol symbol;
991 
992     if (IsKernelThread(pid)) {
993         VirtualThread &kthread = GetThread(pid, tid);
994         HLOGM("try found addr in kernel thread %u with %zu maps", pid,
995               kthread.GetMaps().size());
996         symbol = GetKernelThreadSymbol(ip, kthread);
997         HLOGM("add addr to kernel thread cache 0x%" PRIx64 " cache size %zu", ip,
998               kThreadSymbolCache_.size());
999         kThreadSymbolCache_[symbol.fileVaddr_] = symbol;
1000         if (symbol.IsValid()) {
1001             return symbol;
1002         }
1003     }
1004 
1005     if (context == PERF_CONTEXT_USER or (context == PERF_CONTEXT_MAX and !symbol.IsValid())) {
1006         // check userspace memmap
1007         symbol = GetUserSymbol(ip, GetThread(pid, tid));
1008         if (userSymbolCache_.find(symbol.fileVaddr_) == userSymbolCache_.end()) {
1009             userSymbolCache_.reserve(USER_SYMBOL_CACHE_LIMIT);
1010         }
1011         userSymbolCache_[symbol.fileVaddr_] = symbol;
1012         HLOGV("cache ip 0x%" PRIx64 " to %s", ip,
1013               userSymbolCache_[symbol.fileVaddr_].ToDebugString().c_str());
1014     }
1015 
1016     if (context == PERF_CONTEXT_KERNEL or (context == PERF_CONTEXT_MAX and !symbol.IsValid())) {
1017         // check kernelspace
1018         HLOGM("try found addr in kernelspace %zu maps", kernelSpaceMemMaps_.size());
1019         symbol = GetKernelSymbol(ip, kernelSpaceMemMaps_, GetThread(pid, tid));
1020         HLOGM("add addr to kernel cache 0x%" PRIx64 " cache size %zu", ip,
1021               kernelSymbolCache_.size());
1022         kernelSymbolCache_[symbol.fileVaddr_] = symbol;
1023     }
1024     return symbol;
1025 }
1026 
SetSymbolsPaths(const std::vector<std::string> & symbolsPaths)1027 bool VirtualRuntime::SetSymbolsPaths(const std::vector<std::string> &symbolsPaths)
1028 {
1029     std::unique_ptr<SymbolsFile> symbolsFile = SymbolsFile::CreateSymbolsFile(SYMBOL_UNKNOW_FILE);
1030     // we need check if the path is accessible
1031     bool accessible = symbolsFile->setSymbolsFilePath(symbolsPaths);
1032     if (accessible) {
1033         symbolsPaths_ = symbolsPaths;
1034     } else {
1035         if (!symbolsPaths.empty()) {
1036             printf("some symbols path unable access\n");
1037         }
1038     }
1039     return accessible;
1040 }
1041 
UpdateFromPerfData(const std::vector<SymbolFileStruct> & symbolFileStructs)1042 void VirtualRuntime::UpdateFromPerfData(const std::vector<SymbolFileStruct> &symbolFileStructs)
1043 {
1044     // review: if we need move to some other place ?
1045     HLOG_ASSERT_MESSAGE(symbolsFiles_.size() == 0, " symbolsFiles_ size is %zu",
1046                         symbolsFiles_.size());
1047     for (const auto &symbolFileStruct : symbolFileStructs) {
1048         HLOGV("symbolFileStruct.filePath_:'%s'", symbolFileStruct.filePath_.c_str());
1049         HLOGV("symbolFileStruct.buildId_:'%s'", symbolFileStruct.buildId_.c_str());
1050         HLOGV("process symbols file:'%s':'%s'", symbolFileStruct.filePath_.c_str(),
1051               symbolFileStruct.buildId_.c_str());
1052 
1053         // load from symbolFileStruct (perf.data)
1054         std::unique_ptr<SymbolsFile> symbolsFile = SymbolsFile::LoadSymbolsFromSaved(symbolFileStruct);
1055 
1056         // reaload from sybol path If it exists
1057         if (symbolsPaths_.size() > 0) {
1058             HLOGV("try again with symbolsPaths setup");
1059             symbolsFile->setSymbolsFilePath(symbolsPaths_); // also load from search path
1060             // use give us path ,we must reload it.
1061             symbolsFile->LoadSymbols();
1062         }
1063         symbolsFile->id_ = static_cast<int32_t>(symbolsFiles_.size());
1064         symbolsFiles_.emplace_back(std::move(symbolsFile));
1065     }
1066 }
1067 
ImportUniqueStackNodes(const std::vector<UniStackTableInfo> & uniStackTableInfos)1068 void VirtualRuntime::ImportUniqueStackNodes(const std::vector<UniStackTableInfo>& uniStackTableInfos)
1069 {
1070     for (const UniStackTableInfo& item : uniStackTableInfos) {
1071         auto stackTable = std::make_shared<UniqueStackTable>(item.pid, item.tableSize);
1072         for (const UniStackNode& node : item.nodes) {
1073             stackTable->ImportNode(node.index, node.node);
1074         }
1075         processStackMap_[item.pid] = std::move(stackTable);
1076     }
1077 }
1078 
1079 /*
1080    ARM functions
1081        The table below lists the symbols exported by the vDSO.
1082 
1083        symbol                 version
1084        ────────────────────────────────────────────────────────────
1085        __vdso_gettimeofday    LINUX_2.6 (exported since Linux 4.1)
1086        __vdso_clock_gettime   LINUX_2.6 (exported since Linux 4.1)
1087 
1088        Additionally, the ARM port has a code page full of utility
1089        functions.  Since it's just a raw page of code, there is no ELF
1090        information for doing symbol lookups or versioning.  It does
1091        provide support for different versions though.
1092 
1093        For information on this code page, it's best to refer to the
1094        kernel documentation as it's extremely detailed and covers
1095        everything you need to know:
1096        Documentation/arm/kernel_user_helpers.txt.
1097 
1098    aarch64 functions
1099        The table below lists the symbols exported by the vDSO.
1100 
1101        symbol                   version
1102        ──────────────────────────────────────
1103        __kernel_rt_sigreturn    LINUX_2.6.39
1104        __kernel_gettimeofday    LINUX_2.6.39
1105        __kernel_clock_gettime   LINUX_2.6.39
1106        __kernel_clock_getres    LINUX_2.6.39
1107 */
LoadVdso()1108 void VirtualRuntime::LoadVdso()
1109 {
1110 #if !is_mingw
1111     VirtualThread myThread(getpid(), symbolsFiles_);
1112     myThread.ParseMap();
1113     for (const auto &map : myThread.GetMaps()) {
1114         if (map->name == MMAP_VDSO_NAME) {
1115             std::string memory(map->end - map->begin, '\0');
1116             std::copy(reinterpret_cast<char *>((map->begin)), reinterpret_cast<char *>((map->end)),
1117                       &memory[0]);
1118             std::string tempPath("/data/local/tmp/");
1119             std::string tempFileName = tempPath + MMAP_VDSO_NAME;
1120             if (!WriteStringToFile(tempFileName, memory)) {
1121                 printf("vdso temp file create fail at %s\n", tempFileName.c_str());
1122             } else {
1123                 HLOGD("vdso temp file create at %s:%zu", tempFileName.c_str(), memory.size());
1124                 auto symbolsFile = SymbolsFile::CreateSymbolsFile(MMAP_VDSO_NAME);
1125                 symbolsFile->setSymbolsFilePath(tempPath); // also load from search path
1126                 symbolsFiles_.emplace_back(std::move(symbolsFile));
1127                 return;
1128             }
1129         }
1130     }
1131     HLOGD("no vdso found");
1132 #endif
1133 }
1134 
UpdateServiceSpaceMaps()1135 void VirtualRuntime::UpdateServiceSpaceMaps()
1136 {
1137     VirtualThread &kthread = GetThread(SYSMGR_PID, SYSMGR_PID);
1138     kthread.ParseServiceMap(SYSMGR_FILE_NAME);
1139     if (recordCallBack_) {
1140         for (const auto &map : kthread.GetMaps()) {
1141             auto record =
1142             std::make_unique<PerfRecordMmap>(true, SYSMGR_PID, SYSMGR_PID,
1143                                              map->begin, map->end - map->begin,
1144                                              0, SYSMGR_FILE_NAME);
1145             recordCallBack_(std::move(record));
1146         }
1147     }
1148 }
1149 
UpdateServiceSymbols()1150 void VirtualRuntime::UpdateServiceSymbols()
1151 {
1152     HLOGD("try to update kernel thread symbols for kernel service");
1153     std::string fileName = SYSMGR_FILE_NAME;
1154     auto symbolsFile = SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_THREAD_FILE, fileName);
1155 
1156     HLOGD("add kernel service symbol file: %s", fileName.c_str());
1157     if (symbolsFile->LoadSymbols()) {
1158         symbolsFiles_.emplace_back(std::move(symbolsFile));
1159     } else {
1160         HLOGW("symbols file for '%s' not found.", fileName.c_str());
1161     }
1162 }
1163 
UpdateKernelThreadMap(pid_t pid,uint64_t begin,uint64_t len,std::string filename)1164 void VirtualRuntime::UpdateKernelThreadMap(pid_t pid, uint64_t begin, uint64_t len,
1165                                            std::string filename)
1166 {
1167     HLOGV("update kernel thread map pid %u name:'%s'", pid, filename.c_str());
1168 
1169     VirtualThread &thread = GetThread(pid, pid);
1170     thread.CreateMapItem(filename, begin, len, 0u);
1171 }
1172 
UpdateDevhostSpaceMaps()1173 void VirtualRuntime::UpdateDevhostSpaceMaps()
1174 {
1175     VirtualThread &kthread = GetThread(devhostPid_, devhostPid_);
1176     kthread.ParseDevhostMap(devhostPid_);
1177     if (recordCallBack_) {
1178         for (const auto &map : kthread.GetMaps()) {
1179             auto record =
1180             std::make_unique<PerfRecordMmap>(false, devhostPid_, devhostPid_,
1181                                              map->begin, map->end - map->begin,
1182                                              0, map->name);
1183             recordCallBack_(std::move(record));
1184         }
1185     }
1186 }
1187 
UpdateDevhostSymbols()1188 void VirtualRuntime::UpdateDevhostSymbols()
1189 {
1190     HLOGD("try to update kernel thread symbols for devhost");
1191     auto kallsyms = SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_THREAD_FILE, DEVHOST_FILE_NAME);
1192     // file name of devhost.ko
1193     std::map<std::string_view, std::unique_ptr<SymbolsFile>> koMaps;
1194     koMaps[DEVHOST_FILE_NAME] =
1195         SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_THREAD_FILE, DEVHOST_LINUX_FILE_NAME);
1196 
1197     if (kallsyms->LoadSymbols()) {
1198         for (auto &symbol : kallsyms->GetSymbols()) {
1199             if (koMaps.find(symbol.module_) == koMaps.end()) {
1200                 std::string filename = std::string(symbol.module_);
1201                 // [devhost] to /liblinux/devhost.ko
1202                 filename.erase(filename.begin());
1203                 filename.erase(filename.end() - 1);
1204                 filename = DEVHOST_LINUX_PREFIX + filename + KERNEL_MODULES_EXT_NAME;
1205                 koMaps[symbol.module_] =
1206                     SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_THREAD_FILE, filename);
1207             }
1208             koMaps[symbol.module_]->AddSymbol(std::move(symbol));
1209         }
1210 
1211         HLOGD("devhost loaded %zu symbolfiles", koMaps.size());
1212         for (auto &it : koMaps) {
1213             HLOGD("Load %zu symbols to %s", it.second->GetSymbols().size(),
1214                   it.second->filePath_.c_str());
1215             symbolsFiles_.emplace_back(std::move(it.second));
1216         }
1217     } else {
1218         HLOGW("symbols file for devhost parse failed.");
1219     }
1220 
1221     // update normal symbole files
1222     VirtualThread &kthread = GetThread(devhostPid_, devhostPid_);
1223     for (const auto &map : kthread.GetMaps()) {
1224         UpdateSymbols(map->name);
1225     }
1226 }
1227 
FixHMBundleMmap(char * filename,int pid,u16 & headerSize)1228 void VirtualRuntime::FixHMBundleMmap(char *filename, int pid, u16 &headerSize)
1229 {
1230     if (!isHM_) {
1231         return;
1232     }
1233     // fix bundle path in mmap
1234     std::string newFilename = filename;
1235     VirtualThread &thread = GetThread(pid, pid);
1236     if (NeedAdaptHMBundlePath(newFilename, thread.name_)) {
1237         size_t oldSize = strlen(filename);
1238         (void)memset_s(filename, KILO, '\0', KILO);
1239         if (strncpy_s(filename, KILO, newFilename.c_str(), newFilename.size()) != 0) {
1240             HLOGD("strncpy_s recordMmap2 failed!");
1241         }
1242         headerSize += newFilename.size() - oldSize;
1243     }
1244 }
1245 
SetDevhostPid(pid_t devhost)1246 void VirtualRuntime::SetDevhostPid(pid_t devhost)
1247 {
1248     HLOGD("Set devhost pid: %d", devhost);
1249     devhostPid_ = devhost;
1250 }
1251 
IsKernelThread(pid_t pid)1252 bool VirtualRuntime::IsKernelThread(pid_t pid)
1253 {
1254     if (!isHM_) {
1255         return false;
1256     }
1257     return pid == SYSMGR_PID || pid == devhostPid_;
1258 }
1259 } // namespace HiPerf
1260 } // namespace Developtools
1261 } // namespace OHOS
1262