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