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