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