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