• 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, pid);
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     if (map == nullptr) {
189         return false;
190     }
191     // found it by name
192     auto symbolsFile = SymbolsFile::CreateSymbolsFile(map->name);
193     if (symbolsFile == nullptr) {
194         HLOGV("Failed to load CreateSymbolsFile for exec section in hap(%s)", map->name.c_str());
195         return false;
196     }
197     // update maps name if load debuginfo successfully
198     if (!symbolsFile->LoadDebugInfo(map)) {
199         HLOGV("Failed to load debuginfo for exec section in hap(%s)", map->name.c_str());
200         return false;
201     }
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 (kernelFile->LoadSymbols()) {
327         auto record = std::make_unique<PerfRecordMmap>(
328             true, 0, 0, kernelFile->textExecVaddr_, kernelFile->textExecVaddrRange_,
329             kernelFile->textExecVaddrFileOffset_, KERNEL_MMAP_NAME);
330 
331         if (recordCallBack_) {
332             recordCallBack_(std::move(record));
333         }
334         symbolsFiles_.emplace_back(std::move(kernelFile));
335     } else {
336         HLOGW("kernel symbol not found.\n");
337     }
338 }
339 
UpdatekernelMap(uint64_t begin,uint64_t end,uint64_t offset,std::string filename)340 void VirtualRuntime::UpdatekernelMap(uint64_t begin, uint64_t end, uint64_t offset,
341                                      std::string filename)
342 {
343     HLOGV("update kernel map name:'%s' 0x%" PRIx64 " - 0x%" PRIx64 "@0x%08" PRIx64 "",
344           filename.c_str(), begin, end, offset);
345 
346     HLOG_ASSERT(!filename.empty());
347     auto it = find(kernelSpaceMemMaps_.begin(), kernelSpaceMemMaps_.end(), filename);
348     if (it == kernelSpaceMemMaps_.end()) {
349         kernelSpaceMemMaps_.emplace_back(begin, end, offset, "", filename);
350     } else {
351         it->begin = begin;
352         it->end = end;
353         it->offset = offset;
354         it->name = filename;
355     }
356 }
357 
DedupFromRecord(PerfRecordSample * recordSample)358 void VirtualRuntime::DedupFromRecord(PerfRecordSample *recordSample)
359 {
360     if (recordSample == nullptr) {
361         return;
362     }
363     u64 nr = recordSample->data_.nr;
364     if (nr == 0) {
365         collectSymbolCallBack_(recordSample);
366         return;
367     }
368     u32 pid = recordSample->data_.pid;
369     u64 *ips = recordSample->data_.ips;
370     StackId stackId;
371     stackId.value = 0;
372     auto entry = processStackMap_.find(pid);
373     std::shared_ptr<UniqueStackTable> table = nullptr;
374     if (entry != processStackMap_.end()) {
375         table = entry->second;
376     } else {
377         table = std::make_shared<UniqueStackTable>(pid);
378         processStackMap_[pid] = table;
379     }
380     if (table == nullptr) {
381         return;
382     }
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 {
465         HLOGW("skip record type %d", record.GetType());
466     }
467 }
468 
MakeCallFrame(DfxSymbol & symbol,DfxFrame & callFrame)469 void VirtualRuntime::MakeCallFrame(DfxSymbol &symbol, DfxFrame &callFrame)
470 {
471     callFrame.funcOffset = symbol.funcVaddr_;
472     callFrame.mapOffset = symbol.offsetToVaddr_;
473     callFrame.symbolFileIndex = symbol.symbolFileIndex_;
474     callFrame.funcName = symbol.GetName();
475     if (callFrame.funcName.empty()) {
476         HLOGD("callFrame.funcName:%s, GetName:%s\n", callFrame.funcName.c_str(), symbol.GetName().data());
477     }
478 
479     callFrame.index = static_cast<size_t>(symbol.index_);
480     callFrame.mapName = symbol.module_.empty() ? symbol.comm_ : symbol.module_;
481     HLOG_ASSERT_MESSAGE(!callFrame.funcName.empty(), "%s", symbol.ToDebugString().c_str());
482 }
483 
SymbolicCallFrame(PerfRecordSample & recordSample,uint64_t ip,pid_t serverPid,perf_callchain_context context)484 void VirtualRuntime::SymbolicCallFrame(PerfRecordSample &recordSample, uint64_t ip,
485                                        pid_t serverPid, perf_callchain_context context)
486 {
487     pid_t pid = static_cast<pid_t>(recordSample.data_.pid);
488     pid_t tid = static_cast<pid_t>(recordSample.data_.tid);
489     if (serverPid != pid) {
490         pid = tid = serverPid;
491     }
492     auto symbol = GetSymbol(ip, pid, tid, context);
493     MakeCallFrame(symbol, recordSample.callFrames_.emplace_back(ip, 0));
494     HLOGV(" (%zu)unwind symbol: %*s%s", recordSample.callFrames_.size(),
495           static_cast<int>(recordSample.callFrames_.size()), "",
496           recordSample.callFrames_.back().ToSymbolString().c_str());
497 }
498 
RecoverCallStack(PerfRecordSample & recordSample)499 bool VirtualRuntime::RecoverCallStack(PerfRecordSample &recordSample)
500 {
501     auto StackTable = processStackMap_.find(recordSample.data_.pid);
502     if (StackTable == processStackMap_.end()) {
503         HLOGV("not found %" PRIu32 " pid", recordSample.data_.pid);
504         return false;
505     }
506     recordSample.ips_.clear();
507     if (StackTable->second != nullptr) {
508         StackTable->second->GetIpsByStackId(recordSample.stackId_, recordSample.ips_);
509     }
510     recordSample.RecoverCallStack();
511     return true;
512 }
513 
SymbolicRecord(PerfRecordSample & recordSample)514 void VirtualRuntime::SymbolicRecord(PerfRecordSample &recordSample)
515 {
516 #ifdef HIPERF_DEBUG_TIME
517     const auto startTime = steady_clock::now();
518 #endif
519     // Symbolic the Call Stack
520     recordSample.callFrames_.clear();
521     perf_callchain_context context = PERF_CONTEXT_MAX;
522     pid_t serverPid;
523     if (recordSample.data_.nr == 0) {
524         serverPid = recordSample.GetServerPidof(0);
525         SymbolicCallFrame(recordSample, recordSample.data_.ip, serverPid, PERF_CONTEXT_MAX);
526     }
527     for (u64 i = 0; i < recordSample.data_.nr; i++) {
528         uint64_t ip = recordSample.data_.ips[i];
529         if (ip >= PERF_CONTEXT_MAX) {
530             std::string contextName = UpdatePerfContext(ip, context);
531             HLOGV("context switch to %s", contextName.c_str());
532             continue;
533         } else if (ip < BAD_IP_ADDRESS) {
534             // ip 0 or 1 or less than 0
535             continue;
536         }
537         serverPid = recordSample.GetServerPidof(i);
538         SymbolicCallFrame(recordSample, ip, serverPid, context);
539     }
540 #ifdef HIPERF_DEBUG_TIME
541     auto usedTime = duration_cast<microseconds>(steady_clock::now() - startTime);
542     if (usedTime.count() != 0) {
543         HLOGV("cost %0.3f ms to symbolic ", usedTime.count() / MS_DURATION);
544     }
545     symbolicRecordTimes_ += usedTime;
546 #endif
547 }
548 
NeedDropKernelCallChain(PerfRecordSample & sample)549 void VirtualRuntime::NeedDropKernelCallChain(PerfRecordSample &sample)
550 {
551     // only do this in record mode.
552     if (recordCallBack_ == nullptr || needkernelCallChain_ ||
553         !sample.inKernel() || sample.data_.nr == 0) {
554         return;
555     }
556 
557     u64 skip = 0;
558     u64 skipPid = 0;
559     u64 *ips = sample.data_.ips;
560     for (; skip < sample.data_.nr; skip++) {
561         if (ips[skip] == PERF_CONTEXT_KERNEL) {
562             skipPid++;
563         }
564         if (ips[skip] == PERF_CONTEXT_USER) {
565             break;
566         }
567     }
568     sample.skipKernel_ = skip;
569     sample.data_.nr -= skip;
570     sample.header.size -= sizeof(u64) * skip;
571     if (sample.data_.server_nr > 0) {
572         sample.skipPid_ = skipPid;
573         sample.data_.server_nr -= skipPid;
574         sample.header.size -= sizeof(u64) * skipPid;
575     }
576 }
577 
UnwindFromRecord(PerfRecordSample & recordSample)578 void VirtualRuntime::UnwindFromRecord(PerfRecordSample &recordSample)
579 {
580 #if defined(is_ohos) && is_ohos
581 #ifdef HIPERF_DEBUG_TIME
582     const auto startTime = steady_clock::now();
583 #endif
584     HLOGV("unwind record (time:%llu)", recordSample.data_.time);
585     // if we have userstack ?
586     if (recordSample.data_.stack_size > 0) {
587         pid_t serverPid = recordSample.GetUstackServerPid();
588         pid_t pid = static_cast<pid_t>(recordSample.data_.pid);
589         pid_t tid = static_cast<pid_t>(recordSample.data_.tid);
590         if (serverPid != pid) {
591             pid = tid = serverPid;
592         }
593         auto &thread = UpdateThread(pid, tid);
594         callstack_.UnwindCallStack(thread, recordSample.data_.user_abi == PERF_SAMPLE_REGS_ABI_32,
595                                    recordSample.data_.user_regs, recordSample.data_.reg_nr,
596                                    recordSample.data_.stack_data, recordSample.data_.dyn_size,
597                                    recordSample.callFrames_);
598 #ifdef HIPERF_DEBUG_TIME
599         unwindCallStackTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
600 #endif
601         size_t oldSize = recordSample.callFrames_.size();
602         HLOGV("unwind %zu", recordSample.callFrames_.size());
603         callstack_.ExpandCallStack(thread.tid_, recordSample.callFrames_, callstackMergeLevel_);
604         HLOGV("expand %zu (+%zu)", recordSample.callFrames_.size(),
605               recordSample.callFrames_.size() - oldSize);
606 
607         recordSample.ReplaceWithCallStack(oldSize);
608     }
609 
610 #ifdef HIPERF_DEBUG_TIME
611     unwindFromRecordTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
612 #endif
613 
614     NeedDropKernelCallChain(recordSample);
615     // we will not do this in non record mode.
616     if (dedupStack_ && recordCallBack_ != nullptr) {
617         DedupFromRecord(&recordSample);
618     }
619 #endif
620 
621     // we will not do this in record mode
622     if (recordCallBack_ == nullptr) {
623         if (dedupStack_ && recordSample.stackId_.section.id > 0 && recordSample.data_.nr == 0) {
624             RecoverCallStack(recordSample);
625         }
626         // find the symbols , reabuild frame info
627         SymbolicRecord(recordSample);
628     }
629 }
630 
SetCollectSymbolCallBack(CollectSymbolCallBack collectSymbolCallBack)631 void VirtualRuntime::SetCollectSymbolCallBack(CollectSymbolCallBack collectSymbolCallBack)
632 {
633     collectSymbolCallBack_ = collectSymbolCallBack;
634 }
635 
UpdateFromRecord(PerfRecordSample & recordSample)636 void VirtualRuntime::UpdateFromRecord(PerfRecordSample &recordSample)
637 {
638     UpdateThread(recordSample.data_.pid, recordSample.data_.tid);
639     if (recordSample.data_.server_nr) {
640         // update all server threads
641         for (size_t i = 0; i < recordSample.data_.server_nr; i++) {
642             pid_t pid = static_cast<pid_t>(recordSample.data_.server_pids[i]);
643             UpdateThread(pid, pid);
644         }
645     }
646     // unwind
647     if (disableUnwind_) {
648         return;
649     } else {
650         UnwindFromRecord(recordSample);
651     }
652 }
653 
UpdateFromRecord(PerfRecordMmap & recordMmap)654 void VirtualRuntime::UpdateFromRecord(PerfRecordMmap &recordMmap)
655 {
656     HLOGV("  MMAP: size %d pid %u tid %u", recordMmap.header.size, recordMmap.data_.pid,
657           recordMmap.data_.tid);
658     HLOGV("  MMAP: %s dso '%s' (0x%llx-0x%llx)@0x%llx", recordMmap.inKernel() ? "kernel" : "user",
659           recordMmap.data_.filename, recordMmap.data_.addr,
660           recordMmap.data_.addr + recordMmap.data_.len, recordMmap.data_.pgoff);
661     // kernel mmap
662     // don't overwrite the vailed mmap , so we also check the recordMmap.data_.len
663     if (IsKernelThread(recordMmap.data_.pid)) {
664         UpdateKernelThreadMap(recordMmap.data_.pid, recordMmap.data_.addr,
665                               recordMmap.data_.len, recordMmap.data_.filename);
666     } else if (recordMmap.inKernel()) {
667         UpdatekernelMap(recordMmap.data_.addr, recordMmap.data_.addr + recordMmap.data_.len,
668                         recordMmap.data_.pgoff, recordMmap.data_.filename);
669     } else {
670         NeedAdaptSandboxPath(recordMmap.data_.filename, recordMmap.data_.pid, recordMmap.header.size);
671         FixHMBundleMmap(recordMmap.data_.filename, recordMmap.data_.pid, recordMmap.header.size);
672         auto map = UpdateThreadMaps(recordMmap.data_.pid, recordMmap.data_.tid, recordMmap.data_.filename,
673                                     recordMmap.data_.addr, recordMmap.data_.len, recordMmap.data_.pgoff);
674         UpdateSymbols(map, recordMmap.data_.pid);
675     }
676 }
677 
CheckValidSandBoxMmap(PerfRecordMmap2 & recordMmap2)678 bool VirtualRuntime::CheckValidSandBoxMmap(PerfRecordMmap2 &recordMmap2)
679 {
680     static std::shared_ptr<DfxMap> prevMap;
681     if ((recordMmap2.data_.prot & PROT_EXEC) != 0) {
682         // fake first segment, when second segment come.
683         auto symFile = SymbolsFile::CreateSymbolsFile(SYMBOL_ELF_FILE, recordMmap2.data_.filename);
684         if (symFile == nullptr) {
685             HLOGD("CheckValidSandBoxMmap Failed to create symbolFile!");
686             return false;
687         }
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         if (!symFile->LoadDebugInfo(curMap)) {
702             HLOGD("CheckValidSandBoxMmap Failed to load debuginfo!");
703             return false;
704         }
705 
706         if (!loadSymboleWhenNeeded_) {
707             symFile->LoadSymbols(curMap);
708         }
709 
710         if (strstr(recordMmap2.data_.filename, ".hap") == nullptr) {
711             auto elfLoadInfoMap = symFile->GetPtLoads();
712             u64 begin = recordMmap2.data_.addr - elfLoadInfoMap[0].mmapLen;
713             u64 len = elfLoadInfoMap[0].mmapLen;
714             u64 pgoff = elfLoadInfoMap[0].offset & (~(elfLoadInfoMap[0].align >= 1 ? elfLoadInfoMap[0].align - 1 : 0));
715             std::unique_ptr<PerfRecordMmap2> mmap2FirstSeg =
716                 std::make_unique<PerfRecordMmap2>(recordMmap2.inKernel(), recordMmap2.data_.pid, recordMmap2.data_.tid,
717                 begin, len, pgoff, 0, 0, 0, PROT_READ, 0, std::string(recordMmap2.data_.filename));
718             UpdateThreadMaps(mmap2FirstSeg->data_.pid, mmap2FirstSeg->data_.tid, mmap2FirstSeg->data_.filename,
719                 mmap2FirstSeg->data_.addr, mmap2FirstSeg->data_.len, mmap2FirstSeg->data_.pgoff);
720             recordCallBack_(std::move(mmap2FirstSeg));
721         } else {
722             auto elfLoadInfoMap = symFile->GetPtLoads();
723             u64 begin = recordMmap2.data_.addr - elfLoadInfoMap[0].mmapLen;
724             u64 len = elfLoadInfoMap[0].mmapLen;
725             u64 pgoff = elfLoadInfoMap[0].offset &
726                         (~(elfLoadInfoMap[0].align >= 1 ? elfLoadInfoMap[0].align - 1 : 0));
727             std::unique_ptr<PerfRecordMmap2> mmap2FirstSeg =
728                 std::make_unique<PerfRecordMmap2>(recordMmap2.inKernel(), recordMmap2.data_.pid, recordMmap2.data_.tid,
729                 begin, len, pgoff, 0, 0, 0, PROT_READ, 0, curMap->name);
730             UpdateThreadMaps(mmap2FirstSeg->data_.pid, mmap2FirstSeg->data_.tid, curMap->name,
731                 mmap2FirstSeg->data_.addr, mmap2FirstSeg->data_.len, mmap2FirstSeg->data_.pgoff);
732             recordCallBack_(std::move(mmap2FirstSeg));
733 
734             std::unique_ptr<PerfRecordMmap2> mmap2SecondSegment =
735                 std::make_unique<PerfRecordMmap2>(recordMmap2.inKernel(), recordMmap2.data_.pid, recordMmap2.data_.tid,
736                 recordMmap2.data_.addr,
737                 recordMmap2.data_.len,
738                 recordMmap2.data_.pgoff - prevMap->offset, // minus load offset of hap
739                 0, 0, 0, recordMmap2.data_.prot, 0, curMap->name);
740             UpdateThreadMaps(mmap2SecondSegment->data_.pid, mmap2SecondSegment->data_.tid, curMap->name,
741                 mmap2SecondSegment->data_.addr, mmap2SecondSegment->data_.len, mmap2SecondSegment->data_.pgoff);
742             recordCallBack_(std::move(mmap2SecondSegment));
743             recordMmap2.discard_ = true;
744         }
745         symbolsFiles_.emplace_back(std::move(symFile));
746         return true;
747     } else if (recordMmap2.data_.pgoff == 0) {
748         recordMmap2.discard_ = true;
749     }
750 
751     if (strstr(recordMmap2.data_.filename, ".hap") != nullptr) {
752         prevMap = std::make_shared<DfxMap>(
753             recordMmap2.data_.addr,
754             recordMmap2.data_.addr + recordMmap2.data_.len,
755             recordMmap2.data_.pgoff,
756             "", // prot
757             recordMmap2.data_.filename
758         );
759         HLOGD("CheckValidSandBoxMmap Update prev map!");
760     }
761     return !recordMmap2.discard_;
762 }
763 
UpdateFromRecord(PerfRecordMmap2 & recordMmap2)764 void VirtualRuntime::UpdateFromRecord(PerfRecordMmap2 &recordMmap2)
765 {
766     if (!OHOS::HiviewDFX::DfxMaps::IsLegalMapItem(recordMmap2.data_.filename)) {
767         return;
768     }
769 
770     HLOGV("  MMAP2: size %d pid %u tid %u", recordMmap2.header.size, recordMmap2.data_.pid,
771           recordMmap2.data_.tid);
772     HLOGV("  MMAP2: %s dso '%s' (0x%llx-0x%llx)@0x%llx prot:%u", recordMmap2.inKernel() ? "kernel" : "user",
773           recordMmap2.data_.filename, recordMmap2.data_.addr,
774           recordMmap2.data_.addr + recordMmap2.data_.len, recordMmap2.data_.pgoff, recordMmap2.data_.prot);
775 
776     if (recordCallBack_) {
777         if (NeedAdaptSandboxPath(recordMmap2.data_.filename, recordMmap2.data_.pid, recordMmap2.header.size)) {
778             FixHMBundleMmap(recordMmap2.data_.filename, recordMmap2.data_.pid, recordMmap2.header.size);
779             if (!CheckValidSandBoxMmap(recordMmap2)) {
780                 return;
781             }
782         }
783     }
784     auto map = UpdateThreadMaps(recordMmap2.data_.pid, recordMmap2.data_.tid, recordMmap2.data_.filename,
785                                 recordMmap2.data_.addr, recordMmap2.data_.len, recordMmap2.data_.pgoff,
786                                 recordMmap2.data_.prot);
787     UpdateSymbols(map, recordMmap2.data_.pid);
788 }
789 
UpdateFromRecord(PerfRecordComm & recordComm)790 void VirtualRuntime::UpdateFromRecord(PerfRecordComm &recordComm)
791 {
792     recordComm.DumpLog(__FUNCTION__);
793     UpdateThread(recordComm.data_.pid, recordComm.data_.tid, recordComm.data_.comm);
794 }
795 
SetRecordMode(RecordCallBack recordCallBack)796 void VirtualRuntime::SetRecordMode(RecordCallBack recordCallBack)
797 {
798     recordCallBack_ = recordCallBack;
799 }
800 
UpdateSymbols(std::shared_ptr<DfxMap> map,pid_t pid)801 void VirtualRuntime::UpdateSymbols(std::shared_ptr<DfxMap> map, pid_t pid)
802 {
803     if (map == nullptr) {
804         return;
805     }
806     if (map->symbolFileIndex != -1) {
807         // in cache
808         return;
809     }
810     HLOGD("try to find symbols for file: %s", map->name.c_str());
811     for (size_t i = 0; i < symbolsFiles_.size(); ++i) {
812         if (symbolsFiles_[i]->filePath_ == map->name) {
813             map->symbolFileIndex = static_cast<int32_t>(i);
814             HLOGV("already have '%s'", map->name.c_str());
815             return;
816         }
817     }
818 
819 #ifdef HIPERF_DEBUG_TIME
820     const auto startTime = steady_clock::now();
821 #endif
822     /**
823      * map[]     map.name = SymbolsFile.filePath_         prot    SymbolsFileType
824      * seg1      /data/storage/el1/bundle/entry.hap       r--p    ABC
825      * seg2      /data/storage/el1/bundle/entry.hap       r-xp    ELF
826      * seg3      /data/storage/el1/bundle/entry.hap       r--p    ABC
827      * seg4      /data/storage/el1/bundle/entry.hap       r--p    ABC
828      * seg1      /system/app/SceneBoard/SceneBoard.hap    r--p    ABC
829      * seg2      /system/app/SceneBoard/SceneBoard.hap    r--p    ABC
830      * seg3      /system/app/SceneBoard/SceneBoard.hap    r--p    ABC
831      * segN      .hap                                     r--p    .an/jit/etc
832      * 1.map.name == symbolsFile.filePath_
833      * 2.map.FileType == symbolsFiles_[map.symbolFileIndex]
834      * 3.cache pc->map->symbolsFiles[map.symbolFileIndex]
835      * 4.must ensure map.mapType assigned with SymbolsFile constructions at the same time.
836     */
837     auto symbolsFile = SymbolsFile::CreateSymbolsFile(map->name, pid);
838     symbolsFile->SetMapsInfo(map);
839     if (enableDebugInfoSymbolic_ && symbolsFile->symbolFileType_ == SymbolsFileType::SYMBOL_ELF_FILE) {
840         symbolsFile->EnableMiniDebugInfo();
841     }
842     // set symbol path If it exists
843     if (symbolsPaths_.size() > 0) {
844         // also load from search path
845         symbolsFile->setSymbolsFilePath(symbolsPaths_);
846     }
847     if (loadSymboleWhenNeeded_) {
848         // load it when we need it
849         symbolsFiles_.emplace_back(std::move(symbolsFile));
850     } else if (symbolsFile->LoadSymbols()) {
851         symbolsFiles_.emplace_back(std::move(symbolsFile));
852     } else {
853         HLOGW("symbols file for '%s' not found.", map->name.c_str());
854     }
855 #ifdef HIPERF_DEBUG_TIME
856     auto usedTime = duration_cast<microseconds>(steady_clock::now() - startTime);
857     if (usedTime.count() != 0) {
858         HLOGV("cost %0.3f ms to load '%s'", usedTime.count() / MS_DURATION, map->name.c_str());
859     }
860     updateSymbolsTimes_ += usedTime;
861 #endif
862 }
863 
GetKernelSymbol(uint64_t ip,const std::vector<DfxMap> & memMaps,const VirtualThread & thread)864 const DfxSymbol VirtualRuntime::GetKernelSymbol(uint64_t ip, const std::vector<DfxMap> &memMaps,
865                                                 const VirtualThread &thread)
866 {
867     DfxSymbol vaddrSymbol(ip, thread.name_);
868     for (auto &map : memMaps) {
869         if (ip > map.begin && ip < map.end) {
870             HLOGM("found addr 0x%" PRIx64 " in kernel map 0x%" PRIx64 " - 0x%" PRIx64 " from %s",
871                   ip, map.begin, map.end, map.name.c_str());
872             vaddrSymbol.module_ = map.name;
873             // found symbols by file name
874             for (auto &symbolsFile : symbolsFiles_) {
875                 if (symbolsFile->filePath_ == map.name) {
876                     vaddrSymbol.symbolFileIndex_ = symbolsFile->id_;
877                     vaddrSymbol.fileVaddr_ =
878                         symbolsFile->GetVaddrInSymbols(ip, map.begin, map.offset);
879                     perf_callchain_context context = PERF_CONTEXT_KERNEL;
880                     if (GetSymbolCache(vaddrSymbol.fileVaddr_, vaddrSymbol, context)) {
881                         return vaddrSymbol;
882                     }
883                     HLOGV("found symbol vaddr 0x%" PRIx64 " for runtime vaddr 0x%" PRIx64
884                           " at '%s'",
885                           vaddrSymbol.fileVaddr_, ip, map.name.c_str());
886                     if (!symbolsFile->SymbolsLoaded()) {
887                         symbolsFile->LoadSymbols();
888                     }
889                     DfxSymbol foundSymbols = symbolsFile->GetSymbolWithVaddr(vaddrSymbol.fileVaddr_);
890                     foundSymbols.taskVaddr_ = ip;
891                     if (!foundSymbols.IsValid()) {
892                         HLOGW("addr 0x%" PRIx64 " vaddr  0x%" PRIx64 " NOT found in symbol file %s",
893                               ip, vaddrSymbol.fileVaddr_, map.name.c_str());
894                         return vaddrSymbol;
895                     } else {
896                         return foundSymbols;
897                     }
898                 }
899             }
900             HLOGW("addr 0x%" PRIx64 " in map but NOT found the symbol file %s", ip,
901                   map.name.c_str());
902         } else {
903             HLOGM("addr 0x%" PRIx64 " not in map 0x%" PRIx64 " - 0x%" PRIx64 " from %s", ip,
904                   map.begin, map.end, map.name.c_str());
905         }
906     }
907     return vaddrSymbol;
908 }
909 
GetKernelThreadSymbol(uint64_t ip,const VirtualThread & thread)910 const DfxSymbol VirtualRuntime::GetKernelThreadSymbol(uint64_t ip, const VirtualThread &thread)
911 {
912     DfxSymbol vaddrSymbol(ip, thread.name_);
913     int64_t mapIndex = thread.FindMapIndexByAddr(ip);
914     if (mapIndex < 0) {
915 #ifdef HIPERF_DEBUG
916         thread.ReportVaddrMapMiss(ip);
917 #endif
918         return vaddrSymbol;
919     }
920 
921     auto map = thread.GetMaps()[mapIndex];
922     if (map == nullptr) {
923         return vaddrSymbol;
924     }
925     HLOGM("found addr 0x%" PRIx64 " in kthread map 0x%" PRIx64 " - 0x%" PRIx64 " from %s",
926             ip, map->begin, map->end, map->name.c_str());
927     // found symbols by file name
928     for (auto &symbolsFile : symbolsFiles_) {
929         if (symbolsFile->filePath_ == map->name) {
930             vaddrSymbol.symbolFileIndex_ = symbolsFile->id_;
931             vaddrSymbol.module_ = map->name;
932             vaddrSymbol.fileVaddr_ =
933                 symbolsFile->GetVaddrInSymbols(ip, map->begin, map->offset);
934             perf_callchain_context context = PERF_CONTEXT_MAX;
935             if (GetSymbolCache(vaddrSymbol.fileVaddr_, vaddrSymbol, context)) {
936                 return vaddrSymbol;
937             }
938             HLOGV("found symbol vaddr 0x%" PRIx64 " for runtime vaddr 0x%" PRIx64 " at '%s'",
939                     vaddrSymbol.fileVaddr_, ip, map->name.c_str());
940             if (!symbolsFile->SymbolsLoaded()) {
941                 symbolsFile->LoadDebugInfo();
942                 symbolsFile->LoadSymbols(map);
943             }
944             auto foundSymbols = symbolsFile->GetSymbolWithVaddr(vaddrSymbol.fileVaddr_);
945             foundSymbols.taskVaddr_ = ip;
946             if (!foundSymbols.IsValid()) {
947                 HLOGW("addr 0x%" PRIx64 " vaddr  0x%" PRIx64 " NOT found in symbol file %s",
948                         ip, vaddrSymbol.fileVaddr_, map->name.c_str());
949                 return vaddrSymbol;
950             } else {
951                 return foundSymbols;
952             }
953         }
954     }
955     HLOGW("addr 0x%" PRIx64 " in map but NOT found the symbol file %s", ip,
956             map->name.c_str());
957     return vaddrSymbol;
958 }
959 
GetUserSymbol(uint64_t ip,const VirtualThread & thread)960 const DfxSymbol VirtualRuntime::GetUserSymbol(uint64_t ip, const VirtualThread &thread)
961 {
962     DfxSymbol vaddrSymbol(ip, thread.name_);
963     int64_t mapIndex = thread.FindMapIndexByAddr(ip);
964     if (mapIndex >= 0) {
965         auto map = thread.GetMaps()[mapIndex];
966         SymbolsFile *symbolsFile = thread.FindSymbolsFileByMap(map);
967         if (symbolsFile != nullptr) {
968             vaddrSymbol.symbolFileIndex_ = symbolsFile->id_;
969             vaddrSymbol.module_ = map->name;
970             vaddrSymbol.fileVaddr_ = symbolsFile->GetVaddrInSymbols(ip, map->begin, map->offset);
971             perf_callchain_context context = PERF_CONTEXT_USER;
972             if (GetSymbolCache(vaddrSymbol.fileVaddr_, vaddrSymbol, context)) {
973                 return vaddrSymbol;
974             }
975             HLOGV("found symbol vaddr 0x%" PRIx64 " for runtime vaddr 0x%" PRIx64 " at '%s'",
976                   vaddrSymbol.fileVaddr_, ip, map->name.c_str());
977             if (!symbolsFile->SymbolsLoaded()) {
978                 symbolsFile->LoadDebugInfo(map);
979                 symbolsFile->LoadSymbols(map);
980             }
981             DfxSymbol foundSymbols;
982             if (!symbolsFile->IsAbc()) {
983                 foundSymbols = symbolsFile->GetSymbolWithVaddr(vaddrSymbol.fileVaddr_);
984             } else {
985                 HLOGD("symbolsFile:%s is ABC :%d", symbolsFile->filePath_.c_str(), symbolsFile->IsAbc());
986                 foundSymbols = symbolsFile->GetSymbolWithPcAndMap(ip, map);
987             }
988 
989             if (foundSymbols.IsValid()) {
990                 return foundSymbols;
991             } else {
992                 HLOGW("addr 0x%" PRIx64 " vaddr  0x%" PRIx64 " NOT found in symbol file %s", ip,
993                       vaddrSymbol.fileVaddr_, map->name.c_str());
994                 if (symbolsFile->IsAbc()) {
995                     symbolsFile->symbolsMap_.insert(std::make_pair(ip, vaddrSymbol));
996                 }
997                 return vaddrSymbol;
998             }
999         } else {
1000             HLOGW("addr 0x%" PRIx64 " in map but NOT found the symbol file %s", ip, map->name.c_str());
1001         }
1002     } else {
1003 #ifdef HIPERF_DEBUG
1004         thread.ReportVaddrMapMiss(ip);
1005 #endif
1006     }
1007     return vaddrSymbol;
1008 }
1009 
GetSymbolCache(uint64_t fileVaddr,DfxSymbol & symbol,const perf_callchain_context & context)1010 bool VirtualRuntime::GetSymbolCache(uint64_t fileVaddr, DfxSymbol &symbol,
1011                                     const perf_callchain_context &context)
1012 {
1013     if (context == PERF_CONTEXT_MAX and kThreadSymbolCache_.count(fileVaddr)) {
1014         if (kThreadSymbolCache_.find(symbol.fileVaddr_) == kThreadSymbolCache_.end()) {
1015             return false;
1016         }
1017         symbol = kThreadSymbolCache_[symbol.fileVaddr_];
1018         symbol.hit_++;
1019         HLOGV("hit kernel thread cache 0x%" PRIx64 " %d", fileVaddr, symbol.hit_);
1020         return true;
1021     } else if (context != PERF_CONTEXT_USER and kernelSymbolCache_.count(fileVaddr)) {
1022         if (kernelSymbolCache_.find(symbol.fileVaddr_) == kernelSymbolCache_.end()) {
1023             return false;
1024         }
1025         symbol = kernelSymbolCache_[symbol.fileVaddr_];
1026         symbol.hit_++;
1027         HLOGV("hit kernel cache 0x%" PRIx64 " %d", fileVaddr, symbol.hit_);
1028         return true;
1029     } else if (userSymbolCache_.count(symbol.fileVaddr_) != 0) {
1030         const DfxSymbol &cachedSymbol = userSymbolCache_[symbol.fileVaddr_];
1031         // must be the same file
1032         if (cachedSymbol.module_ != symbol.module_) {
1033             return false;
1034         }
1035         symbol = cachedSymbol;
1036         symbol.hit_++;
1037         HLOGV("hit user cache 0x%" PRIx64 " %d %s", fileVaddr, symbol.hit_,
1038             symbol.ToDebugString().c_str());
1039         return true;
1040     } else {
1041         HLOGM("cache miss k %zu u %zu kt %zu", kernelSymbolCache_.size(),
1042               userSymbolCache_.size(), kThreadSymbolCache_.size());
1043     }
1044     return false;
1045 }
1046 
GetSymbol(uint64_t ip,pid_t pid,pid_t tid,const perf_callchain_context & context)1047 DfxSymbol VirtualRuntime::GetSymbol(uint64_t ip, pid_t pid, pid_t tid, const perf_callchain_context &context)
1048 {
1049     HLOGV("try find tid %u ip 0x%" PRIx64 " in %zu symbolsFiles\n", tid, ip, symbolsFiles_.size());
1050     DfxSymbol symbol;
1051 
1052     if (IsKernelThread(pid)) {
1053         VirtualThread &kthread = GetThread(pid, tid);
1054         HLOGM("try found addr in kernel thread %u with %zu maps", pid,
1055               kthread.GetMaps().size());
1056         symbol = GetKernelThreadSymbol(ip, kthread);
1057         HLOGM("add addr to kernel thread cache 0x%" PRIx64 " cache size %zu", ip,
1058               kThreadSymbolCache_.size());
1059         kThreadSymbolCache_[symbol.fileVaddr_] = symbol;
1060         if (symbol.IsValid()) {
1061             return symbol;
1062         }
1063     }
1064 
1065     if (context == PERF_CONTEXT_USER or (context == PERF_CONTEXT_MAX and !symbol.IsValid())) {
1066         // check userspace memmap
1067         symbol = GetUserSymbol(ip, GetThread(pid, tid));
1068         if (userSymbolCache_.find(symbol.fileVaddr_) == userSymbolCache_.end()) {
1069             userSymbolCache_.reserve(USER_SYMBOL_CACHE_LIMIT);
1070         }
1071         userSymbolCache_[symbol.fileVaddr_] = symbol;
1072         HLOGV("cache ip 0x%" PRIx64 " to %s", ip,
1073               userSymbolCache_[symbol.fileVaddr_].ToDebugString().c_str());
1074     }
1075 
1076     if (context == PERF_CONTEXT_KERNEL or (context == PERF_CONTEXT_MAX and !symbol.IsValid())) {
1077         // check kernelspace
1078         HLOGM("try found addr in kernelspace %zu maps", kernelSpaceMemMaps_.size());
1079         symbol = GetKernelSymbol(ip, kernelSpaceMemMaps_, GetThread(pid, tid));
1080         HLOGM("add addr to kernel cache 0x%" PRIx64 " cache size %zu", ip,
1081               kernelSymbolCache_.size());
1082         kernelSymbolCache_[symbol.fileVaddr_] = symbol;
1083     }
1084     return symbol;
1085 }
1086 
SetSymbolsPaths(const std::vector<std::string> & symbolsPaths)1087 bool VirtualRuntime::SetSymbolsPaths(const std::vector<std::string> &symbolsPaths)
1088 {
1089     std::unique_ptr<SymbolsFile> symbolsFile = SymbolsFile::CreateSymbolsFile(SYMBOL_UNKNOW_FILE);
1090     if (symbolsFile == nullptr) {
1091         return false;
1092     }
1093     // we need check if the path is accessible
1094     bool accessible = symbolsFile->setSymbolsFilePath(symbolsPaths);
1095     if (accessible) {
1096         symbolsPaths_ = symbolsPaths;
1097     } else {
1098         if (!symbolsPaths.empty()) {
1099             printf("some symbols path unable access\n");
1100         }
1101     }
1102     return accessible;
1103 }
1104 
UpdateFromPerfData(const std::vector<SymbolFileStruct> & symbolFileStructs)1105 void VirtualRuntime::UpdateFromPerfData(const std::vector<SymbolFileStruct> &symbolFileStructs)
1106 {
1107     // review: if we need move to some other place ?
1108     HLOG_ASSERT_MESSAGE(symbolsFiles_.size() == 0, " symbolsFiles_ size is %zu",
1109                         symbolsFiles_.size());
1110     for (const auto &symbolFileStruct : symbolFileStructs) {
1111         HLOGV("symbolFileStruct.filePath_:'%s'", symbolFileStruct.filePath_.c_str());
1112         HLOGV("symbolFileStruct.buildId_:'%s'", symbolFileStruct.buildId_.c_str());
1113         HLOGV("process symbols file:'%s':'%s'", symbolFileStruct.filePath_.c_str(),
1114               symbolFileStruct.buildId_.c_str());
1115 
1116         // load from symbolFileStruct (perf.data)
1117         std::unique_ptr<SymbolsFile> symbolsFile = SymbolsFile::LoadSymbolsFromSaved(symbolFileStruct);
1118         if (symbolsFile == nullptr) {
1119             continue;
1120         }
1121         // reaload from sybol path If it exists
1122         if (symbolsPaths_.size() > 0) {
1123             HLOGV("try again with symbolsPaths setup");
1124             symbolsFile->setSymbolsFilePath(symbolsPaths_); // also load from search path
1125             // use give us path ,we must reload it.
1126             symbolsFile->LoadSymbols();
1127         }
1128         symbolsFile->id_ = static_cast<int32_t>(symbolsFiles_.size());
1129         symbolsFiles_.emplace_back(std::move(symbolsFile));
1130     }
1131 }
1132 
ImportUniqueStackNodes(const std::vector<UniStackTableInfo> & uniStackTableInfos)1133 void VirtualRuntime::ImportUniqueStackNodes(const std::vector<UniStackTableInfo>& uniStackTableInfos)
1134 {
1135     for (const UniStackTableInfo& item : uniStackTableInfos) {
1136         auto stackTable = std::make_shared<UniqueStackTable>(item.pid, item.tableSize);
1137         for (const UniStackNode& node : item.nodes) {
1138             stackTable->ImportNode(node.index, node.node);
1139         }
1140         processStackMap_[item.pid] = std::move(stackTable);
1141     }
1142 }
1143 
1144 /*
1145    ARM functions
1146        The table below lists the symbols exported by the vDSO.
1147 
1148        symbol                 version
1149        ────────────────────────────────────────────────────────────
1150        __vdso_gettimeofday    LINUX_2.6 (exported since Linux 4.1)
1151        __vdso_clock_gettime   LINUX_2.6 (exported since Linux 4.1)
1152 
1153        Additionally, the ARM port has a code page full of utility
1154        functions.  Since it's just a raw page of code, there is no ELF
1155        information for doing symbol lookups or versioning.  It does
1156        provide support for different versions though.
1157 
1158        For information on this code page, it's best to refer to the
1159        kernel documentation as it's extremely detailed and covers
1160        everything you need to know:
1161        Documentation/arm/kernel_user_helpers.txt.
1162 
1163    aarch64 functions
1164        The table below lists the symbols exported by the vDSO.
1165 
1166        symbol                   version
1167        ──────────────────────────────────────
1168        __kernel_rt_sigreturn    LINUX_2.6.39
1169        __kernel_gettimeofday    LINUX_2.6.39
1170        __kernel_clock_gettime   LINUX_2.6.39
1171        __kernel_clock_getres    LINUX_2.6.39
1172 */
LoadVdso()1173 void VirtualRuntime::LoadVdso()
1174 {
1175 #if !is_mingw
1176     VirtualThread myThread(getpid(), symbolsFiles_);
1177     myThread.ParseMap();
1178     for (const auto &map : myThread.GetMaps()) {
1179         if (map->name == MMAP_VDSO_NAME) {
1180             std::string memory(map->end - map->begin, '\0');
1181             std::copy(reinterpret_cast<char *>((map->begin)), reinterpret_cast<char *>((map->end)),
1182                       &memory[0]);
1183             std::string tempPath("/data/local/tmp/");
1184             std::string tempFileName = tempPath + MMAP_VDSO_NAME;
1185             if (!WriteStringToFile(tempFileName, memory)) {
1186                 printf("vdso temp file create fail at %s\n", tempFileName.c_str());
1187             } else {
1188                 HLOGD("vdso temp file create at %s:%zu", tempFileName.c_str(), memory.size());
1189                 auto symbolsFile = SymbolsFile::CreateSymbolsFile(MMAP_VDSO_NAME);
1190                 symbolsFile->setSymbolsFilePath(tempPath); // also load from search path
1191                 symbolsFiles_.emplace_back(std::move(symbolsFile));
1192                 return;
1193             }
1194         }
1195     }
1196     HLOGD("no vdso found");
1197 #endif
1198 }
1199 
UpdateServiceSpaceMaps()1200 void VirtualRuntime::UpdateServiceSpaceMaps()
1201 {
1202     VirtualThread &kthread = GetThread(SYSMGR_PID, SYSMGR_PID);
1203     kthread.ParseServiceMap(SYSMGR_FILE_NAME);
1204     if (recordCallBack_) {
1205         for (const auto &map : kthread.GetMaps()) {
1206             auto record =
1207             std::make_unique<PerfRecordMmap>(true, SYSMGR_PID, SYSMGR_PID,
1208                                              map->begin, map->end - map->begin,
1209                                              0, SYSMGR_FILE_NAME);
1210             recordCallBack_(std::move(record));
1211         }
1212     }
1213 }
1214 
UpdateServiceSymbols()1215 void VirtualRuntime::UpdateServiceSymbols()
1216 {
1217     HLOGD("try to update kernel thread symbols for kernel service");
1218     std::string fileName = SYSMGR_FILE_NAME;
1219     auto symbolsFile = SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_THREAD_FILE, fileName);
1220 
1221     HLOGD("add kernel service symbol file: %s", fileName.c_str());
1222     if (symbolsFile->LoadSymbols()) {
1223         symbolsFiles_.emplace_back(std::move(symbolsFile));
1224     } else {
1225         HLOGW("symbols file for '%s' not found.", fileName.c_str());
1226     }
1227 }
1228 
UpdateKernelThreadMap(pid_t pid,uint64_t begin,uint64_t len,std::string filename)1229 void VirtualRuntime::UpdateKernelThreadMap(pid_t pid, uint64_t begin, uint64_t len,
1230                                            std::string filename)
1231 {
1232     HLOGV("update kernel thread map pid %u name:'%s'", pid, filename.c_str());
1233 
1234     VirtualThread &thread = GetThread(pid, pid);
1235     thread.CreateMapItem(filename, begin, len, 0u);
1236 }
1237 
UpdateDevhostSpaceMaps()1238 void VirtualRuntime::UpdateDevhostSpaceMaps()
1239 {
1240     VirtualThread &kthread = GetThread(devhostPid_, devhostPid_);
1241     kthread.ParseDevhostMap(devhostPid_);
1242     if (recordCallBack_) {
1243         for (const auto &map : kthread.GetMaps()) {
1244             auto record =
1245             std::make_unique<PerfRecordMmap>(false, devhostPid_, devhostPid_,
1246                                              map->begin, map->end - map->begin,
1247                                              0, map->name);
1248             recordCallBack_(std::move(record));
1249         }
1250     }
1251 }
1252 
UpdateDevhostSymbols()1253 void VirtualRuntime::UpdateDevhostSymbols()
1254 {
1255     HLOGD("try to update kernel thread symbols for devhost");
1256     auto kallsyms = SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_THREAD_FILE, DEVHOST_FILE_NAME);
1257     // file name of devhost.ko
1258     std::map<std::string_view, std::unique_ptr<SymbolsFile>> koMaps;
1259     koMaps[DEVHOST_FILE_NAME] =
1260         SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_THREAD_FILE, DEVHOST_LINUX_FILE_NAME);
1261 
1262     if (kallsyms->LoadSymbols()) {
1263         for (auto &symbol : kallsyms->GetSymbols()) {
1264             if (koMaps.find(symbol.module_) == koMaps.end()) {
1265                 std::string filename = std::string(symbol.module_);
1266                 // [devhost] to /liblinux/devhost.ko
1267                 filename.erase(filename.begin());
1268                 filename.erase(filename.end() - 1);
1269                 filename = DEVHOST_LINUX_PREFIX + filename + KERNEL_MODULES_EXT_NAME;
1270                 koMaps[symbol.module_] =
1271                     SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_THREAD_FILE, filename);
1272             }
1273             if (koMaps[symbol.module_] == nullptr) {
1274                 continue;
1275             }
1276             koMaps[symbol.module_]->AddSymbol(std::move(symbol));
1277         }
1278 
1279         HLOGD("devhost loaded %zu symbolfiles", koMaps.size());
1280         for (auto &it : koMaps) {
1281             if (it.second == nullptr) {
1282                 continue;
1283             }
1284             HLOGD("Load %zu symbols to %s", it.second->GetSymbols().size(),
1285                   it.second->filePath_.c_str());
1286             symbolsFiles_.emplace_back(std::move(it.second));
1287         }
1288     } else {
1289         HLOGW("symbols file for devhost parse failed.");
1290     }
1291 
1292     // update normal symbole files
1293     VirtualThread &kthread = GetThread(devhostPid_, devhostPid_);
1294     for (const auto &map : kthread.GetMaps()) {
1295         UpdateSymbols(map, devhostPid_);
1296     }
1297 }
1298 
FixHMBundleMmap(char * filename,int pid,u16 & headerSize)1299 void VirtualRuntime::FixHMBundleMmap(char *filename, int pid, u16 &headerSize)
1300 {
1301     if (!isHM_) {
1302         return;
1303     }
1304     // fix bundle path in mmap
1305     std::string newFilename = filename;
1306     VirtualThread &thread = GetThread(pid, pid);
1307     if (NeedAdaptHMBundlePath(newFilename, thread.name_)) {
1308         size_t oldSize = strlen(filename);
1309         (void)memset_s(filename, KILO, '\0', KILO);
1310         if (strncpy_s(filename, KILO, newFilename.c_str(), newFilename.size()) != 0) {
1311             HLOGD("strncpy_s recordMmap2 failed!");
1312         }
1313         headerSize += newFilename.size() - oldSize;
1314     }
1315 }
1316 
SetDevhostPid(pid_t devhost)1317 void VirtualRuntime::SetDevhostPid(pid_t devhost)
1318 {
1319     HLOGD("Set devhost pid: %d", devhost);
1320     devhostPid_ = devhost;
1321 }
1322 
IsKernelThread(pid_t pid)1323 bool VirtualRuntime::IsKernelThread(pid_t pid)
1324 {
1325     if (!isHM_) {
1326         return false;
1327     }
1328     return pid == SYSMGR_PID || pid == devhostPid_;
1329 }
1330 
1331 } // namespace HiPerf
1332 } // namespace Developtools
1333 } // namespace OHOS
1334