• 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(const bool onDevice)43 VirtualRuntime::VirtualRuntime(const 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(const pid_t tid)55 std::string VirtualRuntime::ReadFromSavedCmdLines(const 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(const pid_t tid,const bool isThread)82 std::string VirtualRuntime::ReadThreadName(const pid_t tid, const 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(const pid_t pid,const pid_t tid,const std::string name)102 VirtualThread &VirtualRuntime::UpdateThread(const pid_t pid, const 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() || !StringEndsWith(thread.name_, name))) {
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(const pid_t pid,const pid_t tid,const std::string name)117 VirtualThread &VirtualRuntime::CreateThread(const pid_t pid, const 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(const pid_t pid,const pid_t tid,const std::string name)210 VirtualThread &VirtualRuntime::GetThread(const pid_t pid, const 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(const pid_t pid,const pid_t tid,const std::string filename,const uint64_t begin,const uint64_t len,const uint64_t offset,const uint32_t prot)227 std::shared_ptr<DfxMap> VirtualRuntime::UpdateThreadMaps(const pid_t pid, const pid_t tid, const std::string filename,
228                                                          const uint64_t begin, const uint64_t len,
229                                                          const uint64_t offset, const uint32_t prot)
230 {
231     VirtualThread &thread = GetThread(pid, tid);
232     std::shared_ptr<DfxMap> map = thread.CreateMapItem(filename, begin, len, offset, prot);
233     if (isHM_) {
234         thread.FixHMBundleMap();
235     }
236     return map;
237 }
238 
UpdateKernelModulesSpaceMaps()239 void VirtualRuntime::UpdateKernelModulesSpaceMaps()
240 {
241     // found the kernel modules
242     std::vector<DfxMap> koMaps;
243     std::ifstream ifs("/proc/modules", std::ifstream::in);
244     if (!ifs.is_open()) {
245         perror("kernel modules read failed(/proc/modules)\n");
246         return;
247     }
248     std::string line;
249     while (getline(ifs, line)) {
250         uint64_t addr = 0;
251         uint64_t size = 0;
252         uint64_t lineSize = line.size();
253         if (lineSize > 4096) { // 4096: line length
254             continue;
255         }
256         char *module = new char[lineSize + 1];
257         /*
258         name       size  load     map
259         hi_mipi_rx 53248 0 - Live 0xbf109000 (O)
260         hi3516cv500_hdmi 237568 0 - Live 0xbf0bb000 (O)
261         hifb 143360 0 - Live 0xbf089000 (O)
262         hi3516cv500_vo_dev 98304 0 - Live 0xbf070000 (O)
263         hi3516cv500_tde 110592 0 - Live 0xbf04a000 (O)
264         hi3516cv500_sys 36864 0 - Live 0xbf03a000 (O)
265         hi3516cv500_base 20480 5
266         hi_mipi_rx,hi3516cv500_hdmi,hifb,hi3516cv500_vo_dev,hi3516cv500_tde,hi3516cv500_sys,
267         hi3516cv500_base,sys_config,hi_proc,hi_irq,Live 0xbf000000 (O)
268         */
269         int ret = sscanf_s(line.c_str(), "%s%" PRIu64 "%*u%*s%*s 0x%" PRIx64 "", module,
270                            lineSize, &size, &addr);
271         constexpr int numSlices {3};
272         if (ret == numSlices) {
273             auto &map = koMaps.emplace_back(addr, addr + size, 0, "", std::string(module));
274             HLOGV("add ko map %s", map.ToString().c_str());
275         } else {
276             HLOGE("unknown line %d: '%s'", ret, line.c_str());
277         }
278         delete []module;
279     }
280 
281     if (std::all_of(koMaps.begin(), koMaps.end(),
282                     [](const DfxMap &item) { return item.begin == 0; })) {
283         koMaps.clear();
284         HLOGW("no addr found in /proc/modules. remove all the ko");
285     }
286     if (recordCallBack_) {
287         for (const auto &map : koMaps) {
288             auto record = std::make_unique<PerfRecordMmap>(true, 0, 0, map.begin,
289                                                            map.end - map.begin, 0, map.name);
290             recordCallBack_(*record);
291         }
292     }
293     std::move(koMaps.begin(), koMaps.end(), std::back_inserter(kernelSpaceMemMaps_));
294 }
295 
UpdateKernelSpaceMaps()296 void VirtualRuntime::UpdateKernelSpaceMaps()
297 {
298     // add kernel first
299     auto &map = kernelSpaceMemMaps_.emplace_back(0, std::numeric_limits<uint64_t>::max(), 0, "", KERNEL_MMAP_NAME);
300     if (recordCallBack_) {
301         auto record = std::make_unique<PerfRecordMmap>(true, 0, 0, map.begin,
302                                                        map.end - map.begin, 0, map.name);
303         recordCallBack_(*record);
304     }
305 }
306 
UpdateKernelModulesSymbols()307 void VirtualRuntime::UpdateKernelModulesSymbols()
308 {
309     HLOGD("load ko symbol and build id");
310     for (auto &map : kernelSpaceMemMaps_) {
311         if (map.name == KERNEL_MMAP_NAME) {
312             continue;
313         }
314         auto kernelModuleFile = SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_MODULE_FILE, map.name);
315         if (symbolsPaths_.size() > 0) {
316             kernelModuleFile->setSymbolsFilePath(symbolsPaths_); // also load from search path
317         }
318         kernelModuleFile->LoadSymbols();
319         symbolsFiles_.emplace_back(std::move(kernelModuleFile));
320     }
321 }
322 
UpdateKernelSymbols()323 void VirtualRuntime::UpdateKernelSymbols()
324 {
325     HLOGD("create a kernel mmap record");
326     // found kernel source
327     auto kernelFile = SymbolsFile::CreateSymbolsFile(KERNEL_MMAP_NAME);
328     // set symbol path If it exists
329     if (symbolsPaths_.size() > 0) {
330         kernelFile->setSymbolsFilePath(symbolsPaths_); // also load from search path
331     }
332     if (!IsRoot()) {
333         HLOGD("user mode do not load kernel syms");
334         printf("Hiperf is not running as root mode. Do not need load kernel syms\n");
335     }
336     if (kernelFile->LoadSymbols()) {
337         auto record = std::make_unique<PerfRecordMmap>(
338             true, 0, 0, kernelFile->textExecVaddr_, kernelFile->textExecVaddrRange_,
339             kernelFile->textExecVaddrFileOffset_, KERNEL_MMAP_NAME);
340 
341         if (recordCallBack_) {
342             recordCallBack_(*record);
343         }
344         symbolsFiles_.emplace_back(std::move(kernelFile));
345     } else {
346         HLOGW("kernel symbol not found.\n");
347     }
348 }
349 
UpdatekernelMap(const uint64_t begin,const uint64_t end,const uint64_t offset,const std::string & filename)350 void VirtualRuntime::UpdatekernelMap(const uint64_t begin, const uint64_t end, const uint64_t offset,
351                                      const std::string &filename)
352 {
353     HLOGV("update kernel map name:'%s' 0x%" PRIx64 " - 0x%" PRIx64 "@0x%08" PRIx64 "",
354           filename.c_str(), begin, end, offset);
355 
356     HLOG_ASSERT(!filename.empty());
357     auto it = find(kernelSpaceMemMaps_.begin(), kernelSpaceMemMaps_.end(), filename);
358     if (it == kernelSpaceMemMaps_.end()) {
359         kernelSpaceMemMaps_.emplace_back(begin, end, offset, "", filename);
360     } else {
361         it->begin = begin;
362         it->end = end;
363         it->offset = offset;
364         it->name = filename;
365     }
366 }
367 
DedupFromRecord(PerfRecordSample * recordSample)368 void VirtualRuntime::DedupFromRecord(PerfRecordSample *recordSample)
369 {
370     CHECK_TRUE(recordSample != nullptr, NO_RETVAL, 0, "");
371     u64 nr = recordSample->data_.nr;
372     if (nr == 0) {
373         collectSymbolCallBack_(recordSample);
374         return;
375     }
376     u32 pid = recordSample->data_.pid;
377     u64 *ips = recordSample->data_.ips;
378     StackId stackId;
379     stackId.value = 0;
380     auto entry = processStackMap_.find(pid);
381     std::shared_ptr<UniqueStackTable> table = nullptr;
382     if (entry != processStackMap_.end()) {
383         table = entry->second;
384     } else {
385         table = std::make_shared<UniqueStackTable>(pid);
386         processStackMap_[pid] = table;
387     }
388     CHECK_TRUE(table != nullptr, NO_RETVAL, 0, "");
389     while (table->PutIpsInTable(&stackId, ips, nr) == 0) {
390         // try expand hashtable if collison can not resolved
391         if (!table->Resize()) {
392             HLOGW("Hashtable size limit, ip compress failed!");
393             collectSymbolCallBack_(recordSample);
394             return;
395         }
396     }
397     // callstack dedup success
398     recordSample->stackId_.value = stackId.value;
399     recordSample->header_.size -= (sizeof(u64) * nr - sizeof(stackId));
400     recordSample->data_.nr = 0;
401     recordSample->data_.ips = nullptr;
402     recordSample->removeStack_ = true;
403 }
404 
CollectDedupSymbol(kSymbolsHits & kernelSymbolsHits,uSymbolsHits & userSymbolsHits)405 void VirtualRuntime::CollectDedupSymbol(kSymbolsHits &kernelSymbolsHits,
406                                         uSymbolsHits &userSymbolsHits)
407 {
408     Node *node = nullptr;
409     Node *head = nullptr;
410     u32 pid;
411     for (const auto &tableEntry : processStackMap_) {
412         const auto &table = tableEntry.second;
413         if (table == nullptr) {
414             continue;
415         }
416         pid = table->GetPid();
417         head = table->GetHeadNode();
418         const auto &idxes = table->GetUsedIndexes();
419         for (const auto idx : idxes) {
420             node = head + idx;
421             if (node == nullptr) {
422                 continue;
423             }
424             if (node->value != 0) {
425                 if (node->section.inKernel) {
426                     uint64_t ip = node->section.ip | KERNEL_PREFIX;
427                     if (ip == PERF_CONTEXT_KERNEL || ip == PERF_CONTEXT_USER) {
428                         continue;
429                     }
430                     kernelSymbolsHits.insert(ip);
431                 } else {
432                     userSymbolsHits[pid].insert(node->section.ip);
433                 }
434             } else {
435                 HLOGD("node value error 0x%x", idx);
436             }
437         }
438     }
439 }
440 
UpdateFromRecord(PerfEventRecord & record)441 void VirtualRuntime::UpdateFromRecord(PerfEventRecord &record)
442 {
443 #ifdef HIPERF_DEBUG_TIME
444     const auto startTime = steady_clock::now();
445 #endif
446     if (record.GetType() == PERF_RECORD_SAMPLE) {
447         auto recordSample = static_cast<PerfRecordSample *>(&record);
448         UpdateFromRecord(*recordSample);
449 #ifdef HIPERF_DEBUG_TIME
450         processSampleRecordTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
451 #endif
452     } else if (record.GetType() == PERF_RECORD_MMAP) {
453         auto recordMmap = static_cast<PerfRecordMmap *>(&record);
454         UpdateFromRecord(*recordMmap);
455 #ifdef HIPERF_DEBUG_TIME
456         processMmapRecordTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
457 #endif
458     } else if (record.GetType() == PERF_RECORD_MMAP2) {
459         auto recordMmap2 = static_cast<PerfRecordMmap2 *>(&record);
460         UpdateFromRecord(*recordMmap2);
461 #ifdef HIPERF_DEBUG_TIME
462         processMmap2RecordTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
463 #endif
464     } else if (record.GetType() == PERF_RECORD_COMM) {
465         auto recordComm = static_cast<PerfRecordComm *>(&record);
466         UpdateFromRecord(*recordComm);
467 #ifdef HIPERF_DEBUG_TIME
468         processCommRecordTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
469 #endif
470     } else if (record.GetType() == PERF_RECORD_AUXTRACE) {
471         auto recordAuxTrace = static_cast<PerfRecordAuxtrace *>(&record);
472         UpdateFromRecord(*recordAuxTrace);
473 #ifdef HIPERF_DEBUG_TIME
474         processCommRecordTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
475 #endif
476     } else {
477         HLOGW("skip record type %d", record.GetType());
478     }
479 }
480 
MakeCallFrame(DfxSymbol & symbol,DfxFrame & callFrame)481 void VirtualRuntime::MakeCallFrame(DfxSymbol &symbol, DfxFrame &callFrame)
482 {
483     callFrame.funcOffset = symbol.funcVaddr_;
484     callFrame.mapOffset = symbol.offsetToVaddr_;
485     callFrame.symbolFileIndex = symbol.symbolFileIndex_;
486     callFrame.funcName = symbol.GetName();
487     if (callFrame.funcName.empty()) {
488         HLOGD("callFrame.funcName:%s, GetName:%s\n", callFrame.funcName.c_str(), symbol.GetName().data());
489     }
490 
491     callFrame.index = static_cast<size_t>(symbol.index_);
492     callFrame.mapName = symbol.module_.empty() ? symbol.comm_ : symbol.module_;
493     HLOG_ASSERT_MESSAGE(!callFrame.funcName.empty(), "%s", symbol.ToDebugString().c_str());
494 }
495 
SymbolicCallFrame(PerfRecordSample & recordSample,const uint64_t ip,const pid_t serverPid,const perf_callchain_context context)496 void VirtualRuntime::SymbolicCallFrame(PerfRecordSample &recordSample, const uint64_t ip,
497                                        const pid_t serverPid, const perf_callchain_context context)
498 {
499     pid_t pid = static_cast<pid_t>(recordSample.data_.pid);
500     pid_t tid = static_cast<pid_t>(recordSample.data_.tid);
501     if (serverPid != pid) {
502         pid = tid = serverPid;
503     }
504     auto symbol = GetSymbol(ip, pid, tid, context);
505     MakeCallFrame(symbol, recordSample.callFrames_.emplace_back(ip, 0));
506     HLOGV(" (%zu)unwind symbol: %*s%s", recordSample.callFrames_.size(),
507           static_cast<int>(recordSample.callFrames_.size()), "",
508           recordSample.callFrames_.back().ToSymbolString().c_str());
509 }
510 
RecoverCallStack(PerfRecordSample & recordSample)511 bool VirtualRuntime::RecoverCallStack(PerfRecordSample &recordSample)
512 {
513     auto StackTable = processStackMap_.find(recordSample.data_.pid);
514     CHECK_TRUE(StackTable != processStackMap_.end(), false, 1, "not found %" PRIu32 " pid", recordSample.data_.pid);
515     recordSample.ips_.clear();
516     if (StackTable->second != nullptr) {
517         StackTable->second->GetIpsByStackId(recordSample.stackId_, recordSample.ips_);
518     }
519     recordSample.RecoverCallStack();
520     return true;
521 }
522 
SymbolicRecord(PerfRecordSample & recordSample)523 void VirtualRuntime::SymbolicRecord(PerfRecordSample &recordSample)
524 {
525 #ifdef HIPERF_DEBUG_TIME
526     const auto startTime = steady_clock::now();
527 #endif
528     // Symbolic the Call Stack
529     recordSample.callFrames_.clear();
530     perf_callchain_context context = PERF_CONTEXT_MAX;
531     pid_t serverPid;
532     if (recordSample.data_.nr == 0) {
533         serverPid = recordSample.GetServerPidof(0);
534         SymbolicCallFrame(recordSample, recordSample.data_.ip, serverPid, PERF_CONTEXT_MAX);
535     }
536     for (u64 i = 0; i < recordSample.data_.nr; i++) {
537         uint64_t ip = recordSample.data_.ips[i];
538         if (ip >= PERF_CONTEXT_MAX) {
539             std::string contextName = UpdatePerfContext(ip, context);
540             HLOGV("context switch to %s", contextName.c_str());
541             continue;
542         } else if (ip < BAD_IP_ADDRESS) {
543             // ip 0 or 1 or less than 0
544             continue;
545         }
546         serverPid = recordSample.GetServerPidof(i);
547         SymbolicCallFrame(recordSample, ip, serverPid, context);
548     }
549 #ifdef HIPERF_DEBUG_TIME
550     auto usedTime = duration_cast<microseconds>(steady_clock::now() - startTime);
551     if (usedTime.count() != 0) {
552         HLOGV("cost %0.3f ms to symbolic ", usedTime.count() / MS_DURATION);
553     }
554     symbolicRecordTimes_ += usedTime;
555 #endif
556 }
557 
NeedDropKernelCallChain(PerfRecordSample & sample)558 void VirtualRuntime::NeedDropKernelCallChain(PerfRecordSample &sample)
559 {
560     // only do this in record mode.
561     if (recordCallBack_ == nullptr || needkernelCallChain_ ||
562         !sample.InKernel() || sample.data_.nr == 0) {
563         return;
564     }
565 
566     u64 skip = 0;
567     u64 skipPid = 0;
568     u64 *ips = sample.data_.ips;
569     for (; skip < sample.data_.nr; skip++) {
570         if (ips[skip] == PERF_CONTEXT_KERNEL) {
571             skipPid++;
572         }
573         if (ips[skip] == PERF_CONTEXT_USER) {
574             break;
575         }
576     }
577     sample.skipKernel_ = skip;
578     sample.data_.nr -= skip;
579     sample.header_.size -= sizeof(u64) * skip;
580     if (sample.data_.server_nr > 0) {
581         sample.skipPid_ = skipPid;
582         sample.data_.server_nr -= skipPid;
583         sample.header_.size -= sizeof(u64) * skipPid;
584     }
585 }
586 
AdjustCallChain(PerfRecordSample & sample)587 void VirtualRuntime::AdjustCallChain(PerfRecordSample &sample)
588 {
589 #if defined(is_ohos) && is_ohos
590     if (!isHM_ || recordCallBack_ == nullptr) {
591         return;
592     }
593     constexpr uint64_t minValue = 0x5;
594     constexpr uint64_t offset = 0x4;
595     for (u64 i = 0; i < sample.data_.nr; i++) {
596         if (sample.data_.ips[i] >= PERF_CONTEXT_MAX) {
597             i++;
598             continue;
599         }
600         if (i >= 1 && sample.data_.ips[i] >= minValue) {
601             sample.data_.ips[i] -= offset;
602         }
603     }
604 #endif
605 }
606 
ProcessKernelCallChain(PerfRecordSample & sample)607 void VirtualRuntime::ProcessKernelCallChain(PerfRecordSample &sample)
608 {
609 #if defined(is_ohos) && is_ohos
610     if (isRoot_) {
611         return;
612     }
613     if (recordCallBack_ != nullptr) {
614         if (sample.data_.ip >= 0xffff000000000000) {
615             sample.data_.ip = sample.data_.ip & 0xffffff0000000fff;
616         }
617         for (u64 i = 0; i < sample.data_.nr; i++) {
618             if (sample.data_.ips[i] >= PERF_CONTEXT_MAX) {
619                 continue;
620             }
621             if (sample.data_.ips[i] >= 0xffff000000000000) {
622                 sample.data_.ips[i] = sample.data_.ips[i] & 0xffffff0000000fff;
623             }
624         }
625     }
626 #endif
627 }
628 
UnwindFromRecord(PerfRecordSample & recordSample)629 void VirtualRuntime::UnwindFromRecord(PerfRecordSample &recordSample)
630 {
631 #if defined(is_ohos) && is_ohos
632 #ifdef HIPERF_DEBUG_TIME
633     const auto startTime = steady_clock::now();
634 #endif
635     HLOGV("unwind record (time:%llu)", recordSample.data_.time);
636     // if we have userstack ?
637     if (recordSample.data_.stack_size > 0) {
638         pid_t serverPid = recordSample.GetUstackServerPid();
639         pid_t pid = static_cast<pid_t>(recordSample.data_.pid);
640         pid_t tid = static_cast<pid_t>(recordSample.data_.tid);
641         if (serverPid != pid) {
642             pid = tid = serverPid;
643         }
644         auto &thread = UpdateThread(pid, tid);
645         callstack_.UnwindCallStack(thread, recordSample.data_.user_abi == PERF_SAMPLE_REGS_ABI_32,
646                                    recordSample.data_.user_regs, recordSample.data_.reg_nr,
647                                    recordSample.data_.stack_data, recordSample.data_.dyn_size,
648                                    recordSample.callFrames_);
649 #ifdef HIPERF_DEBUG_TIME
650         unwindCallStackTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
651 #endif
652         size_t oldSize = recordSample.callFrames_.size();
653         HLOGV("unwind %zu", recordSample.callFrames_.size());
654         callstack_.ExpandCallStack(thread.tid_, recordSample.callFrames_, callstackMergeLevel_);
655         HLOGV("expand %zu (+%zu)", recordSample.callFrames_.size(),
656               recordSample.callFrames_.size() - oldSize);
657 
658         recordSample.ReplaceWithCallStack(oldSize);
659     }
660 
661 #ifdef HIPERF_DEBUG_TIME
662     unwindFromRecordTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
663 #endif
664 
665     NeedDropKernelCallChain(recordSample);
666     // we will not do this in non record mode.
667     if (dedupStack_ && recordCallBack_ != nullptr) {
668         DedupFromRecord(&recordSample);
669     }
670 #endif
671 
672     // we will not do this in record mode
673     if (recordCallBack_ == nullptr) {
674         if (dedupStack_ && recordSample.stackId_.section.id > 0 && recordSample.data_.nr == 0) {
675             RecoverCallStack(recordSample);
676         }
677         // find the symbols , reabuild frame info
678         SymbolicRecord(recordSample);
679     }
680 }
681 
SetCollectSymbolCallBack(const CollectSymbolCallBack & collectSymbolCallBack)682 void VirtualRuntime::SetCollectSymbolCallBack(const CollectSymbolCallBack &collectSymbolCallBack)
683 {
684     collectSymbolCallBack_ = collectSymbolCallBack;
685 }
686 
UpdateFromRecord(PerfRecordSample & recordSample)687 void VirtualRuntime::UpdateFromRecord(PerfRecordSample &recordSample)
688 {
689     UpdateThread(recordSample.data_.pid, recordSample.data_.tid);
690     if (recordSample.data_.server_nr) {
691         // update all server threads
692         for (size_t i = 0; i < recordSample.data_.server_nr; i++) {
693             pid_t pid = static_cast<pid_t>(recordSample.data_.server_pids[i]);
694             UpdateThread(pid, pid);
695         }
696     }
697     AdjustCallChain(recordSample);
698     ProcessKernelCallChain(recordSample);
699     // unwind
700     if (disableUnwind_) {
701         return;
702     }
703     UnwindFromRecord(recordSample);
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(const RecordCallBack & recordCallBack)909 void VirtualRuntime::SetRecordMode(const RecordCallBack &recordCallBack)
910 {
911     recordCallBack_ = recordCallBack;
912 }
913 
UpdateSymbols(std::shared_ptr<DfxMap> map,const pid_t pid)914 void VirtualRuntime::UpdateSymbols(std::shared_ptr<DfxMap> map, const 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      * segN      .hap                                     r--p    .an/jit/etc
936      * 1.map.name == symbolsFile.filePath_
937      * 2.map.FileType == symbolsFiles_[map.symbolFileIndex]
938      * 3.cache pc->map->symbolsFiles[map.symbolFileIndex]
939      * 4.must ensure map.mapType assigned with SymbolsFile constructions at the same time.
940     */
941     auto symbolsFile = SymbolsFile::CreateSymbolsFile(map->name, pid);
942     symbolsFile->SetMapsInfo(map);
943     if (enableDebugInfoSymbolic_ && symbolsFile->symbolFileType_ == SymbolsFileType::SYMBOL_ELF_FILE) {
944         symbolsFile->EnableMiniDebugInfo();
945     }
946     // set symbol path If it exists
947     if (symbolsPaths_.size() > 0) {
948         // also load from search path
949         symbolsFile->setSymbolsFilePath(symbolsPaths_);
950     }
951     if (loadSymboleWhenNeeded_) {
952         // load it when we need it
953         symbolsFiles_.emplace_back(std::move(symbolsFile));
954     } else if (symbolsFile->LoadSymbols()) {
955         symbolsFiles_.emplace_back(std::move(symbolsFile));
956     } else {
957         HLOGW("symbols file for '%s' not found.", map->name.c_str());
958     }
959 #ifdef HIPERF_DEBUG_TIME
960     auto usedTime = duration_cast<microseconds>(steady_clock::now() - startTime);
961     if (usedTime.count() != 0) {
962         HLOGV("cost %0.3f ms to load '%s'", usedTime.count() / MS_DURATION, map->name.c_str());
963     }
964     updateSymbolsTimes_ += usedTime;
965 #endif
966 }
967 
GetKernelSymbol(const uint64_t ip,const std::vector<DfxMap> & memMaps,const VirtualThread & thread)968 const DfxSymbol VirtualRuntime::GetKernelSymbol(const uint64_t ip, const std::vector<DfxMap> &memMaps,
969                                                 const VirtualThread &thread)
970 {
971     DfxSymbol vaddrSymbol(ip, thread.name_);
972     for (auto &map : memMaps) {
973         if (ip > map.begin && ip < map.end) {
974             HLOGM("found addr 0x%" PRIx64 " in kernel map 0x%" PRIx64 " - 0x%" PRIx64 " from %s",
975                   ip, map.begin, map.end, map.name.c_str());
976             vaddrSymbol.module_ = map.name;
977             // found symbols by file name
978             for (auto &symbolsFile : symbolsFiles_) {
979                 if (symbolsFile->filePath_ == map.name) {
980                     vaddrSymbol.symbolFileIndex_ = symbolsFile->id_;
981                     vaddrSymbol.fileVaddr_ =
982                         symbolsFile->GetVaddrInSymbols(ip, map.begin, map.offset);
983                     perf_callchain_context context = PERF_CONTEXT_KERNEL;
984                     if (GetSymbolCache(vaddrSymbol.fileVaddr_, vaddrSymbol, context)) {
985                         return vaddrSymbol;
986                     }
987                     HLOGV("found symbol vaddr 0x%" PRIx64 " for runtime vaddr 0x%" PRIx64
988                           " at '%s'",
989                           vaddrSymbol.fileVaddr_, ip, map.name.c_str());
990                     if (!symbolsFile->SymbolsLoaded()) {
991                         symbolsFile->LoadSymbols();
992                     }
993                     DfxSymbol foundSymbols = symbolsFile->GetSymbolWithVaddr(vaddrSymbol.fileVaddr_);
994                     foundSymbols.taskVaddr_ = ip;
995                     if (!foundSymbols.IsValid()) {
996                         HLOGW("addr 0x%" PRIx64 " vaddr  0x%" PRIx64 " NOT found in symbol file %s",
997                               ip, vaddrSymbol.fileVaddr_, map.name.c_str());
998                         return vaddrSymbol;
999                     } else {
1000                         return foundSymbols;
1001                     }
1002                 }
1003             }
1004             HLOGW("addr 0x%" PRIx64 " in map but NOT found the symbol file %s", ip,
1005                   map.name.c_str());
1006         } else {
1007             HLOGM("addr 0x%" PRIx64 " not in map 0x%" PRIx64 " - 0x%" PRIx64 " from %s", ip,
1008                   map.begin, map.end, map.name.c_str());
1009         }
1010     }
1011     return vaddrSymbol;
1012 }
1013 
GetKernelThreadSymbol(const uint64_t ip,const VirtualThread & thread)1014 const DfxSymbol VirtualRuntime::GetKernelThreadSymbol(const uint64_t ip, const VirtualThread &thread)
1015 {
1016     DfxSymbol vaddrSymbol(ip, thread.name_);
1017     int64_t mapIndex = thread.FindMapIndexByAddr(ip);
1018     if (mapIndex < 0) {
1019         HLOGV("not found in any map");
1020         return vaddrSymbol;
1021     }
1022 
1023     auto map = thread.GetMaps()[mapIndex];
1024     CHECK_TRUE(map != nullptr, vaddrSymbol, 0, "");
1025     HLOGM("found addr 0x%" PRIx64 " in kthread map 0x%" PRIx64 " - 0x%" PRIx64 " from %s",
1026           ip, map->begin, map->end, map->name.c_str());
1027     // found symbols by file name
1028     for (auto &symbolsFile : symbolsFiles_) {
1029         if (symbolsFile->filePath_ == map->name) {
1030             vaddrSymbol.symbolFileIndex_ = symbolsFile->id_;
1031             vaddrSymbol.module_ = map->name;
1032             vaddrSymbol.fileVaddr_ =
1033                 symbolsFile->GetVaddrInSymbols(ip, map->begin, map->offset);
1034             perf_callchain_context context = PERF_CONTEXT_MAX;
1035             if (GetSymbolCache(vaddrSymbol.fileVaddr_, vaddrSymbol, context)) {
1036                 return vaddrSymbol;
1037             }
1038             HLOGV("found symbol vaddr 0x%" PRIx64 " for runtime vaddr 0x%" PRIx64 " at '%s'",
1039                   vaddrSymbol.fileVaddr_, ip, map->name.c_str());
1040             if (!symbolsFile->SymbolsLoaded()) {
1041                 symbolsFile->LoadDebugInfo();
1042                 symbolsFile->LoadSymbols(map);
1043             }
1044             DfxSymbol foundSymbols;
1045             if (thread.pid_ == devhostPid_ && recordCallBack_ != nullptr) {
1046                 foundSymbols = symbolsFile->GetSymbolWithPcAndMap(vaddrSymbol.fileVaddr_, map);
1047             } else {
1048                 foundSymbols = symbolsFile->GetSymbolWithVaddr(vaddrSymbol.fileVaddr_);
1049             }
1050 
1051             foundSymbols.taskVaddr_ = ip;
1052             if (!foundSymbols.IsValid()) {
1053                 HLOGW("addr 0x%" PRIx64 " vaddr  0x%" PRIx64 " NOT found in symbol file %s",
1054                       ip, vaddrSymbol.fileVaddr_, map->name.c_str());
1055                 return vaddrSymbol;
1056             }
1057             return foundSymbols;
1058         }
1059     }
1060     HLOGW("addr 0x%" PRIx64 " in map but NOT found the symbol file %s", ip, map->name.c_str());
1061     return vaddrSymbol;
1062 }
1063 
GetUserSymbol(const uint64_t ip,const VirtualThread & thread)1064 const DfxSymbol VirtualRuntime::GetUserSymbol(const uint64_t ip, const VirtualThread &thread)
1065 {
1066     DfxSymbol vaddrSymbol(ip, thread.name_);
1067     int64_t mapIndex = thread.FindMapIndexByAddr(ip);
1068     if (mapIndex < 0) {
1069         HLOGV("not found in any map");
1070         return vaddrSymbol;
1071     }
1072     auto map = thread.GetMaps()[mapIndex];
1073     SymbolsFile *symbolsFile = thread.FindSymbolsFileByMap(map);
1074     if (symbolsFile == nullptr) {
1075         HLOGW("addr 0x%" PRIx64 " in map but NOT found the symbol file %s", ip, map->name.c_str());
1076         return vaddrSymbol;
1077     }
1078     vaddrSymbol.symbolFileIndex_ = symbolsFile->id_;
1079     vaddrSymbol.module_ = map->name;
1080     vaddrSymbol.fileVaddr_ = symbolsFile->GetVaddrInSymbols(ip, map->begin, map->offset);
1081     perf_callchain_context context = PERF_CONTEXT_USER;
1082     if (GetSymbolCache(vaddrSymbol.fileVaddr_, vaddrSymbol, context)) {
1083         return vaddrSymbol;
1084     }
1085     HLOGV("found symbol vaddr 0x%" PRIx64 " for runtime vaddr 0x%" PRIx64 " at '%s'",
1086           vaddrSymbol.fileVaddr_, ip, map->name.c_str());
1087     if (!symbolsFile->SymbolsLoaded()) {
1088         symbolsFile->LoadDebugInfo(map);
1089         symbolsFile->LoadSymbols(map);
1090     }
1091     DfxSymbol foundSymbols;
1092     if (!symbolsFile->IsAbc() && !IsV8File(map->name)) {
1093         foundSymbols = symbolsFile->GetSymbolWithVaddr(vaddrSymbol.fileVaddr_);
1094     } else {
1095         HLOGD("symbolsFile:%s is ABC or V8 :%d", symbolsFile->filePath_.c_str(),
1096               symbolsFile->IsAbc() || symbolsFile->IsV8());
1097         foundSymbols = symbolsFile->GetSymbolWithPcAndMap(ip, map);
1098     }
1099 
1100     if (foundSymbols.IsValid()) {
1101         return foundSymbols;
1102     }
1103     HLOGW("addr 0x%" PRIx64 " vaddr  0x%" PRIx64 " NOT found in symbol file %s", ip,
1104           vaddrSymbol.fileVaddr_, map->name.c_str());
1105     if (symbolsFile->IsAbc() || symbolsFile->IsV8()) {
1106         symbolsFile->symbolsMap_.insert(std::make_pair(ip, vaddrSymbol));
1107     }
1108     return vaddrSymbol;
1109 }
1110 
GetSymbolCache(const uint64_t fileVaddr,DfxSymbol & symbol,const perf_callchain_context & context)1111 bool VirtualRuntime::GetSymbolCache(const 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(const uint64_t ip,const pid_t pid,const pid_t tid,const perf_callchain_context & context)1144 DfxSymbol VirtualRuntime::GetSymbol(const uint64_t ip, const pid_t pid, const pid_t tid,
1145                                     const perf_callchain_context &context)
1146 {
1147     HLOGV("try find tid %u ip 0x%" PRIx64 " in %zu symbolsFiles\n", tid, ip, symbolsFiles_.size());
1148     DfxSymbol symbol;
1149 
1150     if (IsKernelThread(pid)) {
1151         VirtualThread &kthread = GetThread(pid, tid);
1152         HLOGM("try found addr in kernel thread %u with %zu maps", pid,
1153               kthread.GetMaps().size());
1154         symbol = GetKernelThreadSymbol(ip, kthread);
1155         HLOGM("add addr to kernel thread cache 0x%" PRIx64 " cache size %zu", ip,
1156               kThreadSymbolCache_.size());
1157         kThreadSymbolCache_[symbol.fileVaddr_] = symbol;
1158         if (symbol.IsValid()) {
1159             return symbol;
1160         }
1161     }
1162 
1163     if (context == PERF_CONTEXT_USER || (context == PERF_CONTEXT_MAX && !symbol.IsValid())) {
1164         // check userspace memmap
1165         symbol = GetUserSymbol(ip, GetThread(pid, tid));
1166         if (userSymbolCache_.find(symbol.fileVaddr_) == userSymbolCache_.end()) {
1167             userSymbolCache_.reserve(USER_SYMBOL_CACHE_LIMIT);
1168         }
1169         userSymbolCache_[symbol.fileVaddr_] = symbol;
1170         HLOGV("cache ip 0x%" PRIx64 " to %s", ip,
1171               userSymbolCache_[symbol.fileVaddr_].ToDebugString().c_str());
1172     }
1173 
1174     if (context == PERF_CONTEXT_KERNEL || (context == PERF_CONTEXT_MAX && !symbol.IsValid())) {
1175         // check kernelspace
1176         HLOGM("try found addr in kernelspace %zu maps", kernelSpaceMemMaps_.size());
1177         symbol = GetKernelSymbol(ip, kernelSpaceMemMaps_, GetThread(pid, tid));
1178         HLOGM("add addr to kernel cache 0x%" PRIx64 " cache size %zu", ip,
1179               kernelSymbolCache_.size());
1180         kernelSymbolCache_[symbol.fileVaddr_] = symbol;
1181     }
1182     return symbol;
1183 }
1184 
SetSymbolsPaths(const std::vector<std::string> & symbolsPaths)1185 bool VirtualRuntime::SetSymbolsPaths(const std::vector<std::string> &symbolsPaths)
1186 {
1187     std::unique_ptr<SymbolsFile> symbolsFile = SymbolsFile::CreateSymbolsFile(SYMBOL_UNKNOW_FILE);
1188     CHECK_TRUE(symbolsFile != nullptr, false, 0, "");
1189     // we need check if the path is accessible
1190     bool accessible = symbolsFile->setSymbolsFilePath(symbolsPaths);
1191     if (accessible) {
1192         symbolsPaths_ = symbolsPaths;
1193     } else {
1194         if (!symbolsPaths.empty()) {
1195             HLOGE("some symbols path unable access");
1196         }
1197     }
1198     return accessible;
1199 }
1200 
UpdateFromPerfData(const std::vector<SymbolFileStruct> & symbolFileStructs)1201 void VirtualRuntime::UpdateFromPerfData(const std::vector<SymbolFileStruct> &symbolFileStructs)
1202 {
1203     // review: if we need move to some other place ?
1204     HLOG_ASSERT_MESSAGE(symbolsFiles_.size() == 0, " symbolsFiles_ size is %zu",
1205                         symbolsFiles_.size());
1206     for (const auto &symbolFileStruct : symbolFileStructs) {
1207         HLOGV("symbolFileStruct.filePath_:'%s'", symbolFileStruct.filePath_.c_str());
1208         HLOGV("symbolFileStruct.buildId_:'%s'", symbolFileStruct.buildId_.c_str());
1209         HLOGV("process symbols file:'%s':'%s'", symbolFileStruct.filePath_.c_str(),
1210               symbolFileStruct.buildId_.c_str());
1211 
1212         // load from symbolFileStruct (perf.data)
1213         std::unique_ptr<SymbolsFile> symbolsFile = SymbolsFile::LoadSymbolsFromSaved(symbolFileStruct);
1214         if (symbolsFile == nullptr) {
1215             continue;
1216         }
1217         // reaload from sybol path If it exists
1218         if (symbolsPaths_.size() > 0) {
1219             HLOGV("try again with symbolsPaths setup");
1220             symbolsFile->setSymbolsFilePath(symbolsPaths_); // also load from search path
1221             // use give us path ,we must reload it.
1222             symbolsFile->LoadSymbols();
1223         }
1224         symbolsFile->id_ = static_cast<int32_t>(symbolsFiles_.size());
1225         symbolsFiles_.emplace_back(std::move(symbolsFile));
1226     }
1227 }
1228 
ImportUniqueStackNodes(const std::vector<UniStackTableInfo> & uniStackTableInfos)1229 void VirtualRuntime::ImportUniqueStackNodes(const std::vector<UniStackTableInfo>& uniStackTableInfos)
1230 {
1231     for (const UniStackTableInfo& item : uniStackTableInfos) {
1232         auto stackTable = std::make_shared<UniqueStackTable>(item.pid, item.tableSize);
1233         for (const UniStackNode& node : item.nodes) {
1234             stackTable->ImportNode(node.index, node.node);
1235         }
1236         processStackMap_[item.pid] = std::move(stackTable);
1237     }
1238 }
1239 
1240 /*
1241    ARM functions
1242        The table below lists the symbols exported by the vDSO.
1243 
1244        symbol                 version
1245        ────────────────────────────────────────────────────────────
1246        __vdso_gettimeofday    LINUX_2.6 (exported since Linux 4.1)
1247        __vdso_clock_gettime   LINUX_2.6 (exported since Linux 4.1)
1248 
1249        Additionally, the ARM port has a code page full of utility
1250        functions.  Since it's just a raw page of code, there is no ELF
1251        information for doing symbol lookups or versioning.  It does
1252        provide support for different versions though.
1253 
1254        For information on this code page, it's best to refer to the
1255        kernel documentation as it's extremely detailed and covers
1256        everything you need to know:
1257        Documentation/arm/kernel_user_helpers.txt.
1258 
1259    aarch64 functions
1260        The table below lists the symbols exported by the vDSO.
1261 
1262        symbol                   version
1263        ──────────────────────────────────────
1264        __kernel_rt_sigreturn    LINUX_2.6.39
1265        __kernel_gettimeofday    LINUX_2.6.39
1266        __kernel_clock_gettime   LINUX_2.6.39
1267        __kernel_clock_getres    LINUX_2.6.39
1268 */
LoadVdso()1269 void VirtualRuntime::LoadVdso()
1270 {
1271 #if defined(is_ohos) && is_ohos
1272     VirtualThread myThread(getpid(), symbolsFiles_);
1273     myThread.ParseMap();
1274     for (const auto &map : myThread.GetMaps()) {
1275         if (map->IsVdsoMap()) {
1276             std::string memory(map->end - map->begin, '\0');
1277             std::copy(reinterpret_cast<char *>((map->begin)), reinterpret_cast<char *>((map->end)),
1278                       &memory[0]);
1279             std::string tempPath("/data/log/hiperflog/");
1280             if (!IsDirectoryExists(tempPath)) {
1281                 HIPERF_HILOGI(MODULE_DEFAULT, "%{public}s not exist.", tempPath.c_str());
1282                 CHECK_TRUE(CreateDirectory(tempPath, HIPERF_FILE_PERM_770), NO_RETVAL,
1283                            LOG_TYPE_WITH_HILOG, "Create hiperflog path failed");
1284             }
1285             std::string tempFileName = tempPath + map->name;
1286             if (!WriteStringToFile(tempFileName, memory)) {
1287                 printf("vdso temp file create fail at %s\n", tempFileName.c_str());
1288             } else {
1289                 HLOGD("vdso temp file create at %s:%zu", tempFileName.c_str(), memory.size());
1290                 auto symbolsFile = SymbolsFile::CreateSymbolsFile(map->name);
1291                 symbolsFile->setSymbolsFilePath(tempPath); // also load from search path
1292                 symbolsFiles_.emplace_back(std::move(symbolsFile));
1293                 return;
1294             }
1295         }
1296     }
1297     HLOGD("no vdso found");
1298 #endif
1299 }
1300 
UpdateServiceSpaceMaps()1301 void VirtualRuntime::UpdateServiceSpaceMaps()
1302 {
1303     VirtualThread &kthread = GetThread(SYSMGR_PID, SYSMGR_PID);
1304     kthread.ParseServiceMap(SYSMGR_FILE_NAME);
1305     if (recordCallBack_) {
1306         if (isRoot_) {
1307             for (const auto &map : kthread.GetMaps()) {
1308                 PerfRecordMmap record(true, SYSMGR_PID, SYSMGR_PID,
1309                                       map->begin, map->end - map->begin,
1310                                       0, SYSMGR_FILE_NAME);
1311                 recordCallBack_(record);
1312             }
1313         }
1314     }
1315 }
1316 
UpdateServiceSymbols()1317 void VirtualRuntime::UpdateServiceSymbols()
1318 {
1319     HLOGD("try to update kernel thread symbols for kernel service");
1320     std::string fileName = SYSMGR_FILE_NAME;
1321     auto symbolsFile = SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_THREAD_FILE, fileName);
1322 
1323     HLOGD("add kernel service symbol file: %s", fileName.c_str());
1324     if (symbolsFile->LoadSymbols()) {
1325         symbolsFiles_.emplace_back(std::move(symbolsFile));
1326     } else {
1327         HLOGW("symbols file for '%s' not found.", fileName.c_str());
1328     }
1329 }
1330 
UpdateKernelThreadMap(const pid_t pid,const uint64_t begin,const uint64_t len,const uint64_t offset,const std::string & filename)1331 void VirtualRuntime::UpdateKernelThreadMap(const pid_t pid, const uint64_t begin, const uint64_t len,
1332                                            const uint64_t offset, const std::string &filename)
1333 {
1334     HLOGV("update kernel thread map pid %u offset 0x%" PRIx64 " name:'%s'", pid, offset, filename.c_str());
1335 
1336     VirtualThread &thread = GetThread(pid, pid);
1337     thread.CreateMapItem(filename, begin, len, offset);
1338 }
1339 
UpdateDevhostSpaceMaps()1340 void VirtualRuntime::UpdateDevhostSpaceMaps()
1341 {
1342     VirtualThread &kthread = GetThread(devhostPid_, devhostPid_);
1343     kthread.ParseDevhostMap(devhostPid_);
1344     if (recordCallBack_) {
1345         for (const auto &map : kthread.GetMaps()) {
1346             auto record =
1347             std::make_unique<PerfRecordMmap>(false, devhostPid_, devhostPid_,
1348                                              map->begin, map->end - map->begin,
1349                                              map->offset, map->name);
1350             recordCallBack_(*record);
1351         }
1352     }
1353 }
1354 
UpdateDevhostSymbols()1355 void VirtualRuntime::UpdateDevhostSymbols()
1356 {
1357     HLOGD("try to update kernel thread symbols for devhost");
1358     auto kallsyms = SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_THREAD_FILE, DEVHOST_FILE_NAME);
1359     // file name of devhost.ko
1360     std::map<std::string_view, std::unique_ptr<SymbolsFile>> koMaps;
1361     koMaps[DEVHOST_FILE_NAME] =
1362         SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_THREAD_FILE, DEVHOST_LINUX_FILE_NAME);
1363 
1364     if (kallsyms->LoadSymbols()) {
1365         for (auto &symbol : kallsyms->GetSymbols()) {
1366             if (koMaps.find(symbol.module_) == koMaps.end()) {
1367                 std::string filename = std::string(symbol.module_);
1368                 // [devhost] to /liblinux/devhost.ko
1369                 filename.erase(filename.begin());
1370                 filename.erase(filename.end() - 1);
1371                 filename = DEVHOST_LINUX_PREFIX + filename + KERNEL_MODULES_EXT_NAME;
1372                 koMaps[symbol.module_] =
1373                     SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_THREAD_FILE, filename);
1374             }
1375             if (koMaps[symbol.module_] == nullptr) {
1376                 continue;
1377             }
1378             koMaps[symbol.module_]->AddSymbol(std::move(symbol));
1379         }
1380 
1381         HLOGD("devhost loaded %zu symbolfiles", koMaps.size());
1382         for (auto &it : koMaps) {
1383             if (it.second == nullptr) {
1384                 continue;
1385             }
1386             HLOGD("Load %zu symbols to %s", it.second->GetSymbols().size(),
1387                   it.second->filePath_.c_str());
1388             symbolsFiles_.emplace_back(std::move(it.second));
1389         }
1390     } else {
1391         HLOGW("symbols file for devhost parse failed.");
1392     }
1393 
1394     // update normal symbole files
1395     VirtualThread &kthread = GetThread(devhostPid_, devhostPid_);
1396     for (const auto &map : kthread.GetMaps()) {
1397         UpdateSymbols(map, devhostPid_);
1398     }
1399 }
1400 
FixHMBundleMmap(char * filename,const int pid,u16 & headerSize)1401 void VirtualRuntime::FixHMBundleMmap(char *filename, const int pid, u16 &headerSize)
1402 {
1403     if (!isHM_) {
1404         return;
1405     }
1406     // fix bundle path in mmap
1407     std::string newFilename = filename;
1408     VirtualThread &thread = GetThread(pid, pid);
1409     if (NeedAdaptHMBundlePath(newFilename, thread.name_)) {
1410         size_t oldSize = strlen(filename);
1411         if (memset_s(filename, KILO, '\0', KILO) != EOK) {
1412             HLOGD("memset_s failed in FixHMBundleMmap.");
1413         }
1414         if (strncpy_s(filename, KILO, newFilename.c_str(), newFilename.size()) != 0) {
1415             HLOGD("strncpy_s recordMmap2 failed!");
1416         }
1417         headerSize += newFilename.size() - oldSize;
1418     }
1419 }
1420 
SetDevhostPid(const pid_t devhost)1421 void VirtualRuntime::SetDevhostPid(const pid_t devhost)
1422 {
1423     HLOGD("Set devhost pid: %d", devhost);
1424     devhostPid_ = devhost;
1425 }
1426 
IsKernelThread(const pid_t pid)1427 bool VirtualRuntime::IsKernelThread(const pid_t pid)
1428 {
1429     if (!isHM_) {
1430         return false;
1431     }
1432     return pid == SYSMGR_PID || pid == devhostPid_;
1433 }
1434 
ClearSymbolCache()1435 void VirtualRuntime::ClearSymbolCache()
1436 {
1437     userSpaceThreadMap_.clear();
1438     kernelSpaceMemMaps_.clear();
1439     processStackMap_.clear();
1440     symbolsFiles_.clear();
1441     userSymbolCache_.clear();
1442     kernelSymbolCache_.clear();
1443     kThreadSymbolCache_.clear();
1444     symbolsPaths_.clear();
1445 
1446 #if defined(is_ohos) && is_ohos
1447     callstack_.ClearCache();
1448 #endif
1449 }
1450 
1451 } // namespace HiPerf
1452 } // namespace Developtools
1453 } // namespace OHOS
1454