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 "register.h"
28 #include "symbols_file.h"
29 #include "utilities.h"
30
31 using namespace std::chrono;
32 namespace OHOS {
33 namespace Developtools {
34 namespace HiPerf {
35 // we unable to access 'swapper' from /proc/0/
VirtualRuntime(bool onDevice)36 VirtualRuntime::VirtualRuntime(bool onDevice)
37 {
38 UpdateThread(0, 0, "swapper");
39 }
40
ReadThreadName(pid_t tid)41 std::string VirtualRuntime::ReadThreadName(pid_t tid)
42 {
43 std::string comm = ReadFileToString(StringPrintf("/proc/%d/cmdline", tid)).c_str();
44 comm.erase(std::remove(comm.begin(), comm.end(), '\r'), comm.end());
45 comm.erase(std::remove(comm.begin(), comm.end(), '\n'), comm.end());
46 return comm;
47 }
48
UpdateThread(pid_t pid,pid_t tid,const std::string name)49 VirtualThread &VirtualRuntime::UpdateThread(pid_t pid, pid_t tid, const std::string name)
50 {
51 #ifdef HIPERF_DEBUG_TIME
52 const auto startTime = steady_clock::now();
53 #endif
54 VirtualThread &thread = GetThread(pid, tid);
55 if (!name.empty()) {
56 thread.name_ = name;
57 }
58 #ifdef HIPERF_DEBUG_TIME
59 updateThreadTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
60 #endif
61 return thread;
62 }
63
CreateThread(pid_t pid,pid_t tid)64 VirtualThread &VirtualRuntime::CreateThread(pid_t pid, pid_t tid)
65 {
66 // make a new one
67 if (pid == tid) {
68 userSpaceThreadMap_.emplace(std::piecewise_construct, std::forward_as_tuple(tid),
69 std::forward_as_tuple(pid, symbolsFiles_));
70 } else {
71 // for thread we need give it process info( for same mmap)
72 userSpaceThreadMap_.emplace(
73 std::piecewise_construct, std::forward_as_tuple(tid),
74 std::forward_as_tuple(pid, tid, GetThread(pid, pid), symbolsFiles_));
75 }
76 VirtualThread &thread = userSpaceThreadMap_.at(tid);
77 if (recordCallBack_) {
78 if (pid == tid) {
79 #ifdef HIPERF_DEBUG_TIME
80 const auto startTime = steady_clock::now();
81 #endif
82 thread.ParseMap();
83 #ifdef HIPERF_DEBUG_TIME
84 threadParseMapsTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
85 #endif
86 }
87 #ifdef HIPERF_DEBUG_TIME
88 const auto startCreateMmapTime = steady_clock::now();
89 #endif
90 thread.name_ = ReadThreadName(pid);
91 HLOGD("create a new thread record for %u:%u:%s with %zu dso", pid, tid,
92 thread.name_.c_str(), thread.GetMaps().size());
93 // we need make a PerfRecordComm
94 auto commRecord = std::make_unique<PerfRecordComm>(false, pid, tid, thread.name_);
95 recordCallBack_(std::move(commRecord));
96 // only work for pid
97 if (pid == tid) {
98 for (auto &memMapItem : thread.GetMaps()) {
99 auto mmapRecord =
100 std::make_unique<PerfRecordMmap2>(false, thread.pid_, thread.tid_, memMapItem);
101 if (mmapRecord->data_.pgoff == 0 || (mmapRecord->data_.prot & PROT_EXEC) == 0) {
102 continue;
103 }
104 HLOGD("make PerfRecordMmap2 %d:%d:%s:%s(0x%" PRIx64 "-0x%" PRIx64 ")@%" PRIx64 " ",
105 thread.pid_, thread.tid_, thread.name_.c_str(), memMapItem.name_.c_str(),
106 memMapItem.begin_, memMapItem.end_, memMapItem.pageoffset_);
107 recordCallBack_(std::move(mmapRecord));
108 UpdateSymbols(memMapItem.name_);
109 }
110 }
111 HLOGV("thread created");
112 #ifdef HIPERF_DEBUG_TIME
113 threadCreateMmapTimes_ +=
114 duration_cast<microseconds>(steady_clock::now() - startCreateMmapTime);
115 #endif
116 }
117 return thread;
118 }
119
GetThread(pid_t pid,pid_t tid)120 VirtualThread &VirtualRuntime::GetThread(pid_t pid, pid_t tid)
121 {
122 if (userSpaceThreadMap_.find(pid) == userSpaceThreadMap_.end()) {
123 // no pid found
124 // create process first
125 CreateThread(pid, pid);
126 }
127
128 auto it = userSpaceThreadMap_.find(tid);
129 if (it == userSpaceThreadMap_.end()) {
130 // we also need thread
131 return CreateThread(pid, tid);
132 } else {
133 return it->second;
134 }
135 }
136
UpdateThreadMaps(pid_t pid,pid_t tid,const std::string filename,uint64_t begin,uint64_t len,uint64_t offset)137 void VirtualRuntime::UpdateThreadMaps(pid_t pid, pid_t tid, const std::string filename,
138 uint64_t begin, uint64_t len, uint64_t offset)
139 {
140 VirtualThread &thread = GetThread(pid, tid);
141 thread.CreateMapItem(filename, begin, len, offset);
142 }
143
UpdateKernelModulesSpaceMaps()144 void VirtualRuntime::UpdateKernelModulesSpaceMaps()
145 {
146 // found the kernel modules
147 std::vector<MemMapItem> koMaps;
148 std::ifstream ifs("/proc/modules", std::ifstream::in);
149 if (!ifs.is_open()) {
150 perror("kernel modules read failed(/proc/modules)\n");
151 return;
152 }
153 std::string line;
154 while (getline(ifs, line)) {
155 uint64_t addr = 0;
156 uint64_t size = 0;
157 char module[line.size()];
158 /*
159 name size load map
160 hi_mipi_rx 53248 0 - Live 0xbf109000 (O)
161 hi3516cv500_hdmi 237568 0 - Live 0xbf0bb000 (O)
162 hifb 143360 0 - Live 0xbf089000 (O)
163 hi3516cv500_vo_dev 98304 0 - Live 0xbf070000 (O)
164 hi3516cv500_tde 110592 0 - Live 0xbf04a000 (O)
165 hi3516cv500_sys 36864 0 - Live 0xbf03a000 (O)
166 hi3516cv500_base 20480 5
167 hi_mipi_rx,hi3516cv500_hdmi,hifb,hi3516cv500_vo_dev,hi3516cv500_tde,hi3516cv500_sys,
168 hi3516cv500_base,sys_config,hi_proc,hi_irq,Live 0xbf000000 (O)
169 */
170 int ret = sscanf_s(line.c_str(), "%s%" PRIu64 "%*u%*s%*s 0x%" PRIx64 "", module,
171 sizeof(module), &size, &addr, sizeof(addr));
172 constexpr int numSlices {3};
173 if (ret == numSlices) {
174 MemMapItem &map = koMaps.emplace_back(addr, addr + size, 0, std::string(module));
175 HLOGV("add ko map %s", map.ToString().c_str());
176 } else {
177 HLOGE("unknown line %d: '%s'", ret, line.c_str());
178 }
179 }
180
181 if (std::all_of(koMaps.begin(), koMaps.end(),
182 [](const MemMapItem &item) { return item.begin_ == 0; })) {
183 koMaps.clear();
184 HLOGW("no addr found in /proc/modules. remove all the ko");
185 }
186 if (recordCallBack_) {
187 for (MemMapItem &map : koMaps) {
188 auto record = std::make_unique<PerfRecordMmap>(true, 0, 0, map.begin_,
189 map.end_ - map.begin_, 0, map.name_);
190 recordCallBack_(std::move(record));
191 }
192 }
193 std::move(koMaps.begin(), koMaps.end(), std::back_inserter(kernelSpaceMemMaps_));
194 }
195
UpdateKernelSpaceMaps()196 void VirtualRuntime::UpdateKernelSpaceMaps()
197 {
198 // add kernel first
199 auto &map = kernelSpaceMemMaps_.emplace_back(0, std::numeric_limits<uint64_t>::max(), 0,
200 KERNEL_MMAP_NAME);
201 if (recordCallBack_) {
202 auto record = std::make_unique<PerfRecordMmap>(true, 0, 0, map.begin_,
203 map.end_ - map.begin_, 0, map.name_);
204 recordCallBack_(std::move(record));
205 }
206 }
207
UpdateKernelModulesSymbols()208 void VirtualRuntime::UpdateKernelModulesSymbols()
209 {
210 HLOGD("load ko symbol and build id");
211 for (MemMapItem &map : kernelSpaceMemMaps_) {
212 if (map.name_ == KERNEL_MMAP_NAME) {
213 continue;
214 }
215 auto kernelModuleFile =
216 SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_MODULE_FILE, map.name_);
217 if (symbolsPaths_.size() > 0) {
218 kernelModuleFile->setSymbolsFilePath(symbolsPaths_); // also load from search path
219 }
220 kernelModuleFile->LoadSymbols();
221 symbolsFiles_.emplace_back(std::move(kernelModuleFile));
222 }
223 }
224
UpdateKernelSymbols()225 void VirtualRuntime::UpdateKernelSymbols()
226 {
227 HLOGD("create a kernel mmap record");
228 // found kernel source
229 auto kernelFile = SymbolsFile::CreateSymbolsFile(KERNEL_MMAP_NAME);
230 // set sybol path If it exists
231 if (symbolsPaths_.size() > 0) {
232 kernelFile->setSymbolsFilePath(symbolsPaths_); // also load from search path
233 }
234 if (kernelFile->LoadSymbols()) {
235 auto record = std::make_unique<PerfRecordMmap>(
236 true, 0, 0, kernelFile->textExecVaddr_, kernelFile->textExecVaddrRange_,
237 kernelFile->textExecVaddrFileOffset_, KERNEL_MMAP_NAME);
238
239 if (recordCallBack_) {
240 recordCallBack_(std::move(record));
241 }
242 symbolsFiles_.emplace_back(std::move(kernelFile));
243 } else {
244 HLOGW("kernel symbol not found.\n");
245 }
246 }
247
UpdatekernelMap(uint64_t begin,uint64_t end,uint64_t offset,std::string filename)248 void VirtualRuntime::UpdatekernelMap(uint64_t begin, uint64_t end, uint64_t offset,
249 std::string filename)
250 {
251 HLOGV("update kernel map name:'%s' 0x%" PRIx64 " - 0x%" PRIx64 "@0x%08" PRIx64 "",
252 filename.c_str(), begin, end, offset);
253
254 HLOG_ASSERT(!filename.empty());
255 auto it = find(kernelSpaceMemMaps_.begin(), kernelSpaceMemMaps_.end(), filename);
256 if (it == kernelSpaceMemMaps_.end()) {
257 kernelSpaceMemMaps_.emplace_back(begin, end, offset, filename);
258 } else {
259 it->begin_ = begin;
260 it->end_ = end;
261 it->pageoffset_ = offset;
262 it->name_ = filename;
263 }
264 }
265
UpdateFromRecord(PerfEventRecord & record)266 void VirtualRuntime::UpdateFromRecord(PerfEventRecord &record)
267 {
268 #ifdef HIPERF_DEBUG_TIME
269 const auto startTime = steady_clock::now();
270 #endif
271 if (record.GetType() == PERF_RECORD_SAMPLE) {
272 auto recordSample = static_cast<PerfRecordSample *>(&record);
273 UpdateFromRecord(*recordSample);
274 #ifdef HIPERF_DEBUG_TIME
275 prcessSampleRecordTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
276 #endif
277 } else if (record.GetType() == PERF_RECORD_MMAP) {
278 auto recordMmap = static_cast<PerfRecordMmap *>(&record);
279 UpdateFromRecord(*recordMmap);
280 #ifdef HIPERF_DEBUG_TIME
281 prcessMmapRecordTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
282 #endif
283 } else if (record.GetType() == PERF_RECORD_MMAP2) {
284 auto recordMmap2 = static_cast<PerfRecordMmap2 *>(&record);
285 UpdateFromRecord(*recordMmap2);
286 #ifdef HIPERF_DEBUG_TIME
287 prcessMmap2RecordTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
288 #endif
289 } else if (record.GetType() == PERF_RECORD_COMM) {
290 auto recordCommp = static_cast<PerfRecordComm *>(&record);
291 UpdateFromRecord(*recordCommp);
292 #ifdef HIPERF_DEBUG_TIME
293 prcessCommRecordTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
294 #endif
295 } else {
296 HLOGW("skip record type %d", record.GetType());
297 }
298 }
299
MakeCallFrame(Symbol & symbol,CallFrame & callFrame)300 void VirtualRuntime::MakeCallFrame(Symbol &symbol, CallFrame &callFrame)
301 {
302 callFrame.vaddrInFile_ = symbol.funcVaddr_;
303 callFrame.offsetToVaddr_ = symbol.offsetToVaddr_;
304 callFrame.symbolFileIndex_ = symbol.symbolFileIndex_;
305 callFrame.symbolName_ = symbol.Name();
306 callFrame.symbolIndex_ = symbol.index_;
307 callFrame.filePath_ = symbol.module_.empty() ? symbol.comm_ : symbol.module_;
308 HLOG_ASSERT_MESSAGE(!callFrame.symbolName_.empty(), "%s", symbol.ToDebugString().c_str());
309 }
310
SymbolicCallFrame(PerfRecordSample & recordSample,uint64_t ip,perf_callchain_context context)311 void VirtualRuntime::SymbolicCallFrame(PerfRecordSample &recordSample, uint64_t ip,
312 perf_callchain_context context)
313 {
314 auto symbol = GetSymbol(ip, recordSample.data_.pid, recordSample.data_.tid, context);
315 MakeCallFrame(symbol, recordSample.callFrames_.emplace_back(ip, 0));
316 HLOGV(" (%zu)unwind symbol: %*s%s", recordSample.callFrames_.size(),
317 static_cast<int>(recordSample.callFrames_.size()), "",
318 recordSample.callFrames_.back().ToSymbolString().c_str());
319 }
320
SymbolicRecord(PerfRecordSample & recordSample)321 void VirtualRuntime::SymbolicRecord(PerfRecordSample &recordSample)
322 {
323 #ifdef HIPERF_DEBUG_TIME
324 const auto startTime = steady_clock::now();
325 #endif
326 // Symbolic the Call Stack
327 recordSample.callFrames_.clear();
328 perf_callchain_context context = PERF_CONTEXT_MAX;
329 if (recordSample.data_.nr == 0) {
330 SymbolicCallFrame(recordSample, recordSample.data_.ip, PERF_CONTEXT_MAX);
331 }
332 for (u64 i = 0; i < recordSample.data_.nr; i++) {
333 uint64_t ip = recordSample.data_.ips[i];
334 if (ip >= PERF_CONTEXT_MAX) {
335 std::string contextName = UpdatePerfContext(ip, context);
336 HLOGV("context switch to %s", contextName.c_str());
337 continue;
338 } else if (ip < BAD_IP_ADDRESS) {
339 // ip 0 or 1 or less than 0
340 continue;
341 }
342 SymbolicCallFrame(recordSample, ip, context);
343 }
344 #ifdef HIPERF_DEBUG_TIME
345 auto usedTime = duration_cast<microseconds>(steady_clock::now() - startTime);
346 if (usedTime.count() != 0) {
347 HLOGV("cost %0.3f ms to symbolic ", usedTime.count() / MS_DUARTION);
348 }
349 symbolicRecordTimes_ += usedTime;
350 #endif
351 }
352
UnwindFromRecord(PerfRecordSample & recordSample)353 void VirtualRuntime::UnwindFromRecord(PerfRecordSample &recordSample)
354 {
355 #ifdef HIPERF_DEBUG_TIME
356 const auto startTime = steady_clock::now();
357 #endif
358 HLOGV("unwind record (time:%llu)", recordSample.data_.time);
359 // if we have userstack ?
360 if (recordSample.data_.stack_size > 0) {
361 auto &thread = UpdateThread(recordSample.data_.pid, recordSample.data_.tid);
362 callstack_.UnwindCallStack(thread, recordSample.data_.user_abi == PERF_SAMPLE_REGS_ABI_32,
363 recordSample.data_.user_regs, recordSample.data_.reg_nr,
364 recordSample.data_.stack_data, recordSample.data_.dyn_size,
365 recordSample.callFrames_);
366 #ifdef HIPERF_DEBUG_TIME
367 unwindCallStackTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
368 #endif
369 size_t oldSize = recordSample.callFrames_.size();
370 HLOGV("unwind %zu", recordSample.callFrames_.size());
371 callstack_.ExpandCallStack(thread.tid_, recordSample.callFrames_, callstackMergeLevel_);
372 HLOGV("expand %zu (+%zu)", recordSample.callFrames_.size(),
373 recordSample.callFrames_.size() - oldSize);
374
375 recordSample.ReplaceWithCallStack(oldSize);
376 }
377
378 #ifdef HIPERF_DEBUG_TIME
379 unwindFromRecordTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
380 #endif
381
382 // we will not do this in record mode
383 if (recordCallBack_ == nullptr) {
384 // find the symbols , reabuild frame info
385 SymbolicRecord(recordSample);
386 }
387 }
388
UpdateFromRecord(PerfRecordSample & recordSample)389 void VirtualRuntime::UpdateFromRecord(PerfRecordSample &recordSample)
390 {
391 UpdateThread(recordSample.data_.pid, recordSample.data_.tid);
392 // unwind
393 if (disableUnwind_) {
394 return;
395 } else {
396 UnwindFromRecord(recordSample);
397 }
398 }
399
UpdateFromRecord(PerfRecordMmap & recordMmap)400 void VirtualRuntime::UpdateFromRecord(PerfRecordMmap &recordMmap)
401 {
402 HLOGV(" MMAP: size %d pid %u tid %u", recordMmap.header.size, recordMmap.data_.pid,
403 recordMmap.data_.tid);
404 HLOGV(" MMAP: %s dso '%s' (0x%llx-0x%llx)@0x%llx", recordMmap.inKernel() ? "kernel" : "user",
405 recordMmap.data_.filename, recordMmap.data_.addr,
406 recordMmap.data_.addr + recordMmap.data_.len, recordMmap.data_.pgoff);
407 // kernel mmap
408 // don't overwrite the vailed mmap , so we also check the recordMmap.data_.len
409 if (recordMmap.inKernel()) {
410 UpdatekernelMap(recordMmap.data_.addr, recordMmap.data_.addr + recordMmap.data_.len,
411 recordMmap.data_.pgoff, recordMmap.data_.filename);
412 } else {
413 UpdateThreadMaps(recordMmap.data_.pid, recordMmap.data_.tid, recordMmap.data_.filename,
414 recordMmap.data_.addr, recordMmap.data_.len, recordMmap.data_.pgoff);
415 UpdateSymbols(recordMmap.data_.filename);
416 }
417 }
418
UpdateFromRecord(PerfRecordMmap2 & recordMmap2)419 void VirtualRuntime::UpdateFromRecord(PerfRecordMmap2 &recordMmap2)
420 {
421 if (!VirtualThread::IsLegalFileName(recordMmap2.data_.filename)) {
422 return;
423 }
424 HLOGV(" MMAP2: size %d pid %u tid %u", recordMmap2.header.size, recordMmap2.data_.pid,
425 recordMmap2.data_.tid);
426 HLOGV(" MMAP2: %s dso '%s' (0x%llx-0x%llx)@0x%llx", recordMmap2.inKernel() ? "kernel" : "user",
427 recordMmap2.data_.filename, recordMmap2.data_.addr,
428 recordMmap2.data_.addr + recordMmap2.data_.len, recordMmap2.data_.pgoff);
429 UpdateThreadMaps(recordMmap2.data_.pid, recordMmap2.data_.tid, recordMmap2.data_.filename,
430 recordMmap2.data_.addr, recordMmap2.data_.len, recordMmap2.data_.pgoff);
431 UpdateSymbols(recordMmap2.data_.filename);
432 }
433
UpdateFromRecord(PerfRecordComm & recordComm)434 void VirtualRuntime::UpdateFromRecord(PerfRecordComm &recordComm)
435 {
436 recordComm.DumpLog(__FUNCTION__);
437 UpdateThread(recordComm.data_.pid, recordComm.data_.tid, recordComm.data_.comm);
438 }
439
SetRecordMode(RecordCallBack recordCallBack)440 void VirtualRuntime::SetRecordMode(RecordCallBack recordCallBack)
441 {
442 recordCallBack_ = recordCallBack;
443 }
444
UpdateSymbols(std::string fileName)445 void VirtualRuntime::UpdateSymbols(std::string fileName)
446 {
447 HLOGD("try to find symbols for file: %s", fileName.c_str());
448 #ifdef HIPERF_DEBUG_TIME
449 const auto startTime = steady_clock::now();
450 #endif
451 for (auto &symbolsFile : symbolsFiles_) {
452 if (symbolsFile->filePath_ == fileName) {
453 HLOGV("already have '%s'", fileName.c_str());
454 return;
455 }
456 }
457 // found it by name
458 auto symbolsFile = SymbolsFile::CreateSymbolsFile(fileName);
459
460 // set sybol path If it exists
461 if (symbolsPaths_.size() > 0) {
462 symbolsFile->setSymbolsFilePath(symbolsPaths_); // also load from search path
463 }
464 if (loadSymboleWhenNeeded_) {
465 // load it when we need it
466 symbolsFiles_.emplace_back(std::move(symbolsFile));
467 } else if (symbolsFile->LoadSymbols()) {
468 symbolsFiles_.emplace_back(std::move(symbolsFile));
469 } else {
470 HLOGW("symbols file for '%s' not found.", fileName.c_str());
471 }
472 #ifdef HIPERF_DEBUG_TIME
473 auto usedTime = duration_cast<microseconds>(steady_clock::now() - startTime);
474 if (usedTime.count() != 0) {
475 HLOGV("cost %0.3f ms to load '%s'", usedTime.count() / MS_DUARTION, fileName.c_str());
476 }
477 updateSymbolsTimes_ += usedTime;
478 #endif
479 }
480
GetKernelSymbol(uint64_t ip,const std::vector<MemMapItem> & memMaps,const VirtualThread & thread)481 const Symbol VirtualRuntime::GetKernelSymbol(uint64_t ip, const std::vector<MemMapItem> &memMaps,
482 const VirtualThread &thread)
483 {
484 Symbol vaddrSymbol(ip, thread.name_);
485 for (auto &map : memMaps) {
486 if (ip > map.begin_ && ip < map.end_) {
487 HLOGM("found addr 0x%" PRIx64 " in kernel map 0x%" PRIx64 " - 0x%" PRIx64 " from %s",
488 ip, map.begin_, map.end_, map.name_.c_str());
489 vaddrSymbol.module_ = map.name_;
490 // found symbols by file name
491 for (auto &symbolsFile : symbolsFiles_) {
492 if (symbolsFile->filePath_ == map.name_) {
493 vaddrSymbol.symbolFileIndex_ = symbolsFile->id_;
494 vaddrSymbol.fileVaddr_ =
495 symbolsFile->GetVaddrInSymbols(ip, map.begin_, map.pageoffset_);
496 HLOGV("found symbol vaddr 0x%" PRIx64 " for runtime vaddr 0x%" PRIx64
497 " at '%s'",
498 vaddrSymbol.fileVaddr_, ip, map.name_.c_str());
499 if (!symbolsFile->SymbolsLoaded()) {
500 symbolsFile->LoadSymbols();
501 }
502 Symbol foundSymbols = symbolsFile->GetSymbolWithVaddr(vaddrSymbol.fileVaddr_);
503 foundSymbols.taskVaddr_ = ip;
504 if (!foundSymbols.isValid()) {
505 HLOGW("addr 0x%" PRIx64 " vaddr 0x%" PRIx64 " NOT found in symbol file %s",
506 ip, vaddrSymbol.fileVaddr_, map.name_.c_str());
507 return vaddrSymbol;
508 } else {
509 return foundSymbols;
510 }
511 }
512 }
513 HLOGW("addr 0x%" PRIx64 " in map but NOT found the symbol file %s", ip,
514 map.name_.c_str());
515 } else {
516 HLOGM("addr 0x%" PRIx64 " not in map 0x%" PRIx64 " - 0x%" PRIx64 " from %s", ip,
517 map.begin_, map.end_, map.name_.c_str());
518 }
519 }
520 return vaddrSymbol;
521 }
522
GetUserSymbol(uint64_t ip,const VirtualThread & thread)523 const Symbol VirtualRuntime::GetUserSymbol(uint64_t ip, const VirtualThread &thread)
524 {
525 Symbol vaddrSymbol(ip, thread.name_);
526 const MemMapItem *mmap = thread.FindMapByAddr(ip);
527 if (mmap != nullptr) {
528 SymbolsFile *symbolsFile = thread.FindSymbolsFileByMap(*mmap);
529 if (symbolsFile != nullptr) {
530 vaddrSymbol.symbolFileIndex_ = symbolsFile->id_;
531 vaddrSymbol.fileVaddr_ =
532 symbolsFile->GetVaddrInSymbols(ip, mmap->begin_, mmap->pageoffset_);
533 vaddrSymbol.module_ = mmap->nameHold_;
534 HLOGV("found symbol vaddr 0x%" PRIx64 " for runtime vaddr 0x%" PRIx64 " at '%s'",
535 vaddrSymbol.fileVaddr_, ip, mmap->name_.c_str());
536 if (!symbolsFile->SymbolsLoaded()) {
537 symbolsFile->LoadSymbols();
538 }
539 Symbol foundSymbols = symbolsFile->GetSymbolWithVaddr(vaddrSymbol.fileVaddr_);
540 foundSymbols.taskVaddr_ = ip;
541 if (!foundSymbols.isValid()) {
542 HLOGW("addr 0x%" PRIx64 " vaddr 0x%" PRIx64 " NOT found in symbol file %s", ip,
543 vaddrSymbol.fileVaddr_, mmap->name_.c_str());
544 return vaddrSymbol;
545 } else {
546 return foundSymbols;
547 }
548 } else {
549 HLOGW("addr 0x%" PRIx64 " in map but NOT found the symbol file %s", ip,
550 mmap->name_.c_str());
551 }
552 } else {
553 #ifdef HIPERF_DEBUG
554 thread.ReportVaddrMapMiss(ip);
555 #endif
556 }
557 return vaddrSymbol;
558 }
559
GetSymbolCache(uint64_t ip,pid_t pid,pid_t tid,Symbol & symbol,const perf_callchain_context & context)560 bool VirtualRuntime::GetSymbolCache(uint64_t ip, pid_t pid, pid_t tid, Symbol &symbol,
561 const perf_callchain_context &context)
562 {
563 if (context != PERF_CONTEXT_USER and kernelSymbolCache_.count(ip)) {
564 if (kernelSymbolCache_.find(ip) == kernelSymbolCache_.end()) {
565 return false;
566 }
567 Symbol &foundSymbol = kernelSymbolCache_[ip];
568 foundSymbol.hit_++;
569 HLOGV("hit kernel cache 0x%" PRIx64 " %d", ip, foundSymbol.hit_);
570 symbol = foundSymbol;
571 return true;
572 } else if (threadSymbolCache_[tid].count(ip) != 0) {
573 Symbol &foundSymbol = threadSymbolCache_[tid][ip];
574 foundSymbol.hit_++;
575 HLOGV("hit user cache 0x%" PRIx64 " %d %s", ip, foundSymbol.hit_,
576 foundSymbol.ToDebugString().c_str());
577 symbol = foundSymbol;
578 return true;
579 } else {
580 HLOGM("cache miss k %zu u %zu", kernelSymbolCache_.size(), threadSymbolCache_[tid].size());
581 }
582 return false;
583 }
584
GetSymbol(uint64_t ip,pid_t pid,pid_t tid,const perf_callchain_context & context)585 const Symbol VirtualRuntime::GetSymbol(uint64_t ip, pid_t pid, pid_t tid,
586 const perf_callchain_context &context)
587 {
588 HLOGV("try find tid %u ip 0x%" PRIx64 " in %zu symbolsFiles", tid, ip, symbolsFiles_.size());
589 Symbol symbol;
590 if (threadSymbolCache_.find(tid) == threadSymbolCache_.end()) {
591 threadSymbolCache_[tid].reserve(THREAD_SYMBOL_CACHE_LIMIT);
592 }
593 if (GetSymbolCache(ip, pid, tid, symbol, context)) {
594 return symbol;
595 }
596 if (context == PERF_CONTEXT_USER or (context == PERF_CONTEXT_MAX and !symbol.isValid())) {
597 // check userspace memmap
598 symbol = GetUserSymbol(ip, GetThread(pid, tid));
599 threadSymbolCache_[tid][ip] = symbol;
600 HLOGV("cache ip 0x%" PRIx64 " to %s", ip,
601 threadSymbolCache_[tid][ip].ToDebugString().c_str());
602 }
603
604 if (context == PERF_CONTEXT_KERNEL or (context == PERF_CONTEXT_MAX and !symbol.isValid())) {
605 // check kernelspace
606 HLOGM("try found addr in kernelspace %zu maps", kernelSpaceMemMaps_.size());
607 symbol = GetKernelSymbol(ip, kernelSpaceMemMaps_, GetThread(pid, tid));
608 HLOGM("add addr to kernel cache 0x%" PRIx64 " cache size %zu", ip,
609 kernelSymbolCache_.size());
610 kernelSymbolCache_[ip] = symbol;
611 }
612 return symbol;
613 }
614
SetSymbolsPaths(const std::vector<std::string> & symbolsPaths)615 bool VirtualRuntime::SetSymbolsPaths(const std::vector<std::string> &symbolsPaths)
616 {
617 std::unique_ptr<SymbolsFile> symbolsFile = SymbolsFile::CreateSymbolsFile(SYMBOL_UNKNOW_FILE);
618 // we need check if the path is accessible
619 bool accessible = symbolsFile->setSymbolsFilePath(symbolsPaths);
620 if (accessible) {
621 symbolsPaths_ = symbolsPaths;
622 } else {
623 if (!symbolsPaths.empty()) {
624 printf("some symbols path unable access\n");
625 }
626 }
627 return accessible;
628 }
629
UpdateFromPerfData(const std::vector<SymbolFileStruct> & symbolFileStructs)630 void VirtualRuntime::UpdateFromPerfData(const std::vector<SymbolFileStruct> &symbolFileStructs)
631 {
632 // review: if we need move to some other place ?
633 HLOG_ASSERT_MESSAGE(symbolsFiles_.size() == 0, " symbolsFiles_ size is %zu",
634 symbolsFiles_.size());
635 for (const auto &symbolFileStruct : symbolFileStructs) {
636 HLOGV("symbolFileStruct.filePath_:'%s'", symbolFileStruct.filePath_.c_str());
637 HLOGV("symbolFileStruct.buildId_:'%s'", symbolFileStruct.buildId_.c_str());
638 HLOGV("process symbols file:'%s':'%s'", symbolFileStruct.filePath_.c_str(),
639 symbolFileStruct.buildId_.c_str());
640
641 // load from symbolFileStruct (perf.data)
642 std::unique_ptr<SymbolsFile> symbolsFile =
643 SymbolsFile::LoadSymbolsFromSaved(symbolFileStruct);
644
645 // reaload from sybol path If it exists
646 if (symbolsPaths_.size() > 0) {
647 HLOGV("try again with symbolsPaths setup");
648 symbolsFile->setSymbolsFilePath(symbolsPaths_); // also load from search path
649 // use give us path ,we must reload it.
650 symbolsFile->LoadSymbols();
651 }
652 symbolsFile->id_ = symbolsFiles_.size();
653 symbolsFiles_.emplace_back(std::move(symbolsFile));
654 }
655 }
656
657 /*
658 ARM functions
659 The table below lists the symbols exported by the vDSO.
660
661 symbol version
662 ────────────────────────────────────────────────────────────
663 __vdso_gettimeofday LINUX_2.6 (exported since Linux 4.1)
664 __vdso_clock_gettime LINUX_2.6 (exported since Linux 4.1)
665
666 Additionally, the ARM port has a code page full of utility
667 functions. Since it's just a raw page of code, there is no ELF
668 information for doing symbol lookups or versioning. It does
669 provide support for different versions though.
670
671 For information on this code page, it's best to refer to the
672 kernel documentation as it's extremely detailed and covers
673 everything you need to know:
674 Documentation/arm/kernel_user_helpers.txt.
675
676 aarch64 functions
677 The table below lists the symbols exported by the vDSO.
678
679 symbol version
680 ──────────────────────────────────────
681 __kernel_rt_sigreturn LINUX_2.6.39
682 __kernel_gettimeofday LINUX_2.6.39
683 __kernel_clock_gettime LINUX_2.6.39
684 __kernel_clock_getres LINUX_2.6.39
685 */
LoadVdso()686 void VirtualRuntime::LoadVdso()
687 {
688 #if !is_mingw
689 VirtualThread myThread(getpid(), symbolsFiles_);
690 myThread.ParseMap();
691 for (const auto &map : myThread.GetMaps()) {
692 if (map.name_ == MMAP_VDSO_NAME) {
693 std::string memory(map.end_ - map.begin_, '\0');
694 std::copy(reinterpret_cast<char *>((map.begin_)), reinterpret_cast<char *>((map.end_)),
695 &memory[0]);
696 std::string tempPath("/data/local/tmp/");
697 std::string tempFileName = tempPath + MMAP_VDSO_NAME;
698 if (!WriteStringToFile(tempFileName, memory)) {
699 printf("vdso temp file create fail at %s\n", tempFileName.c_str());
700 } else {
701 HLOGD("vdso temp file create at %s:%zu", tempFileName.c_str(), memory.size());
702 auto symbolsFile = SymbolsFile::CreateSymbolsFile(MMAP_VDSO_NAME);
703 symbolsFile->setSymbolsFilePath(tempPath); // also load from search path
704 symbolsFiles_.emplace_back(std::move(symbolsFile));
705 return;
706 }
707 }
708 }
709 HLOGD("no vdso found");
710 #endif
711 }
712 } // namespace HiPerf
713 } // namespace Developtools
714 } // namespace OHOS
715