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 "register.h"
29 #include "symbols_file.h"
30 #include "utilities.h"
31
32 using namespace std::chrono;
33 namespace OHOS {
34 namespace Developtools {
35 namespace HiPerf {
36 namespace {
37 // if ip is 0 , 1 both not useful
38 const uint64_t BAD_IP_ADDRESS = 2;
39 }
40 // we unable to access 'swapper' from /proc/0/
VirtualRuntime(bool onDevice)41 VirtualRuntime::VirtualRuntime(bool onDevice)
42 {
43 UpdateThread(0, 0, "swapper");
44 }
45
~VirtualRuntime()46 VirtualRuntime::~VirtualRuntime()
47 {
48 if (savedCmdLines_.is_open()) {
49 savedCmdLines_.close();
50 }
51 }
52
ReadFromSavedCmdLines(pid_t tid)53 std::string VirtualRuntime::ReadFromSavedCmdLines(pid_t tid)
54 {
55 if (!savedCmdLines_.is_open()) {
56 savedCmdLines_.open(SAVED_CMDLINES, std::ios::in);
57 }
58 if (!savedCmdLines_.is_open()) {
59 return EMPTY_STRING;
60 }
61 savedCmdLines_.seekg(0, std::ios::beg);
62 std::string line;
63 std::string threadid = std::to_string(tid);
64 while (getline(savedCmdLines_, line)) {
65 if (line.find(threadid) != std::string::npos) {
66 constexpr size_t sizeLimit {2};
67 std::vector<std::string> linesToken = StringSplit(line, " ");
68 if (linesToken.size() < sizeLimit) {
69 return EMPTY_STRING;
70 }
71 if (threadid != linesToken[0]) {
72 continue;
73 }
74 return linesToken[1];
75 }
76 }
77 return EMPTY_STRING;
78 }
79
ReadThreadName(pid_t tid,bool isThread)80 std::string VirtualRuntime::ReadThreadName(pid_t tid, bool isThread)
81 {
82 std::string comm = "";
83 if (tid == SYSMGR_PID) {
84 comm = SYSMGR_NAME;
85 } else if (tid == devhostPid_) {
86 comm = DEVHOST_FILE_NAME;
87 } else if (isThread) {
88 comm = ReadFileToString(StringPrintf("/proc/%d/comm", tid)).c_str();
89 } else {
90 comm = ReadFileToString(StringPrintf("/proc/%d/cmdline", tid)).c_str();
91 }
92 if (comm == EMPTY_STRING) {
93 comm = ReadFromSavedCmdLines(tid);
94 }
95 comm.erase(std::remove(comm.begin(), comm.end(), '\r'), comm.end());
96 comm.erase(std::remove(comm.begin(), comm.end(), '\n'), comm.end());
97 return comm;
98 }
99
UpdateThread(pid_t pid,pid_t tid,const std::string name)100 VirtualThread &VirtualRuntime::UpdateThread(pid_t pid, pid_t tid, const std::string name)
101 {
102 #ifdef HIPERF_DEBUG_TIME
103 const auto startTime = steady_clock::now();
104 #endif
105 VirtualThread &thread = GetThread(pid, tid, name);
106 if (!name.empty()) {
107 thread.name_ = name;
108 }
109 #ifdef HIPERF_DEBUG_TIME
110 updateThreadTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
111 #endif
112 return thread;
113 }
114
CreateThread(pid_t pid,pid_t tid,const std::string name)115 VirtualThread &VirtualRuntime::CreateThread(pid_t pid, pid_t tid, const std::string name)
116 {
117 // make a new one
118 if (pid == tid) {
119 userSpaceThreadMap_.emplace(std::piecewise_construct, std::forward_as_tuple(tid),
120 std::forward_as_tuple(pid, symbolsFiles_));
121 } else {
122 // for thread we need give it process info( for same mmap)
123 userSpaceThreadMap_.emplace(
124 std::piecewise_construct, std::forward_as_tuple(tid),
125 std::forward_as_tuple(pid, tid, GetThread(pid, pid), symbolsFiles_));
126 }
127 VirtualThread &thread = userSpaceThreadMap_.at(tid);
128 if (recordCallBack_) {
129 if (pid == tid && !IsKernelThread(pid)) {
130 #ifdef HIPERF_DEBUG_TIME
131 const auto startTime = steady_clock::now();
132 #endif
133 thread.ParseMap();
134 #ifdef HIPERF_DEBUG_TIME
135 threadParseMapsTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
136 #endif
137 }
138 #ifdef HIPERF_DEBUG_TIME
139 const auto startCreateMmapTime = steady_clock::now();
140 #endif
141 thread.name_ = name;
142 if (thread.name_.empty()) {
143 thread.name_ = ReadThreadName(tid, pid != tid);
144 }
145 HLOGD("create a new thread record for %u:%u:%s with %zu dso", pid, tid,
146 thread.name_.c_str(), thread.GetMaps().size());
147 // we need make a PerfRecordComm
148 auto commRecord = std::make_unique<PerfRecordComm>(IsKernelThread(pid), pid, tid, thread.name_);
149 recordCallBack_(std::move(commRecord));
150 // only work for pid
151 if (pid == tid) {
152 if (isHM_) {
153 thread.FixHMBundleMap();
154 }
155 std::shared_ptr<DfxMap> prevMap = nullptr;
156 for (auto &map : thread.GetMaps()) {
157 // so in hap is load before start perf record
158 // dynamic load library should be treat in the same way
159 bool updateNormalSymbol = true;
160 if (map->name.find(".hap") != std::string::npos && (map->prots & PROT_EXEC)) {
161 map->prevMap = prevMap;
162 updateNormalSymbol = !UpdateHapSymbols(map);
163 HLOGD("UpdateHapSymbols");
164 }
165 auto mmapRecord =
166 std::make_unique<PerfRecordMmap2>(false, thread.pid_, thread.tid_, map);
167 HLOGD("make PerfRecordMmap2 %d:%d:%s:%s(0x%" PRIx64 "-0x%" PRIx64 ")@%" PRIx64 " ",
168 thread.pid_, thread.tid_, thread.name_.c_str(), map->name.c_str(),
169 map->begin, map->end, map->offset);
170 recordCallBack_(std::move(mmapRecord));
171 if (updateNormalSymbol) {
172 UpdateSymbols(map, pid);
173 }
174 prevMap = map;
175 }
176 }
177 HLOGV("thread created");
178 #ifdef HIPERF_DEBUG_TIME
179 threadCreateMmapTimes_ +=
180 duration_cast<microseconds>(steady_clock::now() - startCreateMmapTime);
181 #endif
182 }
183 return thread;
184 }
185
UpdateHapSymbols(std::shared_ptr<DfxMap> map)186 bool VirtualRuntime::UpdateHapSymbols(std::shared_ptr<DfxMap> map)
187 {
188 if (map == nullptr) {
189 return false;
190 }
191 // found it by name
192 auto symbolsFile = SymbolsFile::CreateSymbolsFile(map->name);
193 if (symbolsFile == nullptr) {
194 HLOGV("Failed to load CreateSymbolsFile for exec section in hap(%s)", map->name.c_str());
195 return false;
196 }
197 // update maps name if load debuginfo successfully
198 if (!symbolsFile->LoadDebugInfo(map)) {
199 HLOGV("Failed to load debuginfo for exec section in hap(%s)", map->name.c_str());
200 return false;
201 }
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 (kernelFile->LoadSymbols()) {
327 auto record = std::make_unique<PerfRecordMmap>(
328 true, 0, 0, kernelFile->textExecVaddr_, kernelFile->textExecVaddrRange_,
329 kernelFile->textExecVaddrFileOffset_, KERNEL_MMAP_NAME);
330
331 if (recordCallBack_) {
332 recordCallBack_(std::move(record));
333 }
334 symbolsFiles_.emplace_back(std::move(kernelFile));
335 } else {
336 HLOGW("kernel symbol not found.\n");
337 }
338 }
339
UpdatekernelMap(uint64_t begin,uint64_t end,uint64_t offset,std::string filename)340 void VirtualRuntime::UpdatekernelMap(uint64_t begin, uint64_t end, uint64_t offset,
341 std::string filename)
342 {
343 HLOGV("update kernel map name:'%s' 0x%" PRIx64 " - 0x%" PRIx64 "@0x%08" PRIx64 "",
344 filename.c_str(), begin, end, offset);
345
346 HLOG_ASSERT(!filename.empty());
347 auto it = find(kernelSpaceMemMaps_.begin(), kernelSpaceMemMaps_.end(), filename);
348 if (it == kernelSpaceMemMaps_.end()) {
349 kernelSpaceMemMaps_.emplace_back(begin, end, offset, "", filename);
350 } else {
351 it->begin = begin;
352 it->end = end;
353 it->offset = offset;
354 it->name = filename;
355 }
356 }
357
DedupFromRecord(PerfRecordSample * recordSample)358 void VirtualRuntime::DedupFromRecord(PerfRecordSample *recordSample)
359 {
360 if (recordSample == nullptr) {
361 return;
362 }
363 u64 nr = recordSample->data_.nr;
364 if (nr == 0) {
365 collectSymbolCallBack_(recordSample);
366 return;
367 }
368 u32 pid = recordSample->data_.pid;
369 u64 *ips = recordSample->data_.ips;
370 StackId stackId;
371 stackId.value = 0;
372 auto entry = processStackMap_.find(pid);
373 std::shared_ptr<UniqueStackTable> table = nullptr;
374 if (entry != processStackMap_.end()) {
375 table = entry->second;
376 } else {
377 table = std::make_shared<UniqueStackTable>(pid);
378 processStackMap_[pid] = table;
379 }
380 if (table == nullptr) {
381 return;
382 }
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 {
465 HLOGW("skip record type %d", record.GetType());
466 }
467 }
468
MakeCallFrame(DfxSymbol & symbol,DfxFrame & callFrame)469 void VirtualRuntime::MakeCallFrame(DfxSymbol &symbol, DfxFrame &callFrame)
470 {
471 callFrame.funcOffset = symbol.funcVaddr_;
472 callFrame.mapOffset = symbol.offsetToVaddr_;
473 callFrame.symbolFileIndex = symbol.symbolFileIndex_;
474 callFrame.funcName = symbol.GetName();
475 if (callFrame.funcName.empty()) {
476 HLOGD("callFrame.funcName:%s, GetName:%s\n", callFrame.funcName.c_str(), symbol.GetName().data());
477 }
478
479 callFrame.index = static_cast<size_t>(symbol.index_);
480 callFrame.mapName = symbol.module_.empty() ? symbol.comm_ : symbol.module_;
481 HLOG_ASSERT_MESSAGE(!callFrame.funcName.empty(), "%s", symbol.ToDebugString().c_str());
482 }
483
SymbolicCallFrame(PerfRecordSample & recordSample,uint64_t ip,pid_t serverPid,perf_callchain_context context)484 void VirtualRuntime::SymbolicCallFrame(PerfRecordSample &recordSample, uint64_t ip,
485 pid_t serverPid, perf_callchain_context context)
486 {
487 pid_t pid = static_cast<pid_t>(recordSample.data_.pid);
488 pid_t tid = static_cast<pid_t>(recordSample.data_.tid);
489 if (serverPid != pid) {
490 pid = tid = serverPid;
491 }
492 auto symbol = GetSymbol(ip, pid, tid, context);
493 MakeCallFrame(symbol, recordSample.callFrames_.emplace_back(ip, 0));
494 HLOGV(" (%zu)unwind symbol: %*s%s", recordSample.callFrames_.size(),
495 static_cast<int>(recordSample.callFrames_.size()), "",
496 recordSample.callFrames_.back().ToSymbolString().c_str());
497 }
498
RecoverCallStack(PerfRecordSample & recordSample)499 bool VirtualRuntime::RecoverCallStack(PerfRecordSample &recordSample)
500 {
501 auto StackTable = processStackMap_.find(recordSample.data_.pid);
502 if (StackTable == processStackMap_.end()) {
503 HLOGV("not found %" PRIu32 " pid", recordSample.data_.pid);
504 return false;
505 }
506 recordSample.ips_.clear();
507 if (StackTable->second != nullptr) {
508 StackTable->second->GetIpsByStackId(recordSample.stackId_, recordSample.ips_);
509 }
510 recordSample.RecoverCallStack();
511 return true;
512 }
513
SymbolicRecord(PerfRecordSample & recordSample)514 void VirtualRuntime::SymbolicRecord(PerfRecordSample &recordSample)
515 {
516 #ifdef HIPERF_DEBUG_TIME
517 const auto startTime = steady_clock::now();
518 #endif
519 // Symbolic the Call Stack
520 recordSample.callFrames_.clear();
521 perf_callchain_context context = PERF_CONTEXT_MAX;
522 pid_t serverPid;
523 if (recordSample.data_.nr == 0) {
524 serverPid = recordSample.GetServerPidof(0);
525 SymbolicCallFrame(recordSample, recordSample.data_.ip, serverPid, PERF_CONTEXT_MAX);
526 }
527 for (u64 i = 0; i < recordSample.data_.nr; i++) {
528 uint64_t ip = recordSample.data_.ips[i];
529 if (ip >= PERF_CONTEXT_MAX) {
530 std::string contextName = UpdatePerfContext(ip, context);
531 HLOGV("context switch to %s", contextName.c_str());
532 continue;
533 } else if (ip < BAD_IP_ADDRESS) {
534 // ip 0 or 1 or less than 0
535 continue;
536 }
537 serverPid = recordSample.GetServerPidof(i);
538 SymbolicCallFrame(recordSample, ip, serverPid, context);
539 }
540 #ifdef HIPERF_DEBUG_TIME
541 auto usedTime = duration_cast<microseconds>(steady_clock::now() - startTime);
542 if (usedTime.count() != 0) {
543 HLOGV("cost %0.3f ms to symbolic ", usedTime.count() / MS_DURATION);
544 }
545 symbolicRecordTimes_ += usedTime;
546 #endif
547 }
548
NeedDropKernelCallChain(PerfRecordSample & sample)549 void VirtualRuntime::NeedDropKernelCallChain(PerfRecordSample &sample)
550 {
551 // only do this in record mode.
552 if (recordCallBack_ == nullptr || needkernelCallChain_ ||
553 !sample.inKernel() || sample.data_.nr == 0) {
554 return;
555 }
556
557 u64 skip = 0;
558 u64 skipPid = 0;
559 u64 *ips = sample.data_.ips;
560 for (; skip < sample.data_.nr; skip++) {
561 if (ips[skip] == PERF_CONTEXT_KERNEL) {
562 skipPid++;
563 }
564 if (ips[skip] == PERF_CONTEXT_USER) {
565 break;
566 }
567 }
568 sample.skipKernel_ = skip;
569 sample.data_.nr -= skip;
570 sample.header.size -= sizeof(u64) * skip;
571 if (sample.data_.server_nr > 0) {
572 sample.skipPid_ = skipPid;
573 sample.data_.server_nr -= skipPid;
574 sample.header.size -= sizeof(u64) * skipPid;
575 }
576 }
577
UnwindFromRecord(PerfRecordSample & recordSample)578 void VirtualRuntime::UnwindFromRecord(PerfRecordSample &recordSample)
579 {
580 #if defined(is_ohos) && is_ohos
581 #ifdef HIPERF_DEBUG_TIME
582 const auto startTime = steady_clock::now();
583 #endif
584 HLOGV("unwind record (time:%llu)", recordSample.data_.time);
585 // if we have userstack ?
586 if (recordSample.data_.stack_size > 0) {
587 pid_t serverPid = recordSample.GetUstackServerPid();
588 pid_t pid = static_cast<pid_t>(recordSample.data_.pid);
589 pid_t tid = static_cast<pid_t>(recordSample.data_.tid);
590 if (serverPid != pid) {
591 pid = tid = serverPid;
592 }
593 auto &thread = UpdateThread(pid, tid);
594 callstack_.UnwindCallStack(thread, recordSample.data_.user_abi == PERF_SAMPLE_REGS_ABI_32,
595 recordSample.data_.user_regs, recordSample.data_.reg_nr,
596 recordSample.data_.stack_data, recordSample.data_.dyn_size,
597 recordSample.callFrames_);
598 #ifdef HIPERF_DEBUG_TIME
599 unwindCallStackTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
600 #endif
601 size_t oldSize = recordSample.callFrames_.size();
602 HLOGV("unwind %zu", recordSample.callFrames_.size());
603 callstack_.ExpandCallStack(thread.tid_, recordSample.callFrames_, callstackMergeLevel_);
604 HLOGV("expand %zu (+%zu)", recordSample.callFrames_.size(),
605 recordSample.callFrames_.size() - oldSize);
606
607 recordSample.ReplaceWithCallStack(oldSize);
608 }
609
610 #ifdef HIPERF_DEBUG_TIME
611 unwindFromRecordTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
612 #endif
613
614 NeedDropKernelCallChain(recordSample);
615 // we will not do this in non record mode.
616 if (dedupStack_ && recordCallBack_ != nullptr) {
617 DedupFromRecord(&recordSample);
618 }
619 #endif
620
621 // we will not do this in record mode
622 if (recordCallBack_ == nullptr) {
623 if (dedupStack_ && recordSample.stackId_.section.id > 0 && recordSample.data_.nr == 0) {
624 RecoverCallStack(recordSample);
625 }
626 // find the symbols , reabuild frame info
627 SymbolicRecord(recordSample);
628 }
629 }
630
SetCollectSymbolCallBack(CollectSymbolCallBack collectSymbolCallBack)631 void VirtualRuntime::SetCollectSymbolCallBack(CollectSymbolCallBack collectSymbolCallBack)
632 {
633 collectSymbolCallBack_ = collectSymbolCallBack;
634 }
635
UpdateFromRecord(PerfRecordSample & recordSample)636 void VirtualRuntime::UpdateFromRecord(PerfRecordSample &recordSample)
637 {
638 UpdateThread(recordSample.data_.pid, recordSample.data_.tid);
639 if (recordSample.data_.server_nr) {
640 // update all server threads
641 for (size_t i = 0; i < recordSample.data_.server_nr; i++) {
642 pid_t pid = static_cast<pid_t>(recordSample.data_.server_pids[i]);
643 UpdateThread(pid, pid);
644 }
645 }
646 // unwind
647 if (disableUnwind_) {
648 return;
649 } else {
650 UnwindFromRecord(recordSample);
651 }
652 }
653
UpdateFromRecord(PerfRecordMmap & recordMmap)654 void VirtualRuntime::UpdateFromRecord(PerfRecordMmap &recordMmap)
655 {
656 HLOGV(" MMAP: size %d pid %u tid %u", recordMmap.header.size, recordMmap.data_.pid,
657 recordMmap.data_.tid);
658 HLOGV(" MMAP: %s dso '%s' (0x%llx-0x%llx)@0x%llx", recordMmap.inKernel() ? "kernel" : "user",
659 recordMmap.data_.filename, recordMmap.data_.addr,
660 recordMmap.data_.addr + recordMmap.data_.len, recordMmap.data_.pgoff);
661 // kernel mmap
662 // don't overwrite the vailed mmap , so we also check the recordMmap.data_.len
663 if (IsKernelThread(recordMmap.data_.pid)) {
664 UpdateKernelThreadMap(recordMmap.data_.pid, recordMmap.data_.addr,
665 recordMmap.data_.len, recordMmap.data_.filename);
666 } else if (recordMmap.inKernel()) {
667 UpdatekernelMap(recordMmap.data_.addr, recordMmap.data_.addr + recordMmap.data_.len,
668 recordMmap.data_.pgoff, recordMmap.data_.filename);
669 } else {
670 NeedAdaptSandboxPath(recordMmap.data_.filename, recordMmap.data_.pid, recordMmap.header.size);
671 FixHMBundleMmap(recordMmap.data_.filename, recordMmap.data_.pid, recordMmap.header.size);
672 auto map = UpdateThreadMaps(recordMmap.data_.pid, recordMmap.data_.tid, recordMmap.data_.filename,
673 recordMmap.data_.addr, recordMmap.data_.len, recordMmap.data_.pgoff);
674 UpdateSymbols(map, recordMmap.data_.pid);
675 }
676 }
677
CheckValidSandBoxMmap(PerfRecordMmap2 & recordMmap2)678 bool VirtualRuntime::CheckValidSandBoxMmap(PerfRecordMmap2 &recordMmap2)
679 {
680 static std::shared_ptr<DfxMap> prevMap;
681 if ((recordMmap2.data_.prot & PROT_EXEC) != 0) {
682 // fake first segment, when second segment come.
683 auto symFile = SymbolsFile::CreateSymbolsFile(SYMBOL_ELF_FILE, recordMmap2.data_.filename);
684 if (symFile == nullptr) {
685 HLOGD("CheckValidSandBoxMmap Failed to create symbolFile!");
686 return false;
687 }
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 if (!symFile->LoadDebugInfo(curMap)) {
702 HLOGD("CheckValidSandBoxMmap Failed to load debuginfo!");
703 return false;
704 }
705
706 if (!loadSymboleWhenNeeded_) {
707 symFile->LoadSymbols(curMap);
708 }
709
710 if (strstr(recordMmap2.data_.filename, ".hap") == nullptr) {
711 auto elfLoadInfoMap = symFile->GetPtLoads();
712 u64 begin = recordMmap2.data_.addr - elfLoadInfoMap[0].mmapLen;
713 u64 len = elfLoadInfoMap[0].mmapLen;
714 u64 pgoff = elfLoadInfoMap[0].offset & (~(elfLoadInfoMap[0].align >= 1 ? elfLoadInfoMap[0].align - 1 : 0));
715 std::unique_ptr<PerfRecordMmap2> mmap2FirstSeg =
716 std::make_unique<PerfRecordMmap2>(recordMmap2.inKernel(), recordMmap2.data_.pid, recordMmap2.data_.tid,
717 begin, len, pgoff, 0, 0, 0, PROT_READ, 0, std::string(recordMmap2.data_.filename));
718 UpdateThreadMaps(mmap2FirstSeg->data_.pid, mmap2FirstSeg->data_.tid, mmap2FirstSeg->data_.filename,
719 mmap2FirstSeg->data_.addr, mmap2FirstSeg->data_.len, mmap2FirstSeg->data_.pgoff);
720 recordCallBack_(std::move(mmap2FirstSeg));
721 } else {
722 auto elfLoadInfoMap = symFile->GetPtLoads();
723 u64 begin = recordMmap2.data_.addr - elfLoadInfoMap[0].mmapLen;
724 u64 len = elfLoadInfoMap[0].mmapLen;
725 u64 pgoff = elfLoadInfoMap[0].offset &
726 (~(elfLoadInfoMap[0].align >= 1 ? elfLoadInfoMap[0].align - 1 : 0));
727 std::unique_ptr<PerfRecordMmap2> mmap2FirstSeg =
728 std::make_unique<PerfRecordMmap2>(recordMmap2.inKernel(), recordMmap2.data_.pid, recordMmap2.data_.tid,
729 begin, len, pgoff, 0, 0, 0, PROT_READ, 0, curMap->name);
730 UpdateThreadMaps(mmap2FirstSeg->data_.pid, mmap2FirstSeg->data_.tid, curMap->name,
731 mmap2FirstSeg->data_.addr, mmap2FirstSeg->data_.len, mmap2FirstSeg->data_.pgoff);
732 recordCallBack_(std::move(mmap2FirstSeg));
733
734 std::unique_ptr<PerfRecordMmap2> mmap2SecondSegment =
735 std::make_unique<PerfRecordMmap2>(recordMmap2.inKernel(), recordMmap2.data_.pid, recordMmap2.data_.tid,
736 recordMmap2.data_.addr,
737 recordMmap2.data_.len,
738 recordMmap2.data_.pgoff - prevMap->offset, // minus load offset of hap
739 0, 0, 0, recordMmap2.data_.prot, 0, curMap->name);
740 UpdateThreadMaps(mmap2SecondSegment->data_.pid, mmap2SecondSegment->data_.tid, curMap->name,
741 mmap2SecondSegment->data_.addr, mmap2SecondSegment->data_.len, mmap2SecondSegment->data_.pgoff);
742 recordCallBack_(std::move(mmap2SecondSegment));
743 recordMmap2.discard_ = true;
744 }
745 symbolsFiles_.emplace_back(std::move(symFile));
746 return true;
747 } else if (recordMmap2.data_.pgoff == 0) {
748 recordMmap2.discard_ = true;
749 }
750
751 if (strstr(recordMmap2.data_.filename, ".hap") != nullptr) {
752 prevMap = std::make_shared<DfxMap>(
753 recordMmap2.data_.addr,
754 recordMmap2.data_.addr + recordMmap2.data_.len,
755 recordMmap2.data_.pgoff,
756 "", // prot
757 recordMmap2.data_.filename
758 );
759 HLOGD("CheckValidSandBoxMmap Update prev map!");
760 }
761 return !recordMmap2.discard_;
762 }
763
UpdateFromRecord(PerfRecordMmap2 & recordMmap2)764 void VirtualRuntime::UpdateFromRecord(PerfRecordMmap2 &recordMmap2)
765 {
766 if (!OHOS::HiviewDFX::DfxMaps::IsLegalMapItem(recordMmap2.data_.filename)) {
767 return;
768 }
769
770 HLOGV(" MMAP2: size %d pid %u tid %u", recordMmap2.header.size, recordMmap2.data_.pid,
771 recordMmap2.data_.tid);
772 HLOGV(" MMAP2: %s dso '%s' (0x%llx-0x%llx)@0x%llx prot:%u", recordMmap2.inKernel() ? "kernel" : "user",
773 recordMmap2.data_.filename, recordMmap2.data_.addr,
774 recordMmap2.data_.addr + recordMmap2.data_.len, recordMmap2.data_.pgoff, recordMmap2.data_.prot);
775
776 if (recordCallBack_) {
777 if (NeedAdaptSandboxPath(recordMmap2.data_.filename, recordMmap2.data_.pid, recordMmap2.header.size)) {
778 FixHMBundleMmap(recordMmap2.data_.filename, recordMmap2.data_.pid, recordMmap2.header.size);
779 if (!CheckValidSandBoxMmap(recordMmap2)) {
780 return;
781 }
782 }
783 }
784 auto map = UpdateThreadMaps(recordMmap2.data_.pid, recordMmap2.data_.tid, recordMmap2.data_.filename,
785 recordMmap2.data_.addr, recordMmap2.data_.len, recordMmap2.data_.pgoff,
786 recordMmap2.data_.prot);
787 UpdateSymbols(map, recordMmap2.data_.pid);
788 }
789
UpdateFromRecord(PerfRecordComm & recordComm)790 void VirtualRuntime::UpdateFromRecord(PerfRecordComm &recordComm)
791 {
792 recordComm.DumpLog(__FUNCTION__);
793 UpdateThread(recordComm.data_.pid, recordComm.data_.tid, recordComm.data_.comm);
794 }
795
SetRecordMode(RecordCallBack recordCallBack)796 void VirtualRuntime::SetRecordMode(RecordCallBack recordCallBack)
797 {
798 recordCallBack_ = recordCallBack;
799 }
800
UpdateSymbols(std::shared_ptr<DfxMap> map,pid_t pid)801 void VirtualRuntime::UpdateSymbols(std::shared_ptr<DfxMap> map, pid_t pid)
802 {
803 if (map == nullptr) {
804 return;
805 }
806 if (map->symbolFileIndex != -1) {
807 // in cache
808 return;
809 }
810 HLOGD("try to find symbols for file: %s", map->name.c_str());
811 for (size_t i = 0; i < symbolsFiles_.size(); ++i) {
812 if (symbolsFiles_[i]->filePath_ == map->name) {
813 map->symbolFileIndex = static_cast<int32_t>(i);
814 HLOGV("already have '%s'", map->name.c_str());
815 return;
816 }
817 }
818
819 #ifdef HIPERF_DEBUG_TIME
820 const auto startTime = steady_clock::now();
821 #endif
822 /**
823 * map[] map.name = SymbolsFile.filePath_ prot SymbolsFileType
824 * seg1 /data/storage/el1/bundle/entry.hap r--p ABC
825 * seg2 /data/storage/el1/bundle/entry.hap r-xp ELF
826 * seg3 /data/storage/el1/bundle/entry.hap r--p ABC
827 * seg4 /data/storage/el1/bundle/entry.hap r--p ABC
828 * seg1 /system/app/SceneBoard/SceneBoard.hap r--p ABC
829 * seg2 /system/app/SceneBoard/SceneBoard.hap r--p ABC
830 * seg3 /system/app/SceneBoard/SceneBoard.hap r--p ABC
831 * segN .hap r--p .an/jit/etc
832 * 1.map.name == symbolsFile.filePath_
833 * 2.map.FileType == symbolsFiles_[map.symbolFileIndex]
834 * 3.cache pc->map->symbolsFiles[map.symbolFileIndex]
835 * 4.must ensure map.mapType assigned with SymbolsFile constructions at the same time.
836 */
837 auto symbolsFile = SymbolsFile::CreateSymbolsFile(map->name, pid);
838 symbolsFile->SetMapsInfo(map);
839 if (enableDebugInfoSymbolic_ && symbolsFile->symbolFileType_ == SymbolsFileType::SYMBOL_ELF_FILE) {
840 symbolsFile->EnableMiniDebugInfo();
841 }
842 // set symbol path If it exists
843 if (symbolsPaths_.size() > 0) {
844 // also load from search path
845 symbolsFile->setSymbolsFilePath(symbolsPaths_);
846 }
847 if (loadSymboleWhenNeeded_) {
848 // load it when we need it
849 symbolsFiles_.emplace_back(std::move(symbolsFile));
850 } else if (symbolsFile->LoadSymbols()) {
851 symbolsFiles_.emplace_back(std::move(symbolsFile));
852 } else {
853 HLOGW("symbols file for '%s' not found.", map->name.c_str());
854 }
855 #ifdef HIPERF_DEBUG_TIME
856 auto usedTime = duration_cast<microseconds>(steady_clock::now() - startTime);
857 if (usedTime.count() != 0) {
858 HLOGV("cost %0.3f ms to load '%s'", usedTime.count() / MS_DURATION, map->name.c_str());
859 }
860 updateSymbolsTimes_ += usedTime;
861 #endif
862 }
863
GetKernelSymbol(uint64_t ip,const std::vector<DfxMap> & memMaps,const VirtualThread & thread)864 const DfxSymbol VirtualRuntime::GetKernelSymbol(uint64_t ip, const std::vector<DfxMap> &memMaps,
865 const VirtualThread &thread)
866 {
867 DfxSymbol vaddrSymbol(ip, thread.name_);
868 for (auto &map : memMaps) {
869 if (ip > map.begin && ip < map.end) {
870 HLOGM("found addr 0x%" PRIx64 " in kernel map 0x%" PRIx64 " - 0x%" PRIx64 " from %s",
871 ip, map.begin, map.end, map.name.c_str());
872 vaddrSymbol.module_ = map.name;
873 // found symbols by file name
874 for (auto &symbolsFile : symbolsFiles_) {
875 if (symbolsFile->filePath_ == map.name) {
876 vaddrSymbol.symbolFileIndex_ = symbolsFile->id_;
877 vaddrSymbol.fileVaddr_ =
878 symbolsFile->GetVaddrInSymbols(ip, map.begin, map.offset);
879 perf_callchain_context context = PERF_CONTEXT_KERNEL;
880 if (GetSymbolCache(vaddrSymbol.fileVaddr_, vaddrSymbol, context)) {
881 return vaddrSymbol;
882 }
883 HLOGV("found symbol vaddr 0x%" PRIx64 " for runtime vaddr 0x%" PRIx64
884 " at '%s'",
885 vaddrSymbol.fileVaddr_, ip, map.name.c_str());
886 if (!symbolsFile->SymbolsLoaded()) {
887 symbolsFile->LoadSymbols();
888 }
889 DfxSymbol foundSymbols = symbolsFile->GetSymbolWithVaddr(vaddrSymbol.fileVaddr_);
890 foundSymbols.taskVaddr_ = ip;
891 if (!foundSymbols.IsValid()) {
892 HLOGW("addr 0x%" PRIx64 " vaddr 0x%" PRIx64 " NOT found in symbol file %s",
893 ip, vaddrSymbol.fileVaddr_, map.name.c_str());
894 return vaddrSymbol;
895 } else {
896 return foundSymbols;
897 }
898 }
899 }
900 HLOGW("addr 0x%" PRIx64 " in map but NOT found the symbol file %s", ip,
901 map.name.c_str());
902 } else {
903 HLOGM("addr 0x%" PRIx64 " not in map 0x%" PRIx64 " - 0x%" PRIx64 " from %s", ip,
904 map.begin, map.end, map.name.c_str());
905 }
906 }
907 return vaddrSymbol;
908 }
909
GetKernelThreadSymbol(uint64_t ip,const VirtualThread & thread)910 const DfxSymbol VirtualRuntime::GetKernelThreadSymbol(uint64_t ip, const VirtualThread &thread)
911 {
912 DfxSymbol vaddrSymbol(ip, thread.name_);
913 int64_t mapIndex = thread.FindMapIndexByAddr(ip);
914 if (mapIndex < 0) {
915 #ifdef HIPERF_DEBUG
916 thread.ReportVaddrMapMiss(ip);
917 #endif
918 return vaddrSymbol;
919 }
920
921 auto map = thread.GetMaps()[mapIndex];
922 if (map == nullptr) {
923 return vaddrSymbol;
924 }
925 HLOGM("found addr 0x%" PRIx64 " in kthread map 0x%" PRIx64 " - 0x%" PRIx64 " from %s",
926 ip, map->begin, map->end, map->name.c_str());
927 // found symbols by file name
928 for (auto &symbolsFile : symbolsFiles_) {
929 if (symbolsFile->filePath_ == map->name) {
930 vaddrSymbol.symbolFileIndex_ = symbolsFile->id_;
931 vaddrSymbol.module_ = map->name;
932 vaddrSymbol.fileVaddr_ =
933 symbolsFile->GetVaddrInSymbols(ip, map->begin, map->offset);
934 perf_callchain_context context = PERF_CONTEXT_MAX;
935 if (GetSymbolCache(vaddrSymbol.fileVaddr_, vaddrSymbol, context)) {
936 return vaddrSymbol;
937 }
938 HLOGV("found symbol vaddr 0x%" PRIx64 " for runtime vaddr 0x%" PRIx64 " at '%s'",
939 vaddrSymbol.fileVaddr_, ip, map->name.c_str());
940 if (!symbolsFile->SymbolsLoaded()) {
941 symbolsFile->LoadDebugInfo();
942 symbolsFile->LoadSymbols(map);
943 }
944 auto foundSymbols = symbolsFile->GetSymbolWithVaddr(vaddrSymbol.fileVaddr_);
945 foundSymbols.taskVaddr_ = ip;
946 if (!foundSymbols.IsValid()) {
947 HLOGW("addr 0x%" PRIx64 " vaddr 0x%" PRIx64 " NOT found in symbol file %s",
948 ip, vaddrSymbol.fileVaddr_, map->name.c_str());
949 return vaddrSymbol;
950 } else {
951 return foundSymbols;
952 }
953 }
954 }
955 HLOGW("addr 0x%" PRIx64 " in map but NOT found the symbol file %s", ip,
956 map->name.c_str());
957 return vaddrSymbol;
958 }
959
GetUserSymbol(uint64_t ip,const VirtualThread & thread)960 const DfxSymbol VirtualRuntime::GetUserSymbol(uint64_t ip, const VirtualThread &thread)
961 {
962 DfxSymbol vaddrSymbol(ip, thread.name_);
963 int64_t mapIndex = thread.FindMapIndexByAddr(ip);
964 if (mapIndex >= 0) {
965 auto map = thread.GetMaps()[mapIndex];
966 SymbolsFile *symbolsFile = thread.FindSymbolsFileByMap(map);
967 if (symbolsFile != nullptr) {
968 vaddrSymbol.symbolFileIndex_ = symbolsFile->id_;
969 vaddrSymbol.module_ = map->name;
970 vaddrSymbol.fileVaddr_ = symbolsFile->GetVaddrInSymbols(ip, map->begin, map->offset);
971 perf_callchain_context context = PERF_CONTEXT_USER;
972 if (GetSymbolCache(vaddrSymbol.fileVaddr_, vaddrSymbol, context)) {
973 return vaddrSymbol;
974 }
975 HLOGV("found symbol vaddr 0x%" PRIx64 " for runtime vaddr 0x%" PRIx64 " at '%s'",
976 vaddrSymbol.fileVaddr_, ip, map->name.c_str());
977 if (!symbolsFile->SymbolsLoaded()) {
978 symbolsFile->LoadDebugInfo(map);
979 symbolsFile->LoadSymbols(map);
980 }
981 DfxSymbol foundSymbols;
982 if (!symbolsFile->IsAbc()) {
983 foundSymbols = symbolsFile->GetSymbolWithVaddr(vaddrSymbol.fileVaddr_);
984 } else {
985 HLOGD("symbolsFile:%s is ABC :%d", symbolsFile->filePath_.c_str(), symbolsFile->IsAbc());
986 foundSymbols = symbolsFile->GetSymbolWithPcAndMap(ip, map);
987 }
988
989 if (foundSymbols.IsValid()) {
990 return foundSymbols;
991 } else {
992 HLOGW("addr 0x%" PRIx64 " vaddr 0x%" PRIx64 " NOT found in symbol file %s", ip,
993 vaddrSymbol.fileVaddr_, map->name.c_str());
994 if (symbolsFile->IsAbc()) {
995 symbolsFile->symbolsMap_.insert(std::make_pair(ip, vaddrSymbol));
996 }
997 return vaddrSymbol;
998 }
999 } else {
1000 HLOGW("addr 0x%" PRIx64 " in map but NOT found the symbol file %s", ip, map->name.c_str());
1001 }
1002 } else {
1003 #ifdef HIPERF_DEBUG
1004 thread.ReportVaddrMapMiss(ip);
1005 #endif
1006 }
1007 return vaddrSymbol;
1008 }
1009
GetSymbolCache(uint64_t fileVaddr,DfxSymbol & symbol,const perf_callchain_context & context)1010 bool VirtualRuntime::GetSymbolCache(uint64_t fileVaddr, DfxSymbol &symbol,
1011 const perf_callchain_context &context)
1012 {
1013 if (context == PERF_CONTEXT_MAX and kThreadSymbolCache_.count(fileVaddr)) {
1014 if (kThreadSymbolCache_.find(symbol.fileVaddr_) == kThreadSymbolCache_.end()) {
1015 return false;
1016 }
1017 symbol = kThreadSymbolCache_[symbol.fileVaddr_];
1018 symbol.hit_++;
1019 HLOGV("hit kernel thread cache 0x%" PRIx64 " %d", fileVaddr, symbol.hit_);
1020 return true;
1021 } else if (context != PERF_CONTEXT_USER and kernelSymbolCache_.count(fileVaddr)) {
1022 if (kernelSymbolCache_.find(symbol.fileVaddr_) == kernelSymbolCache_.end()) {
1023 return false;
1024 }
1025 symbol = kernelSymbolCache_[symbol.fileVaddr_];
1026 symbol.hit_++;
1027 HLOGV("hit kernel cache 0x%" PRIx64 " %d", fileVaddr, symbol.hit_);
1028 return true;
1029 } else if (userSymbolCache_.count(symbol.fileVaddr_) != 0) {
1030 const DfxSymbol &cachedSymbol = userSymbolCache_[symbol.fileVaddr_];
1031 // must be the same file
1032 if (cachedSymbol.module_ != symbol.module_) {
1033 return false;
1034 }
1035 symbol = cachedSymbol;
1036 symbol.hit_++;
1037 HLOGV("hit user cache 0x%" PRIx64 " %d %s", fileVaddr, symbol.hit_,
1038 symbol.ToDebugString().c_str());
1039 return true;
1040 } else {
1041 HLOGM("cache miss k %zu u %zu kt %zu", kernelSymbolCache_.size(),
1042 userSymbolCache_.size(), kThreadSymbolCache_.size());
1043 }
1044 return false;
1045 }
1046
GetSymbol(uint64_t ip,pid_t pid,pid_t tid,const perf_callchain_context & context)1047 DfxSymbol VirtualRuntime::GetSymbol(uint64_t ip, pid_t pid, pid_t tid, const perf_callchain_context &context)
1048 {
1049 HLOGV("try find tid %u ip 0x%" PRIx64 " in %zu symbolsFiles\n", tid, ip, symbolsFiles_.size());
1050 DfxSymbol symbol;
1051
1052 if (IsKernelThread(pid)) {
1053 VirtualThread &kthread = GetThread(pid, tid);
1054 HLOGM("try found addr in kernel thread %u with %zu maps", pid,
1055 kthread.GetMaps().size());
1056 symbol = GetKernelThreadSymbol(ip, kthread);
1057 HLOGM("add addr to kernel thread cache 0x%" PRIx64 " cache size %zu", ip,
1058 kThreadSymbolCache_.size());
1059 kThreadSymbolCache_[symbol.fileVaddr_] = symbol;
1060 if (symbol.IsValid()) {
1061 return symbol;
1062 }
1063 }
1064
1065 if (context == PERF_CONTEXT_USER or (context == PERF_CONTEXT_MAX and !symbol.IsValid())) {
1066 // check userspace memmap
1067 symbol = GetUserSymbol(ip, GetThread(pid, tid));
1068 if (userSymbolCache_.find(symbol.fileVaddr_) == userSymbolCache_.end()) {
1069 userSymbolCache_.reserve(USER_SYMBOL_CACHE_LIMIT);
1070 }
1071 userSymbolCache_[symbol.fileVaddr_] = symbol;
1072 HLOGV("cache ip 0x%" PRIx64 " to %s", ip,
1073 userSymbolCache_[symbol.fileVaddr_].ToDebugString().c_str());
1074 }
1075
1076 if (context == PERF_CONTEXT_KERNEL or (context == PERF_CONTEXT_MAX and !symbol.IsValid())) {
1077 // check kernelspace
1078 HLOGM("try found addr in kernelspace %zu maps", kernelSpaceMemMaps_.size());
1079 symbol = GetKernelSymbol(ip, kernelSpaceMemMaps_, GetThread(pid, tid));
1080 HLOGM("add addr to kernel cache 0x%" PRIx64 " cache size %zu", ip,
1081 kernelSymbolCache_.size());
1082 kernelSymbolCache_[symbol.fileVaddr_] = symbol;
1083 }
1084 return symbol;
1085 }
1086
SetSymbolsPaths(const std::vector<std::string> & symbolsPaths)1087 bool VirtualRuntime::SetSymbolsPaths(const std::vector<std::string> &symbolsPaths)
1088 {
1089 std::unique_ptr<SymbolsFile> symbolsFile = SymbolsFile::CreateSymbolsFile(SYMBOL_UNKNOW_FILE);
1090 if (symbolsFile == nullptr) {
1091 return false;
1092 }
1093 // we need check if the path is accessible
1094 bool accessible = symbolsFile->setSymbolsFilePath(symbolsPaths);
1095 if (accessible) {
1096 symbolsPaths_ = symbolsPaths;
1097 } else {
1098 if (!symbolsPaths.empty()) {
1099 printf("some symbols path unable access\n");
1100 }
1101 }
1102 return accessible;
1103 }
1104
UpdateFromPerfData(const std::vector<SymbolFileStruct> & symbolFileStructs)1105 void VirtualRuntime::UpdateFromPerfData(const std::vector<SymbolFileStruct> &symbolFileStructs)
1106 {
1107 // review: if we need move to some other place ?
1108 HLOG_ASSERT_MESSAGE(symbolsFiles_.size() == 0, " symbolsFiles_ size is %zu",
1109 symbolsFiles_.size());
1110 for (const auto &symbolFileStruct : symbolFileStructs) {
1111 HLOGV("symbolFileStruct.filePath_:'%s'", symbolFileStruct.filePath_.c_str());
1112 HLOGV("symbolFileStruct.buildId_:'%s'", symbolFileStruct.buildId_.c_str());
1113 HLOGV("process symbols file:'%s':'%s'", symbolFileStruct.filePath_.c_str(),
1114 symbolFileStruct.buildId_.c_str());
1115
1116 // load from symbolFileStruct (perf.data)
1117 std::unique_ptr<SymbolsFile> symbolsFile = SymbolsFile::LoadSymbolsFromSaved(symbolFileStruct);
1118 if (symbolsFile == nullptr) {
1119 continue;
1120 }
1121 // reaload from sybol path If it exists
1122 if (symbolsPaths_.size() > 0) {
1123 HLOGV("try again with symbolsPaths setup");
1124 symbolsFile->setSymbolsFilePath(symbolsPaths_); // also load from search path
1125 // use give us path ,we must reload it.
1126 symbolsFile->LoadSymbols();
1127 }
1128 symbolsFile->id_ = static_cast<int32_t>(symbolsFiles_.size());
1129 symbolsFiles_.emplace_back(std::move(symbolsFile));
1130 }
1131 }
1132
ImportUniqueStackNodes(const std::vector<UniStackTableInfo> & uniStackTableInfos)1133 void VirtualRuntime::ImportUniqueStackNodes(const std::vector<UniStackTableInfo>& uniStackTableInfos)
1134 {
1135 for (const UniStackTableInfo& item : uniStackTableInfos) {
1136 auto stackTable = std::make_shared<UniqueStackTable>(item.pid, item.tableSize);
1137 for (const UniStackNode& node : item.nodes) {
1138 stackTable->ImportNode(node.index, node.node);
1139 }
1140 processStackMap_[item.pid] = std::move(stackTable);
1141 }
1142 }
1143
1144 /*
1145 ARM functions
1146 The table below lists the symbols exported by the vDSO.
1147
1148 symbol version
1149 ────────────────────────────────────────────────────────────
1150 __vdso_gettimeofday LINUX_2.6 (exported since Linux 4.1)
1151 __vdso_clock_gettime LINUX_2.6 (exported since Linux 4.1)
1152
1153 Additionally, the ARM port has a code page full of utility
1154 functions. Since it's just a raw page of code, there is no ELF
1155 information for doing symbol lookups or versioning. It does
1156 provide support for different versions though.
1157
1158 For information on this code page, it's best to refer to the
1159 kernel documentation as it's extremely detailed and covers
1160 everything you need to know:
1161 Documentation/arm/kernel_user_helpers.txt.
1162
1163 aarch64 functions
1164 The table below lists the symbols exported by the vDSO.
1165
1166 symbol version
1167 ──────────────────────────────────────
1168 __kernel_rt_sigreturn LINUX_2.6.39
1169 __kernel_gettimeofday LINUX_2.6.39
1170 __kernel_clock_gettime LINUX_2.6.39
1171 __kernel_clock_getres LINUX_2.6.39
1172 */
LoadVdso()1173 void VirtualRuntime::LoadVdso()
1174 {
1175 #if !is_mingw
1176 VirtualThread myThread(getpid(), symbolsFiles_);
1177 myThread.ParseMap();
1178 for (const auto &map : myThread.GetMaps()) {
1179 if (map->name == MMAP_VDSO_NAME) {
1180 std::string memory(map->end - map->begin, '\0');
1181 std::copy(reinterpret_cast<char *>((map->begin)), reinterpret_cast<char *>((map->end)),
1182 &memory[0]);
1183 std::string tempPath("/data/local/tmp/");
1184 std::string tempFileName = tempPath + MMAP_VDSO_NAME;
1185 if (!WriteStringToFile(tempFileName, memory)) {
1186 printf("vdso temp file create fail at %s\n", tempFileName.c_str());
1187 } else {
1188 HLOGD("vdso temp file create at %s:%zu", tempFileName.c_str(), memory.size());
1189 auto symbolsFile = SymbolsFile::CreateSymbolsFile(MMAP_VDSO_NAME);
1190 symbolsFile->setSymbolsFilePath(tempPath); // also load from search path
1191 symbolsFiles_.emplace_back(std::move(symbolsFile));
1192 return;
1193 }
1194 }
1195 }
1196 HLOGD("no vdso found");
1197 #endif
1198 }
1199
UpdateServiceSpaceMaps()1200 void VirtualRuntime::UpdateServiceSpaceMaps()
1201 {
1202 VirtualThread &kthread = GetThread(SYSMGR_PID, SYSMGR_PID);
1203 kthread.ParseServiceMap(SYSMGR_FILE_NAME);
1204 if (recordCallBack_) {
1205 for (const auto &map : kthread.GetMaps()) {
1206 auto record =
1207 std::make_unique<PerfRecordMmap>(true, SYSMGR_PID, SYSMGR_PID,
1208 map->begin, map->end - map->begin,
1209 0, SYSMGR_FILE_NAME);
1210 recordCallBack_(std::move(record));
1211 }
1212 }
1213 }
1214
UpdateServiceSymbols()1215 void VirtualRuntime::UpdateServiceSymbols()
1216 {
1217 HLOGD("try to update kernel thread symbols for kernel service");
1218 std::string fileName = SYSMGR_FILE_NAME;
1219 auto symbolsFile = SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_THREAD_FILE, fileName);
1220
1221 HLOGD("add kernel service symbol file: %s", fileName.c_str());
1222 if (symbolsFile->LoadSymbols()) {
1223 symbolsFiles_.emplace_back(std::move(symbolsFile));
1224 } else {
1225 HLOGW("symbols file for '%s' not found.", fileName.c_str());
1226 }
1227 }
1228
UpdateKernelThreadMap(pid_t pid,uint64_t begin,uint64_t len,std::string filename)1229 void VirtualRuntime::UpdateKernelThreadMap(pid_t pid, uint64_t begin, uint64_t len,
1230 std::string filename)
1231 {
1232 HLOGV("update kernel thread map pid %u name:'%s'", pid, filename.c_str());
1233
1234 VirtualThread &thread = GetThread(pid, pid);
1235 thread.CreateMapItem(filename, begin, len, 0u);
1236 }
1237
UpdateDevhostSpaceMaps()1238 void VirtualRuntime::UpdateDevhostSpaceMaps()
1239 {
1240 VirtualThread &kthread = GetThread(devhostPid_, devhostPid_);
1241 kthread.ParseDevhostMap(devhostPid_);
1242 if (recordCallBack_) {
1243 for (const auto &map : kthread.GetMaps()) {
1244 auto record =
1245 std::make_unique<PerfRecordMmap>(false, devhostPid_, devhostPid_,
1246 map->begin, map->end - map->begin,
1247 0, map->name);
1248 recordCallBack_(std::move(record));
1249 }
1250 }
1251 }
1252
UpdateDevhostSymbols()1253 void VirtualRuntime::UpdateDevhostSymbols()
1254 {
1255 HLOGD("try to update kernel thread symbols for devhost");
1256 auto kallsyms = SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_THREAD_FILE, DEVHOST_FILE_NAME);
1257 // file name of devhost.ko
1258 std::map<std::string_view, std::unique_ptr<SymbolsFile>> koMaps;
1259 koMaps[DEVHOST_FILE_NAME] =
1260 SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_THREAD_FILE, DEVHOST_LINUX_FILE_NAME);
1261
1262 if (kallsyms->LoadSymbols()) {
1263 for (auto &symbol : kallsyms->GetSymbols()) {
1264 if (koMaps.find(symbol.module_) == koMaps.end()) {
1265 std::string filename = std::string(symbol.module_);
1266 // [devhost] to /liblinux/devhost.ko
1267 filename.erase(filename.begin());
1268 filename.erase(filename.end() - 1);
1269 filename = DEVHOST_LINUX_PREFIX + filename + KERNEL_MODULES_EXT_NAME;
1270 koMaps[symbol.module_] =
1271 SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_THREAD_FILE, filename);
1272 }
1273 if (koMaps[symbol.module_] == nullptr) {
1274 continue;
1275 }
1276 koMaps[symbol.module_]->AddSymbol(std::move(symbol));
1277 }
1278
1279 HLOGD("devhost loaded %zu symbolfiles", koMaps.size());
1280 for (auto &it : koMaps) {
1281 if (it.second == nullptr) {
1282 continue;
1283 }
1284 HLOGD("Load %zu symbols to %s", it.second->GetSymbols().size(),
1285 it.second->filePath_.c_str());
1286 symbolsFiles_.emplace_back(std::move(it.second));
1287 }
1288 } else {
1289 HLOGW("symbols file for devhost parse failed.");
1290 }
1291
1292 // update normal symbole files
1293 VirtualThread &kthread = GetThread(devhostPid_, devhostPid_);
1294 for (const auto &map : kthread.GetMaps()) {
1295 UpdateSymbols(map, devhostPid_);
1296 }
1297 }
1298
FixHMBundleMmap(char * filename,int pid,u16 & headerSize)1299 void VirtualRuntime::FixHMBundleMmap(char *filename, int pid, u16 &headerSize)
1300 {
1301 if (!isHM_) {
1302 return;
1303 }
1304 // fix bundle path in mmap
1305 std::string newFilename = filename;
1306 VirtualThread &thread = GetThread(pid, pid);
1307 if (NeedAdaptHMBundlePath(newFilename, thread.name_)) {
1308 size_t oldSize = strlen(filename);
1309 (void)memset_s(filename, KILO, '\0', KILO);
1310 if (strncpy_s(filename, KILO, newFilename.c_str(), newFilename.size()) != 0) {
1311 HLOGD("strncpy_s recordMmap2 failed!");
1312 }
1313 headerSize += newFilename.size() - oldSize;
1314 }
1315 }
1316
SetDevhostPid(pid_t devhost)1317 void VirtualRuntime::SetDevhostPid(pid_t devhost)
1318 {
1319 HLOGD("Set devhost pid: %d", devhost);
1320 devhostPid_ = devhost;
1321 }
1322
IsKernelThread(pid_t pid)1323 bool VirtualRuntime::IsKernelThread(pid_t pid)
1324 {
1325 if (!isHM_) {
1326 return false;
1327 }
1328 return pid == SYSMGR_PID || pid == devhostPid_;
1329 }
1330
1331 } // namespace HiPerf
1332 } // namespace Developtools
1333 } // namespace OHOS
1334