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