• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2023 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 <dlfcn.h>
20 #include <pthread.h>
21 #include <iostream>
22 
23 #include <string>
24 #include <utility>
25 #if HAVE_LIBUNWIND
26 #include <libunwind.h>
27 #include <libunwind_i.h>
28 #endif
29 
30 #include "dfx_regs.h"
31 #include "register.h"
32 #ifdef target_cpu_arm
33 // reg size is int (unw_word_t)
34 #define UNW_WORD_PFLAG "x"
35 #else
36 // reg size is long (unw_word_t)
37 #define UNW_WORD_PFLAG "zx"
38 #endif
39 namespace OHOS {
40 namespace Developtools {
41 namespace HiPerf {
42 using namespace OHOS::HiviewDFX;
43 
ReadVirtualThreadMemory(UnwindInfo & unwindInfoPtr,ADDR_TYPE vaddr,ADDR_TYPE * data)44 bool CallStack::ReadVirtualThreadMemory(UnwindInfo &unwindInfoPtr, ADDR_TYPE vaddr, ADDR_TYPE *data)
45 {
46     if (__builtin_expect(unwindInfoPtr.thread.pid_ == unwindInfoPtr.callStack.lastPid_ &&
47         vaddr == unwindInfoPtr.callStack.lastAddr_, true)) {
48         *data = unwindInfoPtr.callStack.lastData_;
49         return true;
50     }
51 
52     if (unwindInfoPtr.thread.ReadRoMemory(vaddr, reinterpret_cast<uint8_t*>(data), sizeof(ADDR_TYPE))) {
53         unwindInfoPtr.callStack.lastPid_ = unwindInfoPtr.thread.pid_;
54         unwindInfoPtr.callStack.lastAddr_ = vaddr;
55         unwindInfoPtr.callStack.lastData_ = *data;
56         return true;
57     } else {
58         unwindInfoPtr.callStack.lastPid_ = -1;
59         unwindInfoPtr.callStack.lastAddr_ = 0;
60         return false;
61     }
62 }
63 
64 #if HAVE_LIBUNWIND
65 const std::map<unw_error_t, const std::string> UNW_ERROR_MAP = {
66     {UNW_ESUCCESS, std::to_string(UNW_ESUCCESS)},
67     {UNW_EUNSPEC, std::to_string(UNW_EUNSPEC)},
68     {UNW_ENOMEM, std::to_string(UNW_ENOMEM)},
69     {UNW_EBADREG, std::to_string(UNW_EBADREG)},
70     {UNW_EREADONLYREG, std::to_string(UNW_EREADONLYREG)},
71     {UNW_ESTOPUNWIND, std::to_string(UNW_ESTOPUNWIND)},
72     {UNW_EINVALIDIP, std::to_string(UNW_EINVALIDIP)},
73     {UNW_EBADFRAME, std::to_string(UNW_EBADFRAME)},
74     {UNW_EINVAL, std::to_string(UNW_EINVAL)},
75     {UNW_EBADVERSION, std::to_string(UNW_EBADVERSION)},
76     {UNW_ENOINFO, std::to_string(UNW_ENOINFO)},
77 };
GetUnwErrorName(int error)78 const std::string CallStack::GetUnwErrorName(int error)
79 {
80     if (UNW_ERROR_MAP.count(static_cast<unw_error_t>(-error)) > 0) {
81         return UNW_ERROR_MAP.at(static_cast<unw_error_t>(-error));
82     } else {
83         return "UNKNOW_UNW_ERROR";
84     }
85 }
86 
dumpUDI(unw_dyn_info_t & di)87 void CallStack::dumpUDI(unw_dyn_info_t &di)
88 {
89     HLOGV("unwind_table info: ");
90     HLOGV(" di.start_ip:            0x%016" UNW_WORD_PFLAG "", di.start_ip);
91     HLOGV(" di.end_ip:              0x%016" UNW_WORD_PFLAG "", di.end_ip);
92     HLOGV(" di.u.rti.segbase:       0x%016" UNW_WORD_PFLAG "", di.u.rti.segbase);
93     HLOGV(" di.u.rti.table_data:    0x%016" UNW_WORD_PFLAG "", di.u.rti.table_data);
94     HLOGV(" di.u.rti.table_len:     0x%016" UNW_WORD_PFLAG "", di.u.rti.table_len);
95 }
96 
fillUDI(unw_dyn_info_t & di,SymbolsFile & symbolsFile,std::shared_ptr<DfxMap> map,const VirtualThread & thread)97 bool CallStack::fillUDI(unw_dyn_info_t &di, SymbolsFile &symbolsFile, std::shared_ptr<DfxMap> map,
98                         const VirtualThread &thread)
99 {
100     di.start_ip = map->begin;
101     di.end_ip = map->end;
102 #ifndef target_cpu_arm
103     uint64_t fdeTableElfOffset;
104     uint64_t fdeTableSize;
105     uint64_t ehFrameHdrElfOffset;
106     if ((UNW_INFO_FORMAT_REMOTE_TABLE == di.format) &&
107         symbolsFile.GetHDRSectionInfo(ehFrameHdrElfOffset, fdeTableElfOffset, fdeTableSize)) {
108         /*
109             unw_word_t name_ptr;        // addr. of table name (e.g., library name)
110             unw_word_t segbase;         // segment base
111             unw_word_t table_len;       // must be a multiple of sizeof(unw_word_t)!
112             unw_word_t table_data;
113         */
114         /*
115             all the rti addr is offset of the elf file
116             begin - page offset = elf file base addr in vaddr user space
117             begin - page offset + elf offset = vaddr in real word.(for this thread)
118         */
119 
120         // segbase is file offset .
121         /*
122             00200000-00344000 r--p 00000000 08:02 46404365
123             00344000-005c4000 r-xp 00143000 08:02 46404365
124 
125             LOAD           0x00000000001439c0 0x00000000003449c0 0x00000000003449c0
126                             0x000000000027f3c0 0x000000000027f3c0  R E    0x1000
127 
128             GNU_EH_FRAME   0x00000000000f3248 0x00000000002f3248 0x00000000002f3248
129                             0x000000000000bb04 0x000000000000bb04  R      0x4
130 
131         */
132         auto ehFrameMap = thread.FindMapByFileInfo(map->name, ehFrameHdrElfOffset);
133         if (ehFrameMap == nullptr) {
134             HLOGE("no ehframe map found.");
135             return false;
136         }
137 
138         di.u.rti.segbase = ehFrameMap->begin + ehFrameHdrElfOffset - ehFrameMap->offset;
139         di.u.rti.table_data = ehFrameMap->begin + fdeTableElfOffset - ehFrameMap->offset;
140         di.u.rti.table_len = fdeTableSize / sizeof(uintptr_t);
141 
142         HLOGV(" map pageoffset:         0x%016" PRIx64 "", map->offset);
143         HLOGV(" ehFrameHdrElfOffset:    0x%016" PRIx64 "", ehFrameHdrElfOffset);
144         HLOGV(" fdeTableElfOffset:      0x%016" PRIx64 "", fdeTableElfOffset);
145         HLOGV(" fdeTableSize:           0x%016" PRIx64 "", fdeTableSize);
146         return true;
147     } else {
148         HLOGD("SymbolsFile::GetHDRSectionInfo() failed");
149     }
150 #else
151     uint64_t SectionVaddr;
152     uint64_t SectionSize;
153     uint64_t SectionFileOffset;
154     if ((UNW_INFO_FORMAT_ARM_EXIDX == di.format) &&
155         symbolsFile.GetSectionInfo(ARM_EXIDX, SectionVaddr, SectionSize, SectionFileOffset)) {
156         auto targetMap = thread.FindMapByFileInfo(map->name, SectionFileOffset);
157         if (targetMap == nullptr) {
158             HLOGE("no debug map found.");
159             return false;
160         }
161         HLOGV(" begin: %" PRIx64 " offset:%" PRIx64 "", targetMap->begin,
162               targetMap->offset);
163 
164         di.u.rti.table_data = targetMap->begin + SectionFileOffset - targetMap->offset;
165         di.u.rti.table_len = SectionSize;
166         HLOGV(" SectionName:           %s", std::string(ARM_EXIDX).c_str());
167         HLOGV(" SectionVaddrt:         0x%016" PRIx64 "", SectionVaddr);
168         HLOGV(" SectionFileOffset      0x%016" PRIx64 "", SectionFileOffset);
169         HLOGV(" SectionSize:           0x%016" PRIx64 "", SectionSize);
170 
171         // GetSectionInfo return true, but SectionVaddr || SectionSize is 0 ???
172         HLOG_ASSERT(SectionVaddr != 0 && SectionSize != 0);
173         return true;
174     } else {
175         HLOGD("SymbolsFile::GetSectionInfo() failed");
176     }
177 #endif
178     return false;
179 }
180 
181 /*
182     https://www.nongnu.org/libunwind/man/libunwind-dynamic(3).html
183 */
FindUnwindTable(SymbolsFile * symbolsFile,std::shared_ptr<DfxMap> map,UnwindInfo * unwindInfoPtr,unw_addr_space_t as,unw_word_t ip,unw_proc_info_t * pi,int need_unwind_info,void * arg)184 int CallStack::FindUnwindTable(SymbolsFile *symbolsFile, std::shared_ptr<DfxMap> map,
185                                UnwindInfo *unwindInfoPtr, unw_addr_space_t as, unw_word_t ip,
186                                unw_proc_info_t *pi, int need_unwind_info, void *arg)
187 {
188     HLOGM("try search debug info at %s", symbolsFile->filePath_.c_str());
189     auto &dynInfoProcessMap = unwindInfoPtr->callStack.unwindTableInfoMap_;
190     // all the thread in same process have same map and symbols
191     if (dynInfoProcessMap.find(unwindInfoPtr->thread.pid_) == dynInfoProcessMap.end()) {
192         dynInfoProcessMap.emplace(unwindInfoPtr->thread.pid_, dsoUnwDynInfoMap {});
193     }
194     dsoUnwDynInfoMap &dynFileMap = dynInfoProcessMap[unwindInfoPtr->thread.pid_];
195     // find use dso name as key
196     if (dynFileMap.find(symbolsFile->filePath_) == dynFileMap.end()) {
197         unw_dyn_info_t newdi;
198         if (memset_s(&newdi, sizeof(unw_dyn_info_t), 0, sizeof(unw_dyn_info_t)) != EOK) {
199             HLOGE("memset_s() failed");
200             return -UNW_EUNSPEC;
201         }
202 #ifdef target_cpu_arm
203         // arm use .ARM.exidx , not use ehframe
204         newdi.format = UNW_INFO_FORMAT_ARM_EXIDX;
205 #else
206         // otherwise we use EH FRAME
207         newdi.format = UNW_INFO_FORMAT_REMOTE_TABLE;
208 #endif
209         if (fillUDI(newdi, *symbolsFile, map, unwindInfoPtr->thread)) {
210             dumpUDI(newdi);
211             // we make a option empty value first
212             std::optional<unw_dyn_info_t> &odi = dynFileMap[symbolsFile->filePath_];
213             odi = newdi;
214         } else {
215             HLOGV("fillUDI failed()");
216             return -UNW_EUNSPEC;
217         }
218     }
219 
220     HLOG_ASSERT(dynInfoProcessMap.find(unwindInfoPtr->thread.pid_) != dynInfoProcessMap.end());
221     HLOG_ASSERT_MESSAGE(dynFileMap.find(symbolsFile->filePath_) != dynFileMap.end(), "%s",
222                         symbolsFile->filePath_.c_str());
223     std::optional<unw_dyn_info_t> &odi =
224         dynInfoProcessMap.at(unwindInfoPtr->thread.pid_).at(symbolsFile->filePath_);
225 
226     if (odi.has_value()) {
227         unw_dyn_info_t &di = odi.value();
228         /*
229             we don't use dwarf_search_unwind_table
230             because in arm it will search two function:
231             1 arm_search_unwind_table first
232             2 dwarf_search_unwind_table
233 
234             see libunwind_i.h for arm
235             define tdep_search_unwind_table UNW_OBJ(search_unwind_table)
236 
237         */
238         int ret = static_cast<unw_error_t>(
239             tdep_search_unwind_table(as, ip, &di, pi, need_unwind_info, arg));
240 
241         HLOGM("search_unwind_table ret %d:%s", ret, GetUnwErrorName(ret).c_str());
242 
243         if (UNW_ESUCCESS != ret) {
244             if (UNW_ENOINFO != ret) {
245                 HLOGW("search_unwind_table ret error %d:%s", ret, GetUnwErrorName(ret).c_str());
246             }
247             return -UNW_EUNSPEC;
248         } else {
249             return UNW_ESUCCESS;
250         }
251     } else {
252         HLOGW("no debug info found for thread %d:%s", unwindInfoPtr->thread.tid_,
253               unwindInfoPtr->thread.name_.c_str());
254         return -UNW_EUNSPEC;
255     }
256 }
257 
FindProcInfo(unw_addr_space_t as,unw_word_t ip,unw_proc_info_t * pi,int need_unwind_info,void * arg)258 int CallStack::FindProcInfo(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
259                             int need_unwind_info, void *arg)
260 {
261     UnwindInfo *unwindInfoPtr = static_cast<UnwindInfo *>(arg);
262 
263     HLOGM("need_unwind_info ret %d ip %" UNW_WORD_PFLAG "", need_unwind_info, ip);
264     auto map = unwindInfoPtr->thread.FindMapByAddr(ip);
265     if (map != nullptr) {
266         SymbolsFile *symbolsFile = unwindInfoPtr->thread.FindSymbolsFileByMap(map);
267         if (symbolsFile != nullptr) {
268             return FindUnwindTable(symbolsFile, map, unwindInfoPtr, as, ip, pi, need_unwind_info, arg);
269         } else {
270             HLOGW("no symbols file found for thread %d:%s", unwindInfoPtr->thread.tid_,
271                   unwindInfoPtr->thread.name_.c_str());
272         }
273     } else {
274         HLOGE("ip 0x%016" UNW_WORD_PFLAG " not found in thread %d:%s", ip,
275               unwindInfoPtr->thread.tid_, unwindInfoPtr->thread.name_.c_str());
276     }
277 
278     return -UNW_EUNSPEC;
279 }
280 
AccessMem(unw_addr_space_t as,unw_word_t addr,unw_word_t * valuePoint,int writeOperation,void * arg)281 int CallStack::AccessMem([[maybe_unused]] unw_addr_space_t as, unw_word_t addr,
282                          unw_word_t *valuePoint, int writeOperation, void *arg)
283 {
284     UnwindInfo *unwindInfoPtr = static_cast<UnwindInfo *>(arg);
285     *valuePoint = 0;
286 
287     /* Check overflow. */
288     if (addr + sizeof(unw_word_t) < addr) {
289         HLOGE("address overfolw at 0x%" UNW_WORD_PFLAG " increase 0x%zu", addr, sizeof(unw_word_t));
290         return -UNW_EUNSPEC;
291     }
292 
293     if (addr < unwindInfoPtr->callStack.stackPoint_ or
294         addr + sizeof(unw_word_t) >= unwindInfoPtr->callStack.stackEnd_) {
295         if (ReadVirtualThreadMemory(*unwindInfoPtr, addr, valuePoint)) {
296             HLOGM("access_mem addr get val 0x%" UNW_WORD_PFLAG ", from mmap", *valuePoint);
297         } else {
298             HLOGW("access_mem addr failed, from mmap, 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<DfxFrame> & callStack,size_t maxStackLevel)377 void CallStack::UnwindStep(unw_cursor_t &c, std::vector<DfxFrame> &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().pc == 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<DfxFrame> & callStack,size_t maxStackLevel)439 bool CallStack::DoUnwind(const VirtualThread &thread, std::vector<DfxFrame> &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<DfxFrame> & 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<DfxFrame> &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     uint64_t ip;
491     uint64_t sp;
492     if (!GetIpSP(ip, sp, regs_, regsNum_)) {
493         HLOGW("unable get sp or sp , unable unwind");
494         return false;
495     } else {
496         if (ip != 0) {
497             HLOGV("unwind:%zu: ip 0x%" PRIx64 " sp 0x%" PRIx64 "", callStack.size(), ip, sp);
498             callStack.emplace_back(ip, sp);
499         }
500     }
501 
502     /*
503      * If we need more than one entry, do the DWARF
504      * unwind itself.
505      */
506     if (maxStackLevel - 1 > 0) {
507 #if HAVE_LIBUNWIND
508         return DoUnwind(thread, callStack, maxStackLevel);
509 #else
510         return DoUnwind2(thread, callStack, maxStackLevel);
511 #endif
512     }
513     return true;
514 }
515 
LogFrame(const std::string msg,const std::vector<DfxFrame> & frames)516 void CallStack::LogFrame(const std::string msg, const std::vector<DfxFrame> &frames)
517 {
518     HLOGM("%s", msg.c_str());
519     int level = 0;
520     for (auto& frame : frames) {
521         HLOGM("%d:%s", level++, frame.ToString().c_str());
522     }
523 }
524 
525 /*
526 we should have CallStack cache for each thread
527 end                    begin
528 0. A -> B -> C -> E -> F
529 1.           C -> E -> F
530 2.      B -> C
531 3. A -> B -> C
532 4.      B -> G -> H
533 5.      J -> C
534 
535 0 is our cache
536 1 2 3... is from record
537 
538 use expandLimit to setup how may frame match is needs
539 
540 */
DoExpandCallStack(std::vector<DfxFrame> & newCallFrames,const std::vector<DfxFrame> & cachedCallFrames,size_t expandLimit)541 size_t CallStack::DoExpandCallStack(std::vector<DfxFrame> &newCallFrames,
542                                     const std::vector<DfxFrame> &cachedCallFrames,
543                                     size_t expandLimit)
544 {
545     int maxCycle = 0;
546 
547     if (expandLimit == 0 or newCallFrames.size() < expandLimit or
548         cachedCallFrames.size() < expandLimit) {
549         HLOGM("expandLimit %zu not match new %zu cache %zu", expandLimit, newCallFrames.size(),
550               cachedCallFrames.size());
551         return 0; // size not enough
552     }
553 
554     // called (Stack Bottom) , this will NOT change when compare
555     // in case1 newIt -> C
556     // in case2 newIt -> B
557     const auto newIt = newCallFrames.end() - expandLimit;
558     if (newIt != newCallFrames.end()) {
559         HLOGM("try find new call chain bottom %s for limit %zu", newIt->ToString().c_str(),
560             expandLimit);
561     }
562 
563     // first frame search, from called - > caller
564     // for case 2 it should found B
565     size_t distances = expandLimit - 1;
566     auto cachedIt = find(cachedCallFrames.begin(), cachedCallFrames.end(), *newIt);
567     if (cachedIt == cachedCallFrames.end()) {
568         HLOGM("not found in first search");
569     }
570 
571     // cache frame found
572     while (std::distance(cachedIt, cachedCallFrames.end()) >= signed(expandLimit)) {
573         HLOG_ASSERT_MESSAGE(maxCycle++ < MAX_CALL_FRAME_EXPAND_CYCLE, "MAX_UNWIND_CYCLE = %d reach",
574                             MAX_CALL_FRAME_EXPAND_CYCLE);
575 
576         if (std::equal(newIt, newIt + expandLimit, cachedIt)) {
577             HLOGM("match %s + %zu", newIt->ToString().c_str(), expandLimit);
578             cachedIt += expandLimit; // in while we check the boundary safe
579             if (cachedIt == cachedCallFrames.end()) {
580                 // same but no more need expand
581                 break;
582             }
583 
584             // expand the frame and make some log ?
585             LogFrame("newCallStack:", newCallFrames);
586             LogFrame("cachedCallStack:", cachedCallFrames);
587 
588             newCallFrames.insert(newCallFrames.end(), cachedIt, cachedCallFrames.end());
589             auto expands = std::distance(cachedIt, cachedCallFrames.end());
590             HLOGV("merge callstack increse to %zu (+%zd) ", newCallFrames.size(), expands);
591             // we done the deal
592             return expands;
593         } else {
594             // quick search next same farme again
595             cachedIt++;
596             if (cachedIt != cachedCallFrames.end()) {
597                 HLOGM("search next");
598                 cachedIt = find(cachedIt, cachedCallFrames.end(), *newIt);
599             }
600         }
601     }
602     HLOGM("cachedIt distance %zd , need %zd", std::distance(cachedCallFrames.begin(), cachedIt),
603           distances);
604     return 0u; // nothing expand
605 }
606 
ExpandCallStack(pid_t tid,std::vector<DfxFrame> & callFrames,size_t expandLimit)607 size_t CallStack::ExpandCallStack(pid_t tid, std::vector<DfxFrame> &callFrames, size_t expandLimit)
608 {
609     size_t expand = 0u;
610     if (expandLimit == 0) {
611         return expand; // nothing need to do
612     } else if (callFrames.size() < expandLimit) {
613         HLOGM("new callstack is too small, skip it");
614         return expand;
615     }
616     if (!cachedCallFramesMap_.count(tid)) {
617         cachedCallFramesMap_[tid].reserve(MAX_CALL_FRAME_EXPAND_CACHE_SIZE);
618     }
619     if (callFrames.size() >= 1u) {
620         // get top  (Earliest caller)
621         HashList<uint64_t, std::vector<DfxFrame>> &cachedCallFrames = cachedCallFramesMap_[tid];
622         HLOGV("find call stack frames in cache size %zu", cachedCallFrames.size());
623         // compare
624         using namespace std::rel_ops; // enable complement comparing operators
625         for (auto itr = cachedCallFrames.begin(); itr < cachedCallFrames.end(); ++itr) {
626             // each cached callstack
627             /*
628                 stack 2    1    0
629                 cache A -> B -> C
630                 new        B -> C
631                 check:
632                 1 if new B == cache C
633                 2 if new B == cache B
634                 3 if new C == new C (if limit > 0)
635                 4 insert A after B in new stack
636             */
637             const std::vector<DfxFrame> &cachedCallStack = *itr;
638             if (cachedCallStack.size() < expandLimit) {
639                 HLOGM("cache callstack is too small, skip it");
640                 continue; // check next
641             }
642             expand = DoExpandCallStack(callFrames, cachedCallStack, expandLimit);
643             if (expand > 0) {
644                 break;
645             }
646         }
647         // add new one in to cache cachedCallFrames.
648         // further optimization can be done by caching pointer which avoids copying
649         // vector
650         cachedCallFrames[callFrames[0].pc] = callFrames;
651     }
652     HLOGM("expand %zu", expand);
653     return expand;
654 }
655 
656 #if defined(HAVE_LIBUNWINDER) && HAVE_LIBUNWINDER
DoUnwind2(const VirtualThread & thread,std::vector<DfxFrame> & callStack,size_t maxStackLevel)657 bool CallStack::DoUnwind2(const VirtualThread &thread, std::vector<DfxFrame> &callStack,
658                           size_t maxStackLevel)
659 {
660 #ifdef target_cpu_x86_64
661     return false;
662 #else
663     UnwindInfo unwindInfo = {
664         .thread = thread,
665         .callStack = *this,
666     };
667 
668     if (pidUnwinder_.count(thread.pid_) == 0) {
669         pidUnwinder_.emplace(thread.pid_, std::make_shared<Unwinder>(accessor_));
670     }
671     auto unwinder = pidUnwinder_[thread.pid_];
672 
673 #ifdef target_cpu_arm
674     static std::shared_ptr<DfxRegs> regs = std::make_shared<DfxRegsArm>();
675     std::vector<uintptr_t> tempRegs;
676     for (auto i = 0; i < regsNum_; ++i) {
677         tempRegs.push_back(static_cast<uintptr_t>(regs_[i]));
678     }
679     regs->SetRegsData(tempRegs);
680 #else
681     static std::shared_ptr<DfxRegs> regs = std::make_shared<DfxRegsArm64>();
682     regs->SetRegsData(reinterpret_cast<uintptr_t*>(regs_), regsNum_);
683 #endif
684     if (unwinder == nullptr) {
685         return false;
686     }
687     unwinder->SetRegs(regs);
688     unwinder->Unwind(&unwindInfo);
689     callStack = unwinder->GetFrames();
690     HLOGD("callStack size:%zu", callStack.size());
691     for (auto frame: callStack) {
692         HLOGD("pc 0x%" PRIx64 " sp 0x%" PRIx64 "", frame.pc, frame.sp);
693     }
694     auto lastIt = callStack.end() - 1;
695     auto preIt = lastIt - 1;
696     if (lastIt != callStack.end() && preIt != callStack.end() &&
697         callStack.size() > 1 && lastIt->pc == preIt->pc && lastIt->sp == preIt->sp) {
698         callStack.erase(lastIt);
699         HLOGD("remove last callframe");
700     }
701     return true;
702 #endif
703 }
704 
DumpTableInfo(UnwindTableInfo & outTableInfo)705 void CallStack::DumpTableInfo(UnwindTableInfo &outTableInfo)
706 {
707     HLOGV("unwind_table info: ");
708     HLOGV(" start_ip:            0x%016" UNW_WORD_PFLAG "", outTableInfo.startPc);
709     HLOGV(" end_ip:              0x%016" UNW_WORD_PFLAG "", outTableInfo.endPc);
710     HLOGV(" segbase:             0x%016" UNW_WORD_PFLAG "", outTableInfo.segbase);
711     HLOGV(" table_data:          0x%016" UNW_WORD_PFLAG "", outTableInfo.tableData);
712     HLOGV(" table_len:           0x%016" UNW_WORD_PFLAG "", outTableInfo.tableLen);
713 }
714 
FillUnwindTable(SymbolsFile * symbolsFile,std::shared_ptr<DfxMap> map,UnwindInfo * unwindInfoPtr,uintptr_t pc,UnwindTableInfo & outTableInfo)715 int CallStack::FillUnwindTable(SymbolsFile *symbolsFile, std::shared_ptr<DfxMap> map, UnwindInfo *unwindInfoPtr,
716                                uintptr_t pc, UnwindTableInfo& outTableInfo)
717 {
718     HLOGM("try search debug info at %s", symbolsFile->filePath_.c_str());
719     if (unwindInfoPtr == nullptr) {
720         return -1;
721     }
722     auto &tableInfoMap = unwindInfoPtr->callStack.unwindTableInfoMap_;
723     // all the thread in same process have same mmap and symbols
724     if (tableInfoMap.find(unwindInfoPtr->thread.pid_) == tableInfoMap.end()) {
725         tableInfoMap.emplace(unwindInfoPtr->thread.pid_, DsoUnwindTableInfoMap {});
726     }
727     DsoUnwindTableInfoMap &unwTabMap = tableInfoMap[unwindInfoPtr->thread.pid_];
728     // find use dso name as key
729     if (unwTabMap.find(symbolsFile->filePath_) == unwTabMap.end()) {
730         UnwindTableInfo uti;
731         auto elf = symbolsFile->GetElfFile();
732         if (elf == nullptr) {
733             return -1;
734         }
735         if (elf->FindUnwindTableInfo(pc, map, uti) == 0) {
736             if (uti.format == -1) {
737                 HLOGV("parse unwind table failed.");
738                 return -1;
739             }
740             unwTabMap[symbolsFile->filePath_] = uti;
741             outTableInfo = unwTabMap[symbolsFile->filePath_];
742             DumpTableInfo(uti);
743             return 0;
744         } else {
745             HLOGV("FillUnwindTable failed");
746             return -1;
747         }
748     } else {
749         outTableInfo = unwTabMap[symbolsFile->filePath_];
750         return 0;
751     }
752     return -1;
753 }
754 
FindUnwindTable(uintptr_t pc,UnwindTableInfo & outTableInfo,void * arg)755 int CallStack::FindUnwindTable(uintptr_t pc, UnwindTableInfo& outTableInfo, void *arg)
756 {
757     UnwindInfo *unwindInfoPtr = static_cast<UnwindInfo *>(arg);
758     if (unwindInfoPtr == nullptr) {
759         return -1;
760     }
761     int64_t mapIndex = unwindInfoPtr->thread.FindMapIndexByAddr(pc);
762     if (mapIndex >= 0) {
763         auto map = unwindInfoPtr->thread.GetMaps()[mapIndex];
764         if (map != nullptr) {
765             SymbolsFile *symbolsFile = unwindInfoPtr->thread.FindSymbolsFileByMap(map);
766             if (symbolsFile != nullptr) {
767                 return FillUnwindTable(symbolsFile, map, unwindInfoPtr, pc, outTableInfo);
768             } else {
769                 HLOGD("no symbols file found for thread %d:%s", unwindInfoPtr->thread.tid_,
770                     unwindInfoPtr->thread.name_.c_str());
771             }
772         } else {
773             HLOGD("pc 0x%016" UNW_WORD_PFLAG " not found in thread %d:%s", pc,
774                 unwindInfoPtr->thread.tid_, unwindInfoPtr->thread.name_.c_str());
775         }
776     } else {
777         HLOGD("map index is -1");
778     }
779     return -1;
780 }
781 
AccessMem2(uintptr_t addr,uintptr_t * val,void * arg)782 int CallStack::AccessMem2(uintptr_t addr, uintptr_t *val, void *arg)
783 {
784     UnwindInfo *unwindInfoPtr = static_cast<UnwindInfo *>(arg);
785     *val = 0;
786 
787     /* Check overflow. */
788     if (unwindInfoPtr == nullptr || addr + sizeof(uintptr_t) < addr) {
789         HLOGE("unwindInfoPtr is null or address overflow at 0x%" UNW_WORD_PFLAG " increase 0x%zu",
790               addr, sizeof(uintptr_t));
791         return -1;
792     }
793 
794     if (addr < unwindInfoPtr->callStack.stackPoint_ or
795         addr + sizeof(uintptr_t) >= unwindInfoPtr->callStack.stackEnd_) {
796         if (ReadVirtualThreadMemory(*unwindInfoPtr, addr, val)) {
797             HLOGM("access_mem addr get val 0x%" UNW_WORD_PFLAG ", from mmap", *val);
798         } else {
799             HLOGW("access_mem mmap 0x%" PRIx64 " failed, STACK RANGE 0x%" PRIx64 "- 0x%" PRIx64 "(0x%" PRIx64 ")",
800                   (uint64_t)addr,
801                   unwindInfoPtr->callStack.stackPoint_, unwindInfoPtr->callStack.stackEnd_,
802                   unwindInfoPtr->callStack.stackEnd_ - unwindInfoPtr->callStack.stackPoint_);
803             return -1;
804         }
805     } else {
806         size_t stackOffset = addr - unwindInfoPtr->callStack.stackPoint_;
807         *val = *(uintptr_t *)&unwindInfoPtr->callStack.stack_[stackOffset];
808         HLOGM("access_mem addr %p val %" UNW_WORD_PFLAG ", from stack offset %zu",
809               reinterpret_cast<void *>(addr), *val, stackOffset);
810     }
811 
812     return 0;
813 }
GetMapByPc(uintptr_t pc,std::shared_ptr<DfxMap> & map,void * arg)814 int CallStack::GetMapByPc(uintptr_t pc, std::shared_ptr<DfxMap>& map, void *arg)
815 {
816     UnwindInfo *unwindInfoPtr = static_cast<UnwindInfo *>(arg);
817     int64_t mapIndex = unwindInfoPtr->thread.FindMapIndexByAddr(pc);
818     if (mapIndex >= 0) {
819         map = unwindInfoPtr->thread.GetMaps()[mapIndex];
820         if (map != nullptr) {
821             return 0;
822         }
823     }
824     HLOGD("pc 0x%016" UNW_WORD_PFLAG " not found in thread %d:%s", pc,
825           unwindInfoPtr->thread.tid_, unwindInfoPtr->thread.name_.c_str());
826     return -1;
827 }
828 #endif
829 
CallStack()830 CallStack::CallStack()
831 {
832 #if defined(HAVE_LIBUNWINDER) && HAVE_LIBUNWINDER
833     accessor_ = std::make_shared<OHOS::HiviewDFX::UnwindAccessors>();
834     accessor_->FindUnwindTable = &CallStack::FindUnwindTable;
835     accessor_->AccessMem = &CallStack::AccessMem2;
836     accessor_->AccessReg = nullptr;
837     accessor_->GetMapByPc = &CallStack::GetMapByPc;
838 #endif
839 }
840 
~CallStack()841 CallStack::~CallStack()
842 {
843 #if HAVE_LIBUNWIND
844     for (auto &pair : unwindAddrSpaceMap_) {
845         unw_destroy_addr_space(pair.second);
846     }
847 #endif
848 }
849 } // namespace HiPerf
850 } // namespace Developtools
851 } // namespace OHOS
852