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->name);
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 // found it by name
189 auto symbolsFile = SymbolsFile::CreateSymbolsFile(map->name);
190 if (symbolsFile == nullptr) {
191 HLOGV("Failed to load CreateSymbolsFile for exec section in hap(%s)", map->name.c_str());
192 return false;
193 }
194 // update maps name if load debuginfo successfully
195 if (!symbolsFile->LoadDebugInfo(map)) {
196 HLOGV("Failed to load debuginfo for exec section in hap(%s)", map->name.c_str());
197 return false;
198 }
199
200 if (!loadSymboleWhenNeeded_) { // todo misspelling
201 symbolsFile->LoadSymbols(map);
202 }
203 symbolsFiles_.emplace_back(std::move(symbolsFile));
204 return true;
205 }
206
GetThread(pid_t pid,pid_t tid,const std::string name)207 VirtualThread &VirtualRuntime::GetThread(pid_t pid, pid_t tid, const std::string name)
208 {
209 if (userSpaceThreadMap_.find(pid) == userSpaceThreadMap_.end()) {
210 // no pid found
211 // create process first
212 CreateThread(pid, pid);
213 }
214
215 auto it = userSpaceThreadMap_.find(tid);
216 if (it == userSpaceThreadMap_.end()) {
217 // we also need thread
218 return CreateThread(pid, tid, name);
219 } else {
220 return it->second;
221 }
222 }
223
UpdateThreadMaps(pid_t pid,pid_t tid,const std::string filename,uint64_t begin,uint64_t len,uint64_t offset)224 void VirtualRuntime::UpdateThreadMaps(pid_t pid, pid_t tid, const std::string filename,
225 uint64_t begin, uint64_t len, uint64_t offset)
226 {
227 VirtualThread &thread = GetThread(pid, tid);
228 thread.CreateMapItem(filename, begin, len, offset);
229 }
230
UpdateKernelModulesSpaceMaps()231 void VirtualRuntime::UpdateKernelModulesSpaceMaps()
232 {
233 // found the kernel modules
234 std::vector<DfxMap> koMaps;
235 std::ifstream ifs("/proc/modules", std::ifstream::in);
236 if (!ifs.is_open()) {
237 perror("kernel modules read failed(/proc/modules)\n");
238 return;
239 }
240 std::string line;
241 while (getline(ifs, line)) {
242 uint64_t addr = 0;
243 uint64_t size = 0;
244 char module[line.size()];
245 /*
246 name size load map
247 hi_mipi_rx 53248 0 - Live 0xbf109000 (O)
248 hi3516cv500_hdmi 237568 0 - Live 0xbf0bb000 (O)
249 hifb 143360 0 - Live 0xbf089000 (O)
250 hi3516cv500_vo_dev 98304 0 - Live 0xbf070000 (O)
251 hi3516cv500_tde 110592 0 - Live 0xbf04a000 (O)
252 hi3516cv500_sys 36864 0 - Live 0xbf03a000 (O)
253 hi3516cv500_base 20480 5
254 hi_mipi_rx,hi3516cv500_hdmi,hifb,hi3516cv500_vo_dev,hi3516cv500_tde,hi3516cv500_sys,
255 hi3516cv500_base,sys_config,hi_proc,hi_irq,Live 0xbf000000 (O)
256 */
257 int ret = sscanf_s(line.c_str(), "%s%" PRIu64 "%*u%*s%*s 0x%" PRIx64 "", module,
258 sizeof(module), &size, &addr, sizeof(addr));
259 constexpr int numSlices {3};
260 if (ret == numSlices) {
261 auto &map = koMaps.emplace_back(addr, addr + size, 0, "", std::string(module));
262 HLOGV("add ko map %s", map.ToString().c_str());
263 } else {
264 HLOGE("unknown line %d: '%s'", ret, line.c_str());
265 }
266 }
267
268 if (std::all_of(koMaps.begin(), koMaps.end(),
269 [](const DfxMap &item) { return item.begin == 0; })) {
270 koMaps.clear();
271 HLOGW("no addr found in /proc/modules. remove all the ko");
272 }
273 if (recordCallBack_) {
274 for (const auto &map : koMaps) {
275 auto record = std::make_unique<PerfRecordMmap>(true, 0, 0, map.begin,
276 map.end - map.begin, 0, map.name);
277 recordCallBack_(std::move(record));
278 }
279 }
280 std::move(koMaps.begin(), koMaps.end(), std::back_inserter(kernelSpaceMemMaps_));
281 }
282
UpdateKernelSpaceMaps()283 void VirtualRuntime::UpdateKernelSpaceMaps()
284 {
285 // add kernel first
286 auto &map = kernelSpaceMemMaps_.emplace_back(0, std::numeric_limits<uint64_t>::max(), 0, "", KERNEL_MMAP_NAME);
287 if (recordCallBack_) {
288 auto record = std::make_unique<PerfRecordMmap>(true, 0, 0, map.begin,
289 map.end - map.begin, 0, map.name);
290 recordCallBack_(std::move(record));
291 }
292 }
293
UpdateKernelModulesSymbols()294 void VirtualRuntime::UpdateKernelModulesSymbols()
295 {
296 HLOGD("load ko symbol and build id");
297 for (auto &map : kernelSpaceMemMaps_) {
298 if (map.name == KERNEL_MMAP_NAME) {
299 continue;
300 }
301 auto kernelModuleFile = SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_MODULE_FILE, map.name);
302 if (symbolsPaths_.size() > 0) {
303 kernelModuleFile->setSymbolsFilePath(symbolsPaths_); // also load from search path
304 }
305 kernelModuleFile->LoadSymbols();
306 symbolsFiles_.emplace_back(std::move(kernelModuleFile));
307 }
308 }
309
UpdateKernelSymbols()310 void VirtualRuntime::UpdateKernelSymbols()
311 {
312 HLOGD("create a kernel mmap record");
313 // found kernel source
314 auto kernelFile = SymbolsFile::CreateSymbolsFile(KERNEL_MMAP_NAME);
315 // set symbol path If it exists
316 if (symbolsPaths_.size() > 0) {
317 kernelFile->setSymbolsFilePath(symbolsPaths_); // also load from search path
318 }
319 if (kernelFile->LoadSymbols()) {
320 auto record = std::make_unique<PerfRecordMmap>(
321 true, 0, 0, kernelFile->textExecVaddr_, kernelFile->textExecVaddrRange_,
322 kernelFile->textExecVaddrFileOffset_, KERNEL_MMAP_NAME);
323
324 if (recordCallBack_) {
325 recordCallBack_(std::move(record));
326 }
327 symbolsFiles_.emplace_back(std::move(kernelFile));
328 } else {
329 HLOGW("kernel symbol not found.\n");
330 }
331 }
332
UpdatekernelMap(uint64_t begin,uint64_t end,uint64_t offset,std::string filename)333 void VirtualRuntime::UpdatekernelMap(uint64_t begin, uint64_t end, uint64_t offset,
334 std::string filename)
335 {
336 HLOGV("update kernel map name:'%s' 0x%" PRIx64 " - 0x%" PRIx64 "@0x%08" PRIx64 "",
337 filename.c_str(), begin, end, offset);
338
339 HLOG_ASSERT(!filename.empty());
340 auto it = find(kernelSpaceMemMaps_.begin(), kernelSpaceMemMaps_.end(), filename);
341 if (it == kernelSpaceMemMaps_.end()) {
342 kernelSpaceMemMaps_.emplace_back(begin, end, offset, "", filename);
343 } else {
344 it->begin = begin;
345 it->end = end;
346 it->offset = offset;
347 it->name = filename;
348 }
349 }
350
DedupFromRecord(PerfRecordSample * recordSample)351 void VirtualRuntime::DedupFromRecord(PerfRecordSample *recordSample)
352 {
353 u64 nr = recordSample->data_.nr;
354 if (nr == 0) {
355 collectSymbolCallBack_(recordSample);
356 return;
357 }
358
359 u32 pid = recordSample->data_.pid;
360 u64 *ips = recordSample->data_.ips;
361 StackId stackId;
362 stackId.value = 0;
363 auto entry = processStackMap_.find(pid);
364 std::shared_ptr<UniqueStackTable> table = nullptr;
365 if (entry != processStackMap_.end()) {
366 table = entry->second;
367 } else {
368 table = std::make_shared<UniqueStackTable>(pid);
369 processStackMap_[pid] = table;
370 }
371
372 while (table->PutIpsInTable(&stackId, ips, nr) == 0) {
373 // try expand hashtable if collison can not resolved
374 if (!table->Resize()) {
375 HLOGW("Hashtable size limit, ip compress failed!");
376 collectSymbolCallBack_(recordSample);
377 return;
378 }
379 }
380
381 // callstack dedup success
382 recordSample->stackId_.value = stackId.value;
383 recordSample->header.size -= (sizeof(u64) * nr - sizeof(stackId));
384 recordSample->data_.nr = 0;
385 recordSample->data_.ips = nullptr;
386 recordSample->removeStack_ = true;
387 }
388
CollectDedupSymbol(kSymbolsHits & kernelSymbolsHits,uSymbolsHits & userSymbolsHits)389 void VirtualRuntime::CollectDedupSymbol(kSymbolsHits &kernelSymbolsHits,
390 uSymbolsHits &userSymbolsHits)
391 {
392 Node *node = nullptr;
393 Node *head = nullptr;
394 u32 pid;
395 for (const auto &tableEntry : processStackMap_) {
396 const auto &table = tableEntry.second;
397 pid = table->GetPid();
398 head = table->GetHeadNode();
399 const auto &idxes = table->GetUsedIndexes();
400 for (const auto idx : idxes) {
401 node = head + idx;
402 if (node->value != 0) {
403 if (node->section.inKernel) {
404 uint64_t ip = node->section.ip | KERNEL_PREFIX;
405 if (ip == PERF_CONTEXT_KERNEL || ip == PERF_CONTEXT_USER) {
406 continue;
407 }
408 kernelSymbolsHits.insert(ip);
409 } else {
410 userSymbolsHits[pid].insert(node->section.ip);
411 }
412 } else {
413 HLOGD("node value error 0x%x", idx);
414 }
415 }
416 }
417 }
418
UpdateFromRecord(PerfEventRecord & record)419 void VirtualRuntime::UpdateFromRecord(PerfEventRecord &record)
420 {
421 #ifdef HIPERF_DEBUG_TIME
422 const auto startTime = steady_clock::now();
423 #endif
424 if (record.GetType() == PERF_RECORD_SAMPLE) {
425 auto recordSample = static_cast<PerfRecordSample *>(&record);
426 UpdateFromRecord(*recordSample);
427 #ifdef HIPERF_DEBUG_TIME
428 processSampleRecordTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
429 #endif
430 } else if (record.GetType() == PERF_RECORD_MMAP) {
431 auto recordMmap = static_cast<PerfRecordMmap *>(&record);
432 UpdateFromRecord(*recordMmap);
433 #ifdef HIPERF_DEBUG_TIME
434 processMmapRecordTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
435 #endif
436 } else if (record.GetType() == PERF_RECORD_MMAP2) {
437 auto recordMmap2 = static_cast<PerfRecordMmap2 *>(&record);
438 UpdateFromRecord(*recordMmap2);
439 #ifdef HIPERF_DEBUG_TIME
440 processMmap2RecordTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
441 #endif
442 } else if (record.GetType() == PERF_RECORD_COMM) {
443 auto recordComm = static_cast<PerfRecordComm *>(&record);
444 UpdateFromRecord(*recordComm);
445 #ifdef HIPERF_DEBUG_TIME
446 processCommRecordTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
447 #endif
448 } else {
449 HLOGW("skip record type %d", record.GetType());
450 }
451 }
452
MakeCallFrame(DfxSymbol & symbol,CallFrame & callFrame)453 void VirtualRuntime::MakeCallFrame(DfxSymbol &symbol, CallFrame &callFrame)
454 {
455 callFrame.vaddrInFile_ = symbol.funcVaddr_;
456 callFrame.offsetToVaddr_ = symbol.offsetToVaddr_;
457 callFrame.symbolFileIndex_ = symbol.symbolFileIndex_;
458 callFrame.symbolName_ = symbol.GetName();
459 callFrame.symbolIndex_ = symbol.index_;
460 callFrame.filePath_ = symbol.module_.empty() ? symbol.comm_ : symbol.module_;
461 HLOG_ASSERT_MESSAGE(!callFrame.symbolName_.empty(), "%s", symbol.ToDebugString().c_str());
462 }
463
SymbolicCallFrame(PerfRecordSample & recordSample,uint64_t ip,pid_t server_pid,perf_callchain_context context)464 void VirtualRuntime::SymbolicCallFrame(PerfRecordSample &recordSample, uint64_t ip,
465 pid_t server_pid, perf_callchain_context context)
466 {
467 pid_t pid = static_cast<pid_t>(recordSample.data_.pid);
468 pid_t tid = static_cast<pid_t>(recordSample.data_.tid);
469 if (server_pid != pid) {
470 pid = tid = server_pid;
471 }
472 auto symbol = GetSymbol(ip, pid, tid, context);
473 MakeCallFrame(symbol, recordSample.callFrames_.emplace_back(ip, 0));
474 HLOGV(" (%zu)unwind symbol: %*s%s", recordSample.callFrames_.size(),
475 static_cast<int>(recordSample.callFrames_.size()), "",
476 recordSample.callFrames_.back().ToSymbolString().c_str());
477 }
478
RecoverCallStack(PerfRecordSample & recordSample)479 bool VirtualRuntime::RecoverCallStack(PerfRecordSample &recordSample)
480 {
481 auto StackTable = processStackMap_.find(recordSample.data_.pid);
482 if (StackTable == processStackMap_.end()) {
483 HLOGV("not found %" PRIu32 " pid", recordSample.data_.pid);
484 return false;
485 }
486 recordSample.ips_.clear();
487 StackTable->second->GetIpsByStackId(recordSample.stackId_, recordSample.ips_);
488 recordSample.RecoverCallStack();
489 return true;
490 }
491
SymbolicRecord(PerfRecordSample & recordSample)492 void VirtualRuntime::SymbolicRecord(PerfRecordSample &recordSample)
493 {
494 #ifdef HIPERF_DEBUG_TIME
495 const auto startTime = steady_clock::now();
496 #endif
497 // Symbolic the Call Stack
498 recordSample.callFrames_.clear();
499 perf_callchain_context context = PERF_CONTEXT_MAX;
500 pid_t server_pid;
501 if (recordSample.data_.nr == 0) {
502 server_pid = recordSample.GetServerPidof(0);
503 SymbolicCallFrame(recordSample, recordSample.data_.ip, server_pid, PERF_CONTEXT_MAX);
504 }
505 for (u64 i = 0; i < recordSample.data_.nr; i++) {
506 uint64_t ip = recordSample.data_.ips[i];
507 if (ip >= PERF_CONTEXT_MAX) {
508 std::string contextName = UpdatePerfContext(ip, context);
509 HLOGV("context switch to %s", contextName.c_str());
510 continue;
511 } else if (ip < BAD_IP_ADDRESS) {
512 // ip 0 or 1 or less than 0
513 continue;
514 }
515 server_pid = recordSample.GetServerPidof(i);
516 SymbolicCallFrame(recordSample, ip, server_pid, context);
517 }
518 #ifdef HIPERF_DEBUG_TIME
519 auto usedTime = duration_cast<microseconds>(steady_clock::now() - startTime);
520 if (usedTime.count() != 0) {
521 HLOGV("cost %0.3f ms to symbolic ", usedTime.count() / MS_DURATION);
522 }
523 symbolicRecordTimes_ += usedTime;
524 #endif
525 }
526
NeedDropKernelCallChain(PerfRecordSample & sample)527 void VirtualRuntime::NeedDropKernelCallChain(PerfRecordSample &sample)
528 {
529 // only do this in record mode.
530 if (recordCallBack_ == nullptr || needkernelCallChain_ ||
531 !sample.inKernel() || sample.data_.nr == 0) {
532 return;
533 }
534
535 u64 skip = 0;
536 u64 skip_pid = 0;
537 u64 *ips = sample.data_.ips;
538 for (; skip < sample.data_.nr; skip++) {
539 if (ips[skip] == PERF_CONTEXT_KERNEL) {
540 skip_pid++;
541 }
542 if (ips[skip] == PERF_CONTEXT_USER) {
543 break;
544 }
545 }
546 sample.skipKernel_ = skip;
547 sample.data_.nr -= skip;
548 sample.header.size -= sizeof(u64) * skip;
549 if (sample.data_.server_nr > 0) {
550 sample.skipPid_ = skip_pid;
551 sample.data_.server_nr -= skip_pid;
552 sample.header.size -= sizeof(u64) * skip_pid;
553 }
554 }
555
UnwindFromRecord(PerfRecordSample & recordSample)556 void VirtualRuntime::UnwindFromRecord(PerfRecordSample &recordSample)
557 {
558 #if defined(is_ohos) && is_ohos
559 #ifdef HIPERF_DEBUG_TIME
560 const auto startTime = steady_clock::now();
561 #endif
562 HLOGV("unwind record (time:%llu)", recordSample.data_.time);
563 // if we have userstack ?
564 if (recordSample.data_.stack_size > 0) {
565 pid_t server_pid = recordSample.GetUstackServerPid();
566 pid_t pid = static_cast<pid_t>(recordSample.data_.pid);
567 pid_t tid = static_cast<pid_t>(recordSample.data_.tid);
568 if (server_pid != pid) {
569 pid = tid = server_pid;
570 }
571 auto &thread = UpdateThread(pid, tid);
572 callstack_.UnwindCallStack(thread, recordSample.data_.user_abi == PERF_SAMPLE_REGS_ABI_32,
573 recordSample.data_.user_regs, recordSample.data_.reg_nr,
574 recordSample.data_.stack_data, recordSample.data_.dyn_size,
575 recordSample.callFrames_);
576 #ifdef HIPERF_DEBUG_TIME
577 unwindCallStackTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
578 #endif
579 size_t oldSize = recordSample.callFrames_.size();
580 HLOGV("unwind %zu", recordSample.callFrames_.size());
581 callstack_.ExpandCallStack(thread.tid_, recordSample.callFrames_, callstackMergeLevel_);
582 HLOGV("expand %zu (+%zu)", recordSample.callFrames_.size(),
583 recordSample.callFrames_.size() - oldSize);
584
585 recordSample.ReplaceWithCallStack(oldSize);
586 }
587
588 #ifdef HIPERF_DEBUG_TIME
589 unwindFromRecordTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
590 #endif
591
592 NeedDropKernelCallChain(recordSample);
593 // we will not do this in non record mode.
594 if (dedupStack_ && recordCallBack_ != nullptr) {
595 DedupFromRecord(&recordSample);
596 }
597 #endif
598
599 // we will not do this in record mode
600 if (recordCallBack_ == nullptr) {
601 if (dedupStack_ && recordSample.stackId_.section.id > 0 && recordSample.data_.nr == 0) {
602 RecoverCallStack(recordSample);
603 }
604 // find the symbols , reabuild frame info
605 SymbolicRecord(recordSample);
606 }
607 }
608
SetCollectSymbolCallBack(CollectSymbolCallBack collectSymbolCallBack)609 void VirtualRuntime::SetCollectSymbolCallBack(CollectSymbolCallBack collectSymbolCallBack)
610 {
611 collectSymbolCallBack_ = collectSymbolCallBack;
612 }
613
UpdateFromRecord(PerfRecordSample & recordSample)614 void VirtualRuntime::UpdateFromRecord(PerfRecordSample &recordSample)
615 {
616 UpdateThread(recordSample.data_.pid, recordSample.data_.tid);
617 if (recordSample.data_.server_nr) {
618 // update all server threads
619 for (size_t i = 0; i < recordSample.data_.server_nr; i++) {
620 pid_t pid = static_cast<pid_t>(recordSample.data_.server_pids[i]);
621 UpdateThread(pid, pid);
622 }
623 }
624 // unwind
625 if (disableUnwind_) {
626 return;
627 } else {
628 UnwindFromRecord(recordSample);
629 }
630 }
631
UpdateFromRecord(PerfRecordMmap & recordMmap)632 void VirtualRuntime::UpdateFromRecord(PerfRecordMmap &recordMmap)
633 {
634 HLOGV(" MMAP: size %d pid %u tid %u", recordMmap.header.size, recordMmap.data_.pid,
635 recordMmap.data_.tid);
636 HLOGV(" MMAP: %s dso '%s' (0x%llx-0x%llx)@0x%llx", recordMmap.inKernel() ? "kernel" : "user",
637 recordMmap.data_.filename, recordMmap.data_.addr,
638 recordMmap.data_.addr + recordMmap.data_.len, recordMmap.data_.pgoff);
639 // kernel mmap
640 // don't overwrite the vailed mmap , so we also check the recordMmap.data_.len
641 if (IsKernelThread(recordMmap.data_.pid)) {
642 UpdateKernelThreadMap(recordMmap.data_.pid, recordMmap.data_.addr,
643 recordMmap.data_.len, recordMmap.data_.filename);
644 } else if (recordMmap.inKernel()) {
645 UpdatekernelMap(recordMmap.data_.addr, recordMmap.data_.addr + recordMmap.data_.len,
646 recordMmap.data_.pgoff, recordMmap.data_.filename);
647 } else {
648 NeedAdaptSandboxPath(recordMmap.data_.filename, recordMmap.data_.pid, recordMmap.header.size);
649 FixHMBundleMmap(recordMmap.data_.filename, recordMmap.data_.pid, recordMmap.header.size);
650 UpdateThreadMaps(recordMmap.data_.pid, recordMmap.data_.tid, recordMmap.data_.filename,
651 recordMmap.data_.addr, recordMmap.data_.len, recordMmap.data_.pgoff);
652 UpdateSymbols(recordMmap.data_.filename);
653 }
654 }
655
CheckValidSandBoxMmap(PerfRecordMmap2 & recordMmap2)656 bool VirtualRuntime::CheckValidSandBoxMmap(PerfRecordMmap2 &recordMmap2)
657 {
658 static std::shared_ptr<DfxMap> prevMap;
659 if ((recordMmap2.data_.prot & PROT_EXEC) != 0) {
660 // fake first segment, when second segment come.
661 auto symFile = SymbolsFile::CreateSymbolsFile(SYMBOL_ELF_FILE, recordMmap2.data_.filename);
662 if (symFile == nullptr) {
663 HLOGD("CheckValidSandBoxMmap Failed to create symbolFile!");
664 return false;
665 }
666
667 std::shared_ptr<DfxMap> curMap;
668 if (strstr(recordMmap2.data_.filename, ".hap") != nullptr) {
669 curMap = std::make_shared<DfxMap>(
670 recordMmap2.data_.addr,
671 recordMmap2.data_.addr + recordMmap2.data_.len,
672 recordMmap2.data_.pgoff,
673 "", // prot
674 recordMmap2.data_.filename
675 );
676 curMap->prevMap = prevMap;
677 }
678
679 if (!symFile->LoadDebugInfo(curMap)) {
680 HLOGD("CheckValidSandBoxMmap Failed to load debuginfo!");
681 return false;
682 }
683
684 if (!loadSymboleWhenNeeded_) {
685 symFile->LoadSymbols(curMap);
686 }
687
688 if (strstr(recordMmap2.data_.filename, ".hap") == nullptr) {
689 auto elfLoadInfoMap = symFile->GetPtLoads();
690 u64 begin = recordMmap2.data_.addr - elfLoadInfoMap[0].mmapLen;
691 u64 len = elfLoadInfoMap[0].mmapLen;
692 u64 pgoff = elfLoadInfoMap[0].offset & (~(elfLoadInfoMap[0].align - 1));
693 std::unique_ptr<PerfRecordMmap2> mmap2FirstSeg =
694 std::make_unique<PerfRecordMmap2>(recordMmap2.inKernel(), recordMmap2.data_.pid, recordMmap2.data_.tid,
695 begin, len, pgoff, 0, 0, 0, PROT_READ, 0, std::string(recordMmap2.data_.filename));
696 UpdateThreadMaps(mmap2FirstSeg->data_.pid, mmap2FirstSeg->data_.tid, mmap2FirstSeg->data_.filename,
697 mmap2FirstSeg->data_.addr, mmap2FirstSeg->data_.len, mmap2FirstSeg->data_.pgoff);
698 recordCallBack_(std::move(mmap2FirstSeg));
699 } else {
700 auto elfLoadInfoMap = symFile->GetPtLoads();
701 u64 begin = recordMmap2.data_.addr - elfLoadInfoMap[0].mmapLen;
702 u64 len = elfLoadInfoMap[0].mmapLen;
703 u64 pgoff = elfLoadInfoMap[0].offset & (~(elfLoadInfoMap[0].align - 1));
704 std::unique_ptr<PerfRecordMmap2> mmap2FirstSeg =
705 std::make_unique<PerfRecordMmap2>(recordMmap2.inKernel(), recordMmap2.data_.pid, recordMmap2.data_.tid,
706 begin, len, pgoff, 0, 0, 0, PROT_READ, 0, curMap->name);
707 UpdateThreadMaps(mmap2FirstSeg->data_.pid, mmap2FirstSeg->data_.tid, curMap->name,
708 mmap2FirstSeg->data_.addr, mmap2FirstSeg->data_.len, mmap2FirstSeg->data_.pgoff);
709 recordCallBack_(std::move(mmap2FirstSeg));
710
711 std::unique_ptr<PerfRecordMmap2> mmap2SecondSegment =
712 std::make_unique<PerfRecordMmap2>(recordMmap2.inKernel(), recordMmap2.data_.pid, recordMmap2.data_.tid,
713 recordMmap2.data_.addr,
714 recordMmap2.data_.len,
715 recordMmap2.data_.pgoff - prevMap->offset, // minus load offset of hap
716 0, 0, 0, recordMmap2.data_.prot, 0, curMap->name);
717 UpdateThreadMaps(mmap2SecondSegment->data_.pid, mmap2SecondSegment->data_.tid, curMap->name,
718 mmap2SecondSegment->data_.addr, mmap2SecondSegment->data_.len, mmap2SecondSegment->data_.pgoff);
719 recordCallBack_(std::move(mmap2SecondSegment));
720 recordMmap2.discard_ = true;
721 }
722 symbolsFiles_.emplace_back(std::move(symFile));
723 return true;
724 } else if (recordMmap2.data_.pgoff == 0) {
725 recordMmap2.discard_ = true;
726 }
727
728 if (strstr(recordMmap2.data_.filename, ".hap") != nullptr) {
729 prevMap = std::make_shared<DfxMap>(
730 recordMmap2.data_.addr,
731 recordMmap2.data_.addr + recordMmap2.data_.len,
732 recordMmap2.data_.pgoff,
733 "", // prot
734 recordMmap2.data_.filename
735 );
736 HLOGD("CheckValidSandBoxMmap Update prev map!");
737 }
738 return !recordMmap2.discard_;
739 }
740
UpdateFromRecord(PerfRecordMmap2 & recordMmap2)741 void VirtualRuntime::UpdateFromRecord(PerfRecordMmap2 &recordMmap2)
742 {
743 if (!OHOS::HiviewDFX::DfxMaps::IsLegalMapItem(recordMmap2.data_.filename)) {
744 return;
745 }
746
747 HLOGV(" MMAP2: size %d pid %u tid %u", recordMmap2.header.size, recordMmap2.data_.pid,
748 recordMmap2.data_.tid);
749 HLOGV(" MMAP2: %s dso '%s' (0x%llx-0x%llx)@0x%llx prot:%u", recordMmap2.inKernel() ? "kernel" : "user",
750 recordMmap2.data_.filename, recordMmap2.data_.addr,
751 recordMmap2.data_.addr + recordMmap2.data_.len, recordMmap2.data_.pgoff, recordMmap2.data_.prot);
752
753 if (recordCallBack_) {
754 if (NeedAdaptSandboxPath(recordMmap2.data_.filename, recordMmap2.data_.pid, recordMmap2.header.size)) {
755 FixHMBundleMmap(recordMmap2.data_.filename, recordMmap2.data_.pid, recordMmap2.header.size);
756 if (!CheckValidSandBoxMmap(recordMmap2)) {
757 return;
758 }
759 }
760 }
761 UpdateThreadMaps(recordMmap2.data_.pid, recordMmap2.data_.tid, recordMmap2.data_.filename,
762 recordMmap2.data_.addr, recordMmap2.data_.len, recordMmap2.data_.pgoff);
763 UpdateSymbols(recordMmap2.data_.filename);
764 }
765
UpdateFromRecord(PerfRecordComm & recordComm)766 void VirtualRuntime::UpdateFromRecord(PerfRecordComm &recordComm)
767 {
768 recordComm.DumpLog(__FUNCTION__);
769 UpdateThread(recordComm.data_.pid, recordComm.data_.tid, recordComm.data_.comm);
770 }
771
SetRecordMode(RecordCallBack recordCallBack)772 void VirtualRuntime::SetRecordMode(RecordCallBack recordCallBack)
773 {
774 recordCallBack_ = recordCallBack;
775 }
776
UpdateSymbols(std::string fileName)777 void VirtualRuntime::UpdateSymbols(std::string fileName)
778 {
779 HLOGD("try to find symbols for file: %s", fileName.c_str());
780 #ifdef HIPERF_DEBUG_TIME
781 const auto startTime = steady_clock::now();
782 #endif
783 for (const auto &symbolsFile : symbolsFiles_) {
784 if (symbolsFile->filePath_ == fileName) {
785 HLOGV("already have '%s'", fileName.c_str());
786 return;
787 }
788 }
789 // found it by name
790 auto symbolsFile = SymbolsFile::CreateSymbolsFile(fileName);
791 if (enableDebugInfoSymbolic_ && symbolsFile->symbolFileType_ == SymbolsFileType::SYMBOL_ELF_FILE) {
792 symbolsFile->EnableMiniDebugInfo();
793 }
794 // set symbol path If it exists
795 if (symbolsPaths_.size() > 0) {
796 symbolsFile->setSymbolsFilePath(symbolsPaths_); // also load from search path
797 }
798 if (loadSymboleWhenNeeded_) {
799 // load it when we need it
800 symbolsFiles_.emplace_back(std::move(symbolsFile));
801 } else if (symbolsFile->LoadSymbols()) {
802 symbolsFiles_.emplace_back(std::move(symbolsFile));
803 } else {
804 HLOGW("symbols file for '%s' not found.", fileName.c_str());
805 }
806 #ifdef HIPERF_DEBUG_TIME
807 auto usedTime = duration_cast<microseconds>(steady_clock::now() - startTime);
808 if (usedTime.count() != 0) {
809 HLOGV("cost %0.3f ms to load '%s'", usedTime.count() / MS_DURATION, fileName.c_str());
810 }
811 updateSymbolsTimes_ += usedTime;
812 #endif
813 }
814
GetKernelSymbol(uint64_t ip,const std::vector<DfxMap> & memMaps,const VirtualThread & thread)815 const DfxSymbol VirtualRuntime::GetKernelSymbol(uint64_t ip, const std::vector<DfxMap> &memMaps,
816 const VirtualThread &thread)
817 {
818 DfxSymbol vaddrSymbol(ip, thread.name_);
819 for (auto &map : memMaps) {
820 if (ip > map.begin && ip < map.end) {
821 HLOGM("found addr 0x%" PRIx64 " in kernel map 0x%" PRIx64 " - 0x%" PRIx64 " from %s",
822 ip, map.begin, map.end, map.name.c_str());
823 vaddrSymbol.module_ = map.name;
824 // found symbols by file name
825 for (auto &symbolsFile : symbolsFiles_) {
826 if (symbolsFile->filePath_ == map.name) {
827 vaddrSymbol.symbolFileIndex_ = symbolsFile->id_;
828 vaddrSymbol.fileVaddr_ =
829 symbolsFile->GetVaddrInSymbols(ip, map.begin, map.offset);
830 perf_callchain_context context = PERF_CONTEXT_KERNEL;
831 if (GetSymbolCache(ip, vaddrSymbol, context)) {
832 return vaddrSymbol;
833 }
834 HLOGV("found symbol vaddr 0x%" PRIx64 " for runtime vaddr 0x%" PRIx64
835 " at '%s'",
836 vaddrSymbol.fileVaddr_, ip, map.name.c_str());
837 if (!symbolsFile->SymbolsLoaded()) {
838 symbolsFile->LoadSymbols();
839 }
840 DfxSymbol foundSymbols = symbolsFile->GetSymbolWithVaddr(vaddrSymbol.fileVaddr_);
841 foundSymbols.taskVaddr_ = ip;
842 if (!foundSymbols.IsValid()) {
843 HLOGW("addr 0x%" PRIx64 " vaddr 0x%" PRIx64 " NOT found in symbol file %s",
844 ip, vaddrSymbol.fileVaddr_, map.name.c_str());
845 return vaddrSymbol;
846 } else {
847 return foundSymbols;
848 }
849 }
850 }
851 HLOGW("addr 0x%" PRIx64 " in map but NOT found the symbol file %s", ip,
852 map.name.c_str());
853 } else {
854 HLOGM("addr 0x%" PRIx64 " not in map 0x%" PRIx64 " - 0x%" PRIx64 " from %s", ip,
855 map.begin, map.end, map.name.c_str());
856 }
857 }
858 return vaddrSymbol;
859 }
860
GetKernelThreadSymbol(uint64_t ip,const VirtualThread & thread)861 const DfxSymbol VirtualRuntime::GetKernelThreadSymbol(uint64_t ip, const VirtualThread &thread)
862 {
863 DfxSymbol vaddrSymbol(ip, thread.name_);
864 int64_t mapIndex = thread.FindMapIndexByAddr(ip);
865 if (mapIndex < 0) {
866 #ifdef HIPERF_DEBUG
867 thread.ReportVaddrMapMiss(ip);
868 #endif
869 return vaddrSymbol;
870 }
871
872 auto map = thread.GetMaps()[mapIndex];
873 HLOGM("found addr 0x%" PRIx64 " in kthread map 0x%" PRIx64 " - 0x%" PRIx64 " from %s",
874 ip, map->begin, map->end, map->name.c_str());
875 // found symbols by file name
876 for (auto &symbolsFile : symbolsFiles_) {
877 if (symbolsFile->filePath_ == map->name) {
878 vaddrSymbol.symbolFileIndex_ = symbolsFile->id_;
879 vaddrSymbol.module_ = map->name;
880 vaddrSymbol.fileVaddr_ =
881 symbolsFile->GetVaddrInSymbols(ip, map->begin, map->offset);
882 perf_callchain_context context = PERF_CONTEXT_MAX;
883 if (GetSymbolCache(ip, vaddrSymbol, context)) {
884 return vaddrSymbol;
885 }
886 HLOGV("found symbol vaddr 0x%" PRIx64 " for runtime vaddr 0x%" PRIx64 " at '%s'",
887 vaddrSymbol.fileVaddr_, ip, map->name.c_str());
888 if (!symbolsFile->SymbolsLoaded()) {
889 symbolsFile->LoadDebugInfo();
890 symbolsFile->LoadSymbols(map);
891 }
892 auto foundSymbols = symbolsFile->GetSymbolWithVaddr(vaddrSymbol.fileVaddr_);
893 foundSymbols.taskVaddr_ = ip;
894 if (!foundSymbols.IsValid()) {
895 HLOGW("addr 0x%" PRIx64 " vaddr 0x%" PRIx64 " NOT found in symbol file %s",
896 ip, vaddrSymbol.fileVaddr_, map->name.c_str());
897 return vaddrSymbol;
898 } else {
899 return foundSymbols;
900 }
901 }
902 }
903 HLOGW("addr 0x%" PRIx64 " in map but NOT found the symbol file %s", ip,
904 map->name.c_str());
905 return vaddrSymbol;
906 }
907
GetUserSymbol(uint64_t ip,const VirtualThread & thread)908 const DfxSymbol VirtualRuntime::GetUserSymbol(uint64_t ip, const VirtualThread &thread)
909 {
910 DfxSymbol vaddrSymbol(ip, thread.name_);
911 int64_t mapIndex = thread.FindMapIndexByAddr(ip);
912 if (mapIndex >= 0) {
913 auto map = thread.GetMaps()[mapIndex];
914 SymbolsFile *symbolsFile = thread.FindSymbolsFileByMap(map);
915 if (symbolsFile != nullptr) {
916 vaddrSymbol.symbolFileIndex_ = symbolsFile->id_;
917 vaddrSymbol.module_ = map->name;
918 vaddrSymbol.fileVaddr_ =
919 symbolsFile->GetVaddrInSymbols(ip, map->begin, map->offset);
920 perf_callchain_context context = PERF_CONTEXT_USER;
921 if (GetSymbolCache(ip, vaddrSymbol, context)) {
922 return vaddrSymbol;
923 }
924 HLOGV("found symbol vaddr 0x%" PRIx64 " for runtime vaddr 0x%" PRIx64 " at '%s'",
925 vaddrSymbol.fileVaddr_, ip, map->name.c_str());
926 if (!symbolsFile->SymbolsLoaded()) {
927 symbolsFile->LoadSymbols(map);
928 }
929 auto foundSymbols = symbolsFile->GetSymbolWithVaddr(vaddrSymbol.fileVaddr_);
930 foundSymbols.taskVaddr_ = ip;
931 if (!foundSymbols.IsValid()) {
932 HLOGW("addr 0x%" PRIx64 " vaddr 0x%" PRIx64 " NOT found in symbol file %s", ip,
933 vaddrSymbol.fileVaddr_, map->name.c_str());
934 return vaddrSymbol;
935 } else {
936 return foundSymbols;
937 }
938 } else {
939 HLOGW("addr 0x%" PRIx64 " in map but NOT found the symbol file %s", ip,
940 map->name.c_str());
941 }
942 } else {
943 #ifdef HIPERF_DEBUG
944 thread.ReportVaddrMapMiss(ip);
945 #endif
946 }
947 return vaddrSymbol;
948 }
949
GetSymbolCache(uint64_t ip,DfxSymbol & symbol,const perf_callchain_context & context)950 bool VirtualRuntime::GetSymbolCache(uint64_t ip, DfxSymbol &symbol,
951 const perf_callchain_context &context)
952 {
953 if (context == PERF_CONTEXT_MAX and kThreadSymbolCache_.count(ip)) {
954 if (kThreadSymbolCache_.find(symbol.fileVaddr_) == kThreadSymbolCache_.end()) {
955 return false;
956 }
957 symbol = kThreadSymbolCache_[symbol.fileVaddr_];
958 symbol.hit_++;
959 HLOGV("hit kernel thread cache 0x%" PRIx64 " %d", ip, symbol.hit_);
960 return true;
961 } else if (context != PERF_CONTEXT_USER and kernelSymbolCache_.count(ip)) {
962 if (kernelSymbolCache_.find(symbol.fileVaddr_) == kernelSymbolCache_.end()) {
963 return false;
964 }
965 symbol = kernelSymbolCache_[symbol.fileVaddr_];
966 symbol.hit_++;
967 HLOGV("hit kernel cache 0x%" PRIx64 " %d", ip, symbol.hit_);
968 return true;
969 } else if (userSymbolCache_.count(symbol.fileVaddr_) != 0) {
970 const DfxSymbol &cachedSymbol = userSymbolCache_[symbol.fileVaddr_];
971 // must be the same file
972 if (cachedSymbol.module_ != symbol.module_) {
973 return false;
974 }
975 symbol = cachedSymbol;
976 symbol.hit_++;
977 HLOGV("hit user cache 0x%" PRIx64 " %d %s", ip, symbol.hit_,
978 symbol.ToDebugString().c_str());
979 return true;
980 } else {
981 HLOGM("cache miss k %zu u %zu kt %zu", kernelSymbolCache_.size(),
982 userSymbolCache_.size(), kThreadSymbolCache_.size());
983 }
984 return false;
985 }
986
GetSymbol(uint64_t ip,pid_t pid,pid_t tid,const perf_callchain_context & context)987 DfxSymbol VirtualRuntime::GetSymbol(uint64_t ip, pid_t pid, pid_t tid, const perf_callchain_context &context)
988 {
989 HLOGV("try find tid %u ip 0x%" PRIx64 " in %zu symbolsFiles\n", tid, ip, symbolsFiles_.size());
990 DfxSymbol symbol;
991
992 if (IsKernelThread(pid)) {
993 VirtualThread &kthread = GetThread(pid, tid);
994 HLOGM("try found addr in kernel thread %u with %zu maps", pid,
995 kthread.GetMaps().size());
996 symbol = GetKernelThreadSymbol(ip, kthread);
997 HLOGM("add addr to kernel thread cache 0x%" PRIx64 " cache size %zu", ip,
998 kThreadSymbolCache_.size());
999 kThreadSymbolCache_[symbol.fileVaddr_] = symbol;
1000 if (symbol.IsValid()) {
1001 return symbol;
1002 }
1003 }
1004
1005 if (context == PERF_CONTEXT_USER or (context == PERF_CONTEXT_MAX and !symbol.IsValid())) {
1006 // check userspace memmap
1007 symbol = GetUserSymbol(ip, GetThread(pid, tid));
1008 if (userSymbolCache_.find(symbol.fileVaddr_) == userSymbolCache_.end()) {
1009 userSymbolCache_.reserve(USER_SYMBOL_CACHE_LIMIT);
1010 }
1011 userSymbolCache_[symbol.fileVaddr_] = symbol;
1012 HLOGV("cache ip 0x%" PRIx64 " to %s", ip,
1013 userSymbolCache_[symbol.fileVaddr_].ToDebugString().c_str());
1014 }
1015
1016 if (context == PERF_CONTEXT_KERNEL or (context == PERF_CONTEXT_MAX and !symbol.IsValid())) {
1017 // check kernelspace
1018 HLOGM("try found addr in kernelspace %zu maps", kernelSpaceMemMaps_.size());
1019 symbol = GetKernelSymbol(ip, kernelSpaceMemMaps_, GetThread(pid, tid));
1020 HLOGM("add addr to kernel cache 0x%" PRIx64 " cache size %zu", ip,
1021 kernelSymbolCache_.size());
1022 kernelSymbolCache_[symbol.fileVaddr_] = symbol;
1023 }
1024 return symbol;
1025 }
1026
SetSymbolsPaths(const std::vector<std::string> & symbolsPaths)1027 bool VirtualRuntime::SetSymbolsPaths(const std::vector<std::string> &symbolsPaths)
1028 {
1029 std::unique_ptr<SymbolsFile> symbolsFile = SymbolsFile::CreateSymbolsFile(SYMBOL_UNKNOW_FILE);
1030 // we need check if the path is accessible
1031 bool accessible = symbolsFile->setSymbolsFilePath(symbolsPaths);
1032 if (accessible) {
1033 symbolsPaths_ = symbolsPaths;
1034 } else {
1035 if (!symbolsPaths.empty()) {
1036 printf("some symbols path unable access\n");
1037 }
1038 }
1039 return accessible;
1040 }
1041
UpdateFromPerfData(const std::vector<SymbolFileStruct> & symbolFileStructs)1042 void VirtualRuntime::UpdateFromPerfData(const std::vector<SymbolFileStruct> &symbolFileStructs)
1043 {
1044 // review: if we need move to some other place ?
1045 HLOG_ASSERT_MESSAGE(symbolsFiles_.size() == 0, " symbolsFiles_ size is %zu",
1046 symbolsFiles_.size());
1047 for (const auto &symbolFileStruct : symbolFileStructs) {
1048 HLOGV("symbolFileStruct.filePath_:'%s'", symbolFileStruct.filePath_.c_str());
1049 HLOGV("symbolFileStruct.buildId_:'%s'", symbolFileStruct.buildId_.c_str());
1050 HLOGV("process symbols file:'%s':'%s'", symbolFileStruct.filePath_.c_str(),
1051 symbolFileStruct.buildId_.c_str());
1052
1053 // load from symbolFileStruct (perf.data)
1054 std::unique_ptr<SymbolsFile> symbolsFile = SymbolsFile::LoadSymbolsFromSaved(symbolFileStruct);
1055
1056 // reaload from sybol path If it exists
1057 if (symbolsPaths_.size() > 0) {
1058 HLOGV("try again with symbolsPaths setup");
1059 symbolsFile->setSymbolsFilePath(symbolsPaths_); // also load from search path
1060 // use give us path ,we must reload it.
1061 symbolsFile->LoadSymbols();
1062 }
1063 symbolsFile->id_ = static_cast<int32_t>(symbolsFiles_.size());
1064 symbolsFiles_.emplace_back(std::move(symbolsFile));
1065 }
1066 }
1067
ImportUniqueStackNodes(const std::vector<UniStackTableInfo> & uniStackTableInfos)1068 void VirtualRuntime::ImportUniqueStackNodes(const std::vector<UniStackTableInfo>& uniStackTableInfos)
1069 {
1070 for (const UniStackTableInfo& item : uniStackTableInfos) {
1071 auto stackTable = std::make_shared<UniqueStackTable>(item.pid, item.tableSize);
1072 for (const UniStackNode& node : item.nodes) {
1073 stackTable->ImportNode(node.index, node.node);
1074 }
1075 processStackMap_[item.pid] = std::move(stackTable);
1076 }
1077 }
1078
1079 /*
1080 ARM functions
1081 The table below lists the symbols exported by the vDSO.
1082
1083 symbol version
1084 ────────────────────────────────────────────────────────────
1085 __vdso_gettimeofday LINUX_2.6 (exported since Linux 4.1)
1086 __vdso_clock_gettime LINUX_2.6 (exported since Linux 4.1)
1087
1088 Additionally, the ARM port has a code page full of utility
1089 functions. Since it's just a raw page of code, there is no ELF
1090 information for doing symbol lookups or versioning. It does
1091 provide support for different versions though.
1092
1093 For information on this code page, it's best to refer to the
1094 kernel documentation as it's extremely detailed and covers
1095 everything you need to know:
1096 Documentation/arm/kernel_user_helpers.txt.
1097
1098 aarch64 functions
1099 The table below lists the symbols exported by the vDSO.
1100
1101 symbol version
1102 ──────────────────────────────────────
1103 __kernel_rt_sigreturn LINUX_2.6.39
1104 __kernel_gettimeofday LINUX_2.6.39
1105 __kernel_clock_gettime LINUX_2.6.39
1106 __kernel_clock_getres LINUX_2.6.39
1107 */
LoadVdso()1108 void VirtualRuntime::LoadVdso()
1109 {
1110 #if !is_mingw
1111 VirtualThread myThread(getpid(), symbolsFiles_);
1112 myThread.ParseMap();
1113 for (const auto &map : myThread.GetMaps()) {
1114 if (map->name == MMAP_VDSO_NAME) {
1115 std::string memory(map->end - map->begin, '\0');
1116 std::copy(reinterpret_cast<char *>((map->begin)), reinterpret_cast<char *>((map->end)),
1117 &memory[0]);
1118 std::string tempPath("/data/local/tmp/");
1119 std::string tempFileName = tempPath + MMAP_VDSO_NAME;
1120 if (!WriteStringToFile(tempFileName, memory)) {
1121 printf("vdso temp file create fail at %s\n", tempFileName.c_str());
1122 } else {
1123 HLOGD("vdso temp file create at %s:%zu", tempFileName.c_str(), memory.size());
1124 auto symbolsFile = SymbolsFile::CreateSymbolsFile(MMAP_VDSO_NAME);
1125 symbolsFile->setSymbolsFilePath(tempPath); // also load from search path
1126 symbolsFiles_.emplace_back(std::move(symbolsFile));
1127 return;
1128 }
1129 }
1130 }
1131 HLOGD("no vdso found");
1132 #endif
1133 }
1134
UpdateServiceSpaceMaps()1135 void VirtualRuntime::UpdateServiceSpaceMaps()
1136 {
1137 VirtualThread &kthread = GetThread(SYSMGR_PID, SYSMGR_PID);
1138 kthread.ParseServiceMap(SYSMGR_FILE_NAME);
1139 if (recordCallBack_) {
1140 for (const auto &map : kthread.GetMaps()) {
1141 auto record =
1142 std::make_unique<PerfRecordMmap>(true, SYSMGR_PID, SYSMGR_PID,
1143 map->begin, map->end - map->begin,
1144 0, SYSMGR_FILE_NAME);
1145 recordCallBack_(std::move(record));
1146 }
1147 }
1148 }
1149
UpdateServiceSymbols()1150 void VirtualRuntime::UpdateServiceSymbols()
1151 {
1152 HLOGD("try to update kernel thread symbols for kernel service");
1153 std::string fileName = SYSMGR_FILE_NAME;
1154 auto symbolsFile = SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_THREAD_FILE, fileName);
1155
1156 HLOGD("add kernel service symbol file: %s", fileName.c_str());
1157 if (symbolsFile->LoadSymbols()) {
1158 symbolsFiles_.emplace_back(std::move(symbolsFile));
1159 } else {
1160 HLOGW("symbols file for '%s' not found.", fileName.c_str());
1161 }
1162 }
1163
UpdateKernelThreadMap(pid_t pid,uint64_t begin,uint64_t len,std::string filename)1164 void VirtualRuntime::UpdateKernelThreadMap(pid_t pid, uint64_t begin, uint64_t len,
1165 std::string filename)
1166 {
1167 HLOGV("update kernel thread map pid %u name:'%s'", pid, filename.c_str());
1168
1169 VirtualThread &thread = GetThread(pid, pid);
1170 thread.CreateMapItem(filename, begin, len, 0u);
1171 }
1172
UpdateDevhostSpaceMaps()1173 void VirtualRuntime::UpdateDevhostSpaceMaps()
1174 {
1175 VirtualThread &kthread = GetThread(devhostPid_, devhostPid_);
1176 kthread.ParseDevhostMap(devhostPid_);
1177 if (recordCallBack_) {
1178 for (const auto &map : kthread.GetMaps()) {
1179 auto record =
1180 std::make_unique<PerfRecordMmap>(false, devhostPid_, devhostPid_,
1181 map->begin, map->end - map->begin,
1182 0, map->name);
1183 recordCallBack_(std::move(record));
1184 }
1185 }
1186 }
1187
UpdateDevhostSymbols()1188 void VirtualRuntime::UpdateDevhostSymbols()
1189 {
1190 HLOGD("try to update kernel thread symbols for devhost");
1191 auto kallsyms = SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_THREAD_FILE, DEVHOST_FILE_NAME);
1192 // file name of devhost.ko
1193 std::map<std::string_view, std::unique_ptr<SymbolsFile>> koMaps;
1194 koMaps[DEVHOST_FILE_NAME] =
1195 SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_THREAD_FILE, DEVHOST_LINUX_FILE_NAME);
1196
1197 if (kallsyms->LoadSymbols()) {
1198 for (auto &symbol : kallsyms->GetSymbols()) {
1199 if (koMaps.find(symbol.module_) == koMaps.end()) {
1200 std::string filename = std::string(symbol.module_);
1201 // [devhost] to /liblinux/devhost.ko
1202 filename.erase(filename.begin());
1203 filename.erase(filename.end() - 1);
1204 filename = DEVHOST_LINUX_PREFIX + filename + KERNEL_MODULES_EXT_NAME;
1205 koMaps[symbol.module_] =
1206 SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_THREAD_FILE, filename);
1207 }
1208 koMaps[symbol.module_]->AddSymbol(std::move(symbol));
1209 }
1210
1211 HLOGD("devhost loaded %zu symbolfiles", koMaps.size());
1212 for (auto &it : koMaps) {
1213 HLOGD("Load %zu symbols to %s", it.second->GetSymbols().size(),
1214 it.second->filePath_.c_str());
1215 symbolsFiles_.emplace_back(std::move(it.second));
1216 }
1217 } else {
1218 HLOGW("symbols file for devhost parse failed.");
1219 }
1220
1221 // update normal symbole files
1222 VirtualThread &kthread = GetThread(devhostPid_, devhostPid_);
1223 for (const auto &map : kthread.GetMaps()) {
1224 UpdateSymbols(map->name);
1225 }
1226 }
1227
FixHMBundleMmap(char * filename,int pid,u16 & headerSize)1228 void VirtualRuntime::FixHMBundleMmap(char *filename, int pid, u16 &headerSize)
1229 {
1230 if (!isHM_) {
1231 return;
1232 }
1233 // fix bundle path in mmap
1234 std::string newFilename = filename;
1235 VirtualThread &thread = GetThread(pid, pid);
1236 if (NeedAdaptHMBundlePath(newFilename, thread.name_)) {
1237 size_t oldSize = strlen(filename);
1238 (void)memset_s(filename, KILO, '\0', KILO);
1239 if (strncpy_s(filename, KILO, newFilename.c_str(), newFilename.size()) != 0) {
1240 HLOGD("strncpy_s recordMmap2 failed!");
1241 }
1242 headerSize += newFilename.size() - oldSize;
1243 }
1244 }
1245
SetDevhostPid(pid_t devhost)1246 void VirtualRuntime::SetDevhostPid(pid_t devhost)
1247 {
1248 HLOGD("Set devhost pid: %d", devhost);
1249 devhostPid_ = devhost;
1250 }
1251
IsKernelThread(pid_t pid)1252 bool VirtualRuntime::IsKernelThread(pid_t pid)
1253 {
1254 if (!isHM_) {
1255 return false;
1256 }
1257 return pid == SYSMGR_PID || pid == devhostPid_;
1258 }
1259 } // namespace HiPerf
1260 } // namespace Developtools
1261 } // namespace OHOS
1262