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