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