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 "CallStack"
16
17 #include "callstack.h"
18
19 #include <string>
20 #if HAVE_LIBUNWIND
21 #include <libunwind.h>
22 extern "C" {
23 #include <libunwind_i.h>
24 }
25 #endif
26
27 #include "register.h"
28 #ifdef target_cpu_arm
29 // reg size is int (unw_word_t)
30 #define UNW_WORD_PFLAG "x"
31 #else
32 // reg size is long (unw_word_t)
33 #define UNW_WORD_PFLAG "zx"
34 #endif
35 namespace OHOS {
36 namespace Developtools {
37 namespace HiPerf {
38 #if HAVE_LIBUNWIND
39 const std::map<unw_error_t, const std::string> UNW_ERROR_MAP = {
40 {UNW_ESUCCESS, std::to_string(UNW_ESUCCESS)},
41 {UNW_EUNSPEC, std::to_string(UNW_EUNSPEC)},
42 {UNW_ENOMEM, std::to_string(UNW_ENOMEM)},
43 {UNW_EBADREG, std::to_string(UNW_EBADREG)},
44 {UNW_EREADONLYREG, std::to_string(UNW_EREADONLYREG)},
45 {UNW_ESTOPUNWIND, std::to_string(UNW_ESTOPUNWIND)},
46 {UNW_EINVALIDIP, std::to_string(UNW_EINVALIDIP)},
47 {UNW_EBADFRAME, std::to_string(UNW_EBADFRAME)},
48 {UNW_EINVAL, std::to_string(UNW_EINVAL)},
49 {UNW_EBADVERSION, std::to_string(UNW_EBADVERSION)},
50 {UNW_ENOINFO, std::to_string(UNW_ENOINFO)},
51 };
GetUnwErrorName(int error)52 const std::string CallStack::GetUnwErrorName(int error)
53 {
54 if (UNW_ERROR_MAP.count(static_cast<unw_error_t>(-error)) > 0) {
55 return UNW_ERROR_MAP.at(static_cast<unw_error_t>(-error));
56 } else {
57 return "UNKNOW_UNW_ERROR";
58 }
59 }
60
dumpUDI(unw_dyn_info_t & di)61 void CallStack::dumpUDI(unw_dyn_info_t &di)
62 {
63 HLOGV("unwind_table info: ");
64 HLOGV(" di.start_ip: 0x%016" UNW_WORD_PFLAG "", di.start_ip);
65 HLOGV(" di.end_ip: 0x%016" UNW_WORD_PFLAG "", di.end_ip);
66 HLOGV(" di.u.rti.segbase: 0x%016" UNW_WORD_PFLAG "", di.u.rti.segbase);
67 HLOGV(" di.u.rti.table_data: 0x%016" UNW_WORD_PFLAG "", di.u.rti.table_data);
68 HLOGV(" di.u.rti.table_len: 0x%016" UNW_WORD_PFLAG "", di.u.rti.table_len);
69 }
70
fillUDI(unw_dyn_info_t & di,SymbolsFile & symbolsFile,const MemMapItem & mmap,const VirtualThread & thread)71 bool CallStack::fillUDI(unw_dyn_info_t &di, SymbolsFile &symbolsFile, const MemMapItem &mmap,
72 const VirtualThread &thread)
73 {
74 di.start_ip = mmap.begin_;
75 di.end_ip = mmap.end_;
76 #ifndef target_cpu_arm
77 uint64_t fdeTableElfOffset;
78 uint64_t fdeTableSize;
79 uint64_t ehFrameHdrElfOffset;
80 if ((UNW_INFO_FORMAT_REMOTE_TABLE == di.format) &&
81 symbolsFile.GetHDRSectionInfo(ehFrameHdrElfOffset, fdeTableElfOffset, fdeTableSize)) {
82 /*
83 unw_word_t name_ptr; // addr. of table name (e.g., library name)
84 unw_word_t segbase; // segment base
85 unw_word_t table_len; // must be a multiple of sizeof(unw_word_t)!
86 unw_word_t table_data;
87 */
88 /*
89 all the rti addr is offset of the elf file
90 begin - page offset = elf file base addr in vaddr user space
91 begin - page offset + elf offset = vaddr in real word.(for this thread)
92 */
93
94 // segbase is file offset .
95 /*
96 00200000-00344000 r--p 00000000 08:02 46404365
97 00344000-005c4000 r-xp 00143000 08:02 46404365
98
99 LOAD 0x00000000001439c0 0x00000000003449c0 0x00000000003449c0
100 0x000000000027f3c0 0x000000000027f3c0 R E 0x1000
101
102 GNU_EH_FRAME 0x00000000000f3248 0x00000000002f3248 0x00000000002f3248
103 0x000000000000bb04 0x000000000000bb04 R 0x4
104
105 */
106 const MemMapItem *ehFrameMmap = thread.FindMapByFileInfo(mmap.name_, ehFrameHdrElfOffset);
107
108 if (ehFrameMmap == nullptr) {
109 HLOGE("no ehframe mmap found.");
110 return false;
111 }
112
113 di.u.rti.segbase = ehFrameMmap->begin_ + ehFrameHdrElfOffset - ehFrameMmap->pageoffset_;
114 di.u.rti.table_data = ehFrameMmap->begin_ + fdeTableElfOffset - ehFrameMmap->pageoffset_;
115 di.u.rti.table_len = fdeTableSize / sizeof(unw_word_t);
116
117 HLOGV(" map pageoffset: 0x%016" PRIx64 "", mmap.pageoffset_);
118 HLOGV(" ehFrameHdrElfOffset: 0x%016" PRIx64 "", ehFrameHdrElfOffset);
119 HLOGV(" fdeTableElfOffset: 0x%016" PRIx64 "", fdeTableElfOffset);
120 HLOGV(" fdeTableSize: 0x%016" PRIx64 "", fdeTableSize);
121 return true;
122 } else {
123 HLOGD("SymbolsFile::GetHDRSectionInfo() failed");
124 }
125 #else
126 uint64_t SectionVaddr;
127 uint64_t SectionSize;
128 uint64_t SectionFileOffset;
129 if ((UNW_INFO_FORMAT_ARM_EXIDX == di.format) &&
130 symbolsFile.GetSectionInfo(ARM_EXIDX, SectionVaddr, SectionSize, SectionFileOffset)) {
131 const MemMapItem *targetMmap = thread.FindMapByFileInfo(mmap.name_, SectionFileOffset);
132 if (targetMmap == nullptr) {
133 HLOGE("no debug mmap found.");
134 return false;
135 }
136 HLOGV(" begin: %" PRIx64 " offset:%" PRIx64 "", targetMmap->begin_,
137 targetMmap->pageoffset_);
138
139 di.u.rti.table_data = targetMmap->begin_ + SectionFileOffset - targetMmap->pageoffset_;
140 di.u.rti.table_len = SectionSize;
141 HLOGV(" SectionName: %s", std::string(ARM_EXIDX).c_str());
142 HLOGV(" SectionVaddrt: 0x%016" PRIx64 "", SectionVaddr);
143 HLOGV(" SectionFileOffset 0x%016" PRIx64 "", SectionFileOffset);
144 HLOGV(" SectionSize: 0x%016" PRIx64 "", SectionSize);
145
146 // GetSectionInfo return true, but SectionVaddr || SectionSize is 0 ???
147 HLOG_ASSERT(SectionVaddr != 0 && SectionSize != 0);
148 return true;
149 } else {
150 HLOGD("SymbolsFile::GetSectionInfo() failed");
151 }
152 #endif
153 return false;
154 }
155
156 /*
157 https://www.nongnu.org/libunwind/man/libunwind-dynamic(3).html
158 */
FindUnwindTable(SymbolsFile * symbolsFile,const MemMapItem & mmap,UnwindInfo * unwindInfoPtr,unw_addr_space_t as,unw_word_t ip,unw_proc_info_t * pi,int need_unwind_info,void * arg)159 int CallStack::FindUnwindTable(SymbolsFile *symbolsFile, const MemMapItem &mmap,
160 UnwindInfo *unwindInfoPtr, unw_addr_space_t as, unw_word_t ip,
161 unw_proc_info_t *pi, int need_unwind_info, void *arg)
162 {
163 HLOGM("try search debug info at %s", symbolsFile->filePath_.c_str());
164 auto &dynInfoProcessMap = unwindInfoPtr->callStack.unwindDynInfoMap_;
165 // all the thread in same process have same mmap and symbols
166 if (dynInfoProcessMap.find(unwindInfoPtr->thread.pid_) == dynInfoProcessMap.end()) {
167 dynInfoProcessMap.emplace(unwindInfoPtr->thread.pid_, dsoUnwDynInfoMap {});
168 }
169 dsoUnwDynInfoMap &dynFileMap = dynInfoProcessMap[unwindInfoPtr->thread.pid_];
170 // find use dso name as key
171 if (dynFileMap.find(symbolsFile->filePath_) == dynFileMap.end()) {
172 unw_dyn_info_t newdi;
173 if (memset_s(&newdi, sizeof(unw_dyn_info_t), 0, sizeof(unw_dyn_info_t)) != EOK) {
174 HLOGE("memset_s() failed");
175 return -UNW_EUNSPEC;
176 }
177 #ifdef target_cpu_arm
178 // arm use .ARM.exidx , not use ehframe
179 newdi.format = UNW_INFO_FORMAT_ARM_EXIDX;
180 #else
181 // otherwise we use EH FRAME
182 newdi.format = UNW_INFO_FORMAT_REMOTE_TABLE;
183 #endif
184 if (fillUDI(newdi, *symbolsFile, mmap, unwindInfoPtr->thread)) {
185 dumpUDI(newdi);
186 // we make a option empty value first
187 std::optional<unw_dyn_info_t> &odi = dynFileMap[symbolsFile->filePath_];
188 odi = newdi;
189 } else {
190 HLOGV("fillUDI failed()");
191 return -UNW_EUNSPEC;
192 }
193 }
194
195 HLOG_ASSERT(dynInfoProcessMap.find(unwindInfoPtr->thread.pid_) != dynInfoProcessMap.end());
196 HLOG_ASSERT_MESSAGE(dynFileMap.find(symbolsFile->filePath_) != dynFileMap.end(), "%s",
197 symbolsFile->filePath_.c_str());
198 std::optional<unw_dyn_info_t> &odi =
199 dynInfoProcessMap.at(unwindInfoPtr->thread.pid_).at(symbolsFile->filePath_);
200
201 if (odi.has_value()) {
202 unw_dyn_info_t &di = odi.value();
203 /*
204 we don't use dwarf_search_unwind_table
205 because in arm it will search two function:
206 1 arm_search_unwind_table first
207 2 dwarf_search_unwind_table
208
209 see libunwind_i.h for arm
210 define tdep_search_unwind_table UNW_OBJ(search_unwind_table)
211
212 */
213 int ret = static_cast<unw_error_t>(
214 tdep_search_unwind_table(as, ip, &di, pi, need_unwind_info, arg));
215
216 HLOGM("search_unwind_table ret %d:%s", ret, GetUnwErrorName(ret).c_str());
217
218 if (UNW_ESUCCESS != ret) {
219 if (UNW_ENOINFO != ret) {
220 HLOGW("search_unwind_table ret error %d:%s", ret, GetUnwErrorName(ret).c_str());
221 }
222 return -UNW_EUNSPEC;
223 } else {
224 return UNW_ESUCCESS;
225 }
226 } else {
227 HLOGW("no debug info found for thread %d:%s", unwindInfoPtr->thread.tid_,
228 unwindInfoPtr->thread.name_.c_str());
229 return -UNW_EUNSPEC;
230 }
231 }
232
FindProcInfo(unw_addr_space_t as,unw_word_t ip,unw_proc_info_t * pi,int need_unwind_info,void * arg)233 int CallStack::FindProcInfo(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
234 int need_unwind_info, void *arg)
235 {
236 UnwindInfo *unwindInfoPtr = static_cast<UnwindInfo *>(arg);
237
238 HLOGM("need_unwind_info ret %d ip %" UNW_WORD_PFLAG "", need_unwind_info, ip);
239 const MemMapItem *mmap = unwindInfoPtr->thread.FindMapByAddr(ip);
240 if (mmap != nullptr) {
241 SymbolsFile *symbolsFile = unwindInfoPtr->thread.FindSymbolsFileByMap(*mmap);
242 if (symbolsFile != nullptr) {
243 return FindUnwindTable(symbolsFile, *mmap, unwindInfoPtr, as, ip, pi, need_unwind_info,
244 arg);
245 } else {
246 HLOGW("no symbols file found for thread %d:%s", unwindInfoPtr->thread.tid_,
247 unwindInfoPtr->thread.name_.c_str());
248 }
249 } else {
250 HLOGE("ip 0x%016" UNW_WORD_PFLAG " not found in thread %d:%s", ip,
251 unwindInfoPtr->thread.tid_, unwindInfoPtr->thread.name_.c_str());
252 }
253
254 return -UNW_EUNSPEC;
255 }
256
ReadVirtualThreadMemory(UnwindInfo & unwindInfoPtr,unw_word_t addr,unw_word_t * data)257 bool CallStack::ReadVirtualThreadMemory(UnwindInfo &unwindInfoPtr, unw_word_t addr,
258 unw_word_t *data)
259 {
260 auto process = unwindInfoPtr.callStack.porcessMemoryMap_.find(unwindInfoPtr.thread.pid_);
261 if (process != unwindInfoPtr.callStack.porcessMemoryMap_.end()) {
262 auto memory = process->second.find(addr);
263 if (memory != process->second.end()) {
264 *data = memory->second;
265 return true;
266 }
267 }
268
269 if (unwindInfoPtr.thread.ReadRoMemory(addr, (uint8_t *)data, sizeof(unw_word_t))) {
270 unwindInfoPtr.callStack.porcessMemoryMap_[unwindInfoPtr.thread.pid_][addr] = *data;
271 return true;
272 } else {
273 return false;
274 }
275 }
276
AccessMem(unw_addr_space_t as,unw_word_t addr,unw_word_t * valuePoint,int writeOperation,void * arg)277 int CallStack::AccessMem([[maybe_unused]] unw_addr_space_t as, unw_word_t addr,
278 unw_word_t *valuePoint, int writeOperation, void *arg)
279 {
280 UnwindInfo *unwindInfoPtr = static_cast<UnwindInfo *>(arg);
281 *valuePoint = 0;
282 HLOGDUMMY("try access addr 0x%" UNW_WORD_PFLAG " ", addr);
283 HLOG_ASSERT(writeOperation == 0);
284
285 /* Check overflow. */
286 if (addr + sizeof(unw_word_t) < addr) {
287 HLOGE("address overfolw at 0x%" UNW_WORD_PFLAG " increase 0x%zu", addr, sizeof(unw_word_t));
288 return -UNW_EUNSPEC;
289 }
290
291 if (addr < unwindInfoPtr->callStack.stackPoint_ or
292 addr + sizeof(unw_word_t) >= unwindInfoPtr->callStack.stackEnd_) {
293 if (ReadVirtualThreadMemory(*unwindInfoPtr, addr, valuePoint)) {
294 HLOGM("access_mem addr %p get val 0x%" UNW_WORD_PFLAG ", from mmap",
295 reinterpret_cast<void *>(addr), *valuePoint);
296 } else {
297 HLOGW("access_mem addr %p failed, from mmap, ", reinterpret_cast<void *>(addr));
298 HLOGW("stack range 0x%" PRIx64 " - 0x%" PRIx64 "(0x%" PRIx64 ")",
299 unwindInfoPtr->callStack.stackPoint_, unwindInfoPtr->callStack.stackEnd_,
300 unwindInfoPtr->callStack.stackEnd_ - unwindInfoPtr->callStack.stackPoint_);
301 return -UNW_EUNSPEC;
302 }
303 } else {
304 size_t stackOffset = addr - unwindInfoPtr->callStack.stackPoint_;
305 *valuePoint = *(unw_word_t *)&unwindInfoPtr->callStack.stack_[stackOffset];
306 HLOGM("access_mem addr %p val %" UNW_WORD_PFLAG ", from stack offset %zu",
307 reinterpret_cast<void *>(addr), *valuePoint, stackOffset);
308 }
309
310 return UNW_ESUCCESS;
311 }
312
AccessReg(unw_addr_space_t as,unw_regnum_t regnum,unw_word_t * valuePoint,int writeOperation,void * arg)313 int CallStack::AccessReg([[maybe_unused]] unw_addr_space_t as, unw_regnum_t regnum,
314 unw_word_t *valuePoint, int writeOperation, void *arg)
315 {
316 UnwindInfo *unwindInfoPtr = static_cast<UnwindInfo *>(arg);
317 uint64_t val;
318 int perfRegIndex = LibunwindRegIdToPerfReg(regnum);
319 if (perfRegIndex < 0) {
320 HLOGE("can't read reg %d", perfRegIndex);
321 return perfRegIndex;
322 }
323 /* Don't support write, I suspect we don't need it. */
324 if (writeOperation) {
325 HLOGE("access_reg %d", regnum);
326 return -UNW_EINVAL;
327 }
328
329 if (unwindInfoPtr->callStack.regsNum_ == 0) {
330 return -UNW_EUNSPEC;
331 }
332
333 if (!RegisterGetValue(val, unwindInfoPtr->callStack.regs_, static_cast<size_t>(perfRegIndex),
334 unwindInfoPtr->callStack.regsNum_)) {
335 HLOGE("can't read reg %d", perfRegIndex);
336 return -UNW_EUNSPEC;
337 }
338
339 *valuePoint = (unw_word_t)val;
340 HLOGM("reg %d:%s, val 0x%" UNW_WORD_PFLAG "", regnum, RegisterGetName(static_cast<size_t>(perfRegIndex)).c_str(),
341 *valuePoint);
342 return UNW_ESUCCESS;
343 }
344
PutUnwindInfo(unw_addr_space_t as,unw_proc_info_t * pi,void * arg)345 void CallStack::PutUnwindInfo([[maybe_unused]] unw_addr_space_t as,
346 [[maybe_unused]] unw_proc_info_t *pi, [[maybe_unused]] void *arg)
347 {
348 }
349
AccessFpreg(unw_addr_space_t as,unw_regnum_t num,unw_fpreg_t * val,int writeOperation,void * arg)350 int CallStack::AccessFpreg([[maybe_unused]] unw_addr_space_t as, [[maybe_unused]] unw_regnum_t num,
351 [[maybe_unused]] unw_fpreg_t *val, [[maybe_unused]] int writeOperation,
352 [[maybe_unused]] void *arg)
353 {
354 return -UNW_EINVAL;
355 }
356
GetDynInfoListAaddr(unw_addr_space_t as,unw_word_t * dil_vaddr,void * arg)357 int CallStack::GetDynInfoListAaddr([[maybe_unused]] unw_addr_space_t as,
358 [[maybe_unused]] unw_word_t *dil_vaddr,
359 [[maybe_unused]] void *arg)
360 {
361 return -UNW_ENOINFO;
362 }
363
Resume(unw_addr_space_t as,unw_cursor_t * cu,void * arg)364 int CallStack::Resume([[maybe_unused]] unw_addr_space_t as, [[maybe_unused]] unw_cursor_t *cu,
365 [[maybe_unused]] void *arg)
366 {
367 return -UNW_EINVAL;
368 }
369
getProcName(unw_addr_space_t as,unw_word_t addr,char * bufp,size_t buf_len,unw_word_t * offp,void * arg)370 int CallStack::getProcName([[maybe_unused]] unw_addr_space_t as, [[maybe_unused]] unw_word_t addr,
371 [[maybe_unused]] char *bufp, [[maybe_unused]] size_t buf_len,
372 [[maybe_unused]] unw_word_t *offp, [[maybe_unused]] void *arg)
373 {
374 return -UNW_EINVAL;
375 }
376
UnwindStep(unw_cursor_t & c,std::vector<CallFrame> & callStack,size_t maxStackLevel)377 void CallStack::UnwindStep(unw_cursor_t &c, std::vector<CallFrame> &callStack, size_t maxStackLevel)
378 {
379 while (callStack.size() < maxStackLevel) {
380 int ret = unw_step(&c);
381 if (ret > 0) {
382 unw_word_t ip;
383 unw_word_t sp;
384 unw_get_reg(&c, UNW_REG_IP, &ip);
385 unw_get_reg(&c, UNW_REG_SP, &sp);
386
387 if (ip == 0) {
388 HLOGD("ip == 0 something is wrong. break");
389 break;
390 }
391
392 /*
393 * Decrement the IP for any non-activation frames.
394 * this is required to properly find the srcline
395 * for caller frames.
396 * See also the documentation for dwfl_frame_pc(),
397 * which this code tries to replicate.
398 */
399 if (unw_is_signal_frame(&c) <= 0) {
400 --ip;
401 }
402 HLOGV("unwind:%zu: ip 0x%" UNW_WORD_PFLAG " sp 0x%" UNW_WORD_PFLAG "", callStack.size(),
403 ip, sp);
404 if (callStack.back().ip_ == ip && callStack.back().sp_ == sp) {
405 HLOGW("we found a same frame, stop here");
406 break;
407 }
408 callStack.emplace_back(ip, sp);
409 } else {
410 HLOGV("no more frame step found. ret %d:%s", ret, GetUnwErrorName(ret).c_str());
411 break;
412 }
413 }
414 }
415 #endif
416
GetIpSP(uint64_t & ip,uint64_t & sp,const u64 * regs,size_t regNum) const417 bool CallStack::GetIpSP(uint64_t &ip, uint64_t &sp, const u64 *regs, size_t regNum) const
418 {
419 if (regNum > 0) {
420 if (!RegisterGetSPValue(sp, arch_, regs, regNum)) {
421 HLOGW("unable get sp");
422 return false;
423 }
424 if (!RegisterGetIPValue(ip, arch_, regs, regNum)) {
425 HLOGW("unable get ip");
426 return false;
427 }
428 if (ip != 0) {
429 return true;
430 }
431 } else {
432 HLOGW("reg size is 0");
433 return false;
434 }
435 return false;
436 }
437
438 #if HAVE_LIBUNWIND
DoUnwind(const VirtualThread & thread,std::vector<CallFrame> & callStack,size_t maxStackLevel)439 bool CallStack::DoUnwind(const VirtualThread &thread, std::vector<CallFrame> &callStack,
440 size_t maxStackLevel)
441 {
442 unw_addr_space_t addr_space;
443 UnwindInfo unwindInfo = {
444 .thread = thread,
445 .callStack = *this,
446 };
447 unw_cursor_t c;
448 if (unwindAddrSpaceMap_.count(thread.tid_) == 0) {
449 addr_space = unw_create_addr_space(&accessors_, 0);
450 if (!addr_space) {
451 HLOGE("Can't create unwind vaddress space.");
452 return false;
453 }
454 unwindAddrSpaceMap_.emplace(thread.tid_, addr_space);
455 unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL);
456 unw_flush_cache(addr_space, 0, 0);
457 } else {
458 addr_space = unwindAddrSpaceMap_.at(thread.tid_);
459 }
460
461 int ret = unw_init_remote(&c, addr_space, &unwindInfo);
462 if (ret) {
463 HLOGE("unwind error %d:%s see unw_error_t.", ret, GetUnwErrorName(ret).c_str());
464 return false;
465 } else {
466 UnwindStep(c, callStack, maxStackLevel);
467 }
468 return true;
469 }
470 #endif
471
UnwindCallStack(const VirtualThread & thread,bool abi32,u64 * regs,u64 regsNum,const u8 * stack,u64 stackSize,std::vector<CallFrame> & callStack,size_t maxStackLevel)472 bool CallStack::UnwindCallStack(const VirtualThread &thread, bool abi32, u64 *regs, u64 regsNum,
473 const u8 *stack, u64 stackSize, std::vector<CallFrame> &callStack,
474 size_t maxStackLevel)
475 {
476 regs_ = regs;
477 regsNum_ = regsNum;
478 stack_ = stack;
479 stackSize_ = stackSize;
480
481 arch_ = GetArchTypeFromABI(abi32);
482 UpdateRegForABI(arch_, regs_);
483 if (!RegisterGetSPValue(stackPoint_, arch_, regs_, regsNum_)) {
484 HLOGE("RegisterGetSPValue failed");
485 return false;
486 } else {
487 stackEnd_ = stackPoint_ + stackSize_;
488 }
489
490 #if HAVE_LIBUNWIND
491 uint64_t ip;
492 uint64_t sp;
493 if (!GetIpSP(ip, sp, regs_, regsNum_)) {
494 HLOGW("unable get sp or sp , unable unwind");
495 return false;
496 } else {
497 if (ip != 0) {
498 HLOGV("unwind:%zu: ip 0x%" PRIx64 " sp 0x%" PRIx64 "", callStack.size(), ip, sp);
499 callStack.emplace_back(ip, sp);
500 }
501 }
502
503 /*
504 * If we need more than one entry, do the DWARF
505 * unwind itself.
506 */
507 if (maxStackLevel - 1 > 0) {
508 return DoUnwind(thread, callStack, maxStackLevel);
509 }
510 #endif
511 return true;
512 }
513
LogFrame(const std::string msg,const std::vector<CallFrame> & frames)514 void CallStack::LogFrame(const std::string msg, const std::vector<CallFrame> &frames)
515 {
516 HLOGM("%s", msg.c_str());
517 int level = 0;
518 for (auto frame : frames) {
519 HLOGM("%d:%s", level++, frame.ToString().c_str());
520 }
521 }
522
523 /*
524 we should have CallStack cache for each thread
525 end begin
526 0. A -> B -> C -> E -> F
527 1. C -> E -> F
528 2. B -> C
529 3. A -> B -> C
530 4. B -> G -> H
531 5. J -> C
532
533 0 is our cache
534 1 2 3... is from record
535
536 use expandLimit to setup how may frame match is needs
537
538 */
DoExpandCallStack(std::vector<CallFrame> & newCallFrames,const std::vector<CallFrame> & cachedCallFrames,size_t expandLimit)539 size_t CallStack::DoExpandCallStack(std::vector<CallFrame> &newCallFrames,
540 const std::vector<CallFrame> &cachedCallFrames,
541 size_t expandLimit)
542 {
543 int maxCycle = 0;
544
545 if (expandLimit == 0 or newCallFrames.size() < expandLimit or
546 cachedCallFrames.size() < expandLimit) {
547 HLOGM("expandLimit %zu not match new %zu cache %zu", expandLimit, newCallFrames.size(),
548 cachedCallFrames.size());
549 return 0; // size not enough
550 }
551
552 // called (Stack Buttom) , this will NOT change when compare
553 // in case1 newIt -> C
554 // in case2 newIt -> B
555 const auto newIt = newCallFrames.end() - expandLimit;
556
557 HLOGM("try find new call chain bottom %s for limit %zu", newIt->ToString().c_str(),
558 expandLimit);
559
560 // first frame search, from called - > caller
561 // for case 2 it should found B
562 size_t distances = expandLimit - 1;
563 auto cachedIt = find(cachedCallFrames.begin(), cachedCallFrames.end(), *newIt);
564 if (cachedIt == cachedCallFrames.end()) {
565 HLOGM("not found in first search");
566 }
567
568 // cache frame found
569 while (std::distance(cachedIt, cachedCallFrames.end()) >= signed(expandLimit)) {
570 HLOG_ASSERT_MESSAGE(maxCycle++ < MAX_CALL_FRAME_EXPAND_CYCLE, "MAX_UNWIND_CYCLE = %d reach",
571 MAX_CALL_FRAME_EXPAND_CYCLE);
572
573 if (std::equal(newIt, newIt + expandLimit, cachedIt)) {
574 HLOGM("match %s + %zu", newIt->ToString().c_str(), expandLimit);
575 cachedIt += expandLimit; // in while we check the boundary safe
576 if (cachedIt == cachedCallFrames.end()) {
577 // same but no more need expand
578 break;
579 }
580
581 // expand the frame and make some log ?
582 LogFrame("newCallStack:", newCallFrames);
583 LogFrame("cachedCallStack:", cachedCallFrames);
584
585 newCallFrames.insert(newCallFrames.end(), cachedIt, cachedCallFrames.end());
586 auto expands = std::distance(cachedIt, cachedCallFrames.end());
587 HLOGV("merge callstack increse to %zu (+%zd) ", newCallFrames.size(), expands);
588 // we done the deal
589 return expands;
590 } else {
591 // quick search next same farme again
592 cachedIt++;
593 if (cachedIt != cachedCallFrames.end()) {
594 HLOGM("search next");
595 cachedIt = find(cachedIt, cachedCallFrames.end(), *newIt);
596 }
597 }
598 }
599 HLOGM("cachedIt distance %zd , need %zd", std::distance(cachedCallFrames.begin(), cachedIt),
600 distances);
601 return 0u; // nothing expand
602 }
603
ExpandCallStack(pid_t tid,std::vector<CallFrame> & callFrames,size_t expandLimit)604 size_t CallStack::ExpandCallStack(pid_t tid, std::vector<CallFrame> &callFrames, size_t expandLimit)
605 {
606 size_t expand = 0u;
607 if (expandLimit == 0) {
608 return expand; // nothing need to do
609 } else if (callFrames.size() < expandLimit) {
610 HLOGM("new callstack is too small, skip it");
611 return expand;
612 }
613 if (!cachedCallFramesMap_.count(tid)) {
614 cachedCallFramesMap_[tid].reserve(MAX_CALL_FRAME_EXPAND_CACHE_SIZE);
615 }
616 if (callFrames.size() >= 1u) {
617 // get top (Earliest caller)
618 HashList<uint64_t, std::vector<CallFrame>> &cachedCallFrames = cachedCallFramesMap_[tid];
619 HLOGV("find call stack frames in cache size %zu", cachedCallFrames.size());
620 // compare
621 using namespace std::rel_ops; // enable complement comparing operators
622 for (auto itr = cachedCallFrames.begin(); itr < cachedCallFrames.end(); ++itr) {
623 // each cached callstack
624 /*
625 stack 2 1 0
626 cache A -> B -> C
627 new B -> C
628 check:
629 1 if new B == cache C
630 2 if new B == cache B
631 3 if new C == new C (if limit > 0)
632 4 insert A after B in new stack
633 */
634 const std::vector<CallFrame> &cachedCallStack = *itr;
635 if (cachedCallStack.size() < expandLimit) {
636 HLOGM("cache callstack is too small, skip it");
637 continue; // check next
638 }
639 expand = DoExpandCallStack(callFrames, cachedCallStack, expandLimit);
640 if (expand > 0) {
641 break;
642 }
643 }
644 // add new one in to cache cachedCallFrames.
645 // further optimization can be done by caching pointer which avoids copying
646 // vector
647 cachedCallFrames[callFrames[0].ip_] = callFrames;
648 }
649 HLOGM("expand %zu", expand);
650 return expand;
651 }
652
CallStack()653 CallStack::CallStack() {}
654
~CallStack()655 CallStack::~CallStack()
656 {
657 #if HAVE_LIBUNWIND
658 for (auto &pair : unwindAddrSpaceMap_) {
659 unw_destroy_addr_space(pair.second);
660 }
661 #endif
662 }
663 } // namespace HiPerf
664 } // namespace Developtools
665 } // namespace OHOS
666