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