• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #if defined(target_cpu_arm)
29 // reg size is int (unw_word_t)
30 #define UNW_WORD_PFLAG "x"
31 #elif defined(__arm__) && defined(target_cpu_x64)
32 // reg size is long long (unw_word_t)
33 #define UNW_WORD_PFLAG "llx"
34 #else
35 // reg size is long (unw_word_t)
36 #define UNW_WORD_PFLAG "zx"
37 #endif
38 namespace OHOS {
39 namespace Developtools {
40 namespace HiPerf {
41 #if HAVE_LIBUNWIND
42 const std::map<unw_error_t, const std::string> UNW_ERROR_MAP = {
43     {UNW_ESUCCESS, std::to_string(UNW_ESUCCESS)},
44     {UNW_EUNSPEC, std::to_string(UNW_EUNSPEC)},
45     {UNW_ENOMEM, std::to_string(UNW_ENOMEM)},
46     {UNW_EBADREG, std::to_string(UNW_EBADREG)},
47     {UNW_EREADONLYREG, std::to_string(UNW_EREADONLYREG)},
48     {UNW_ESTOPUNWIND, std::to_string(UNW_ESTOPUNWIND)},
49     {UNW_EINVALIDIP, std::to_string(UNW_EINVALIDIP)},
50     {UNW_EBADFRAME, std::to_string(UNW_EBADFRAME)},
51     {UNW_EINVAL, std::to_string(UNW_EINVAL)},
52     {UNW_EBADVERSION, std::to_string(UNW_EBADVERSION)},
53     {UNW_ENOINFO, std::to_string(UNW_ENOINFO)},
54 };
GetUnwErrorName(int error)55 const std::string CallStack::GetUnwErrorName(int error)
56 {
57     if (UNW_ERROR_MAP.count(static_cast<unw_error_t>(-error)) > 0) {
58         return UNW_ERROR_MAP.at(static_cast<unw_error_t>(-error));
59     } else {
60         return "UNKNOW_UNW_ERROR";
61     }
62 }
63 
dumpUDI(unw_dyn_info_t & di)64 void CallStack::dumpUDI(unw_dyn_info_t &di)
65 {
66     HLOGV("unwind_table info: ");
67     HLOGV(" di.start_ip:            0x%016" UNW_WORD_PFLAG "", di.start_ip);
68     HLOGV(" di.end_ip:              0x%016" UNW_WORD_PFLAG "", di.end_ip);
69     HLOGV(" di.u.rti.segbase:       0x%016" UNW_WORD_PFLAG "", di.u.rti.segbase);
70     HLOGV(" di.u.rti.table_data:    0x%016" UNW_WORD_PFLAG "", di.u.rti.table_data);
71     HLOGV(" di.u.rti.table_len:     0x%016" UNW_WORD_PFLAG "", di.u.rti.table_len);
72 }
73 
fillUDI(unw_dyn_info_t & di,SymbolsFile & symbolsFile,const MemMapItem & mmap,const VirtualThread & thread)74 bool CallStack::fillUDI(unw_dyn_info_t &di, SymbolsFile &symbolsFile, const MemMapItem &mmap,
75                         const VirtualThread &thread)
76 {
77     di.start_ip = mmap.begin_;
78     di.end_ip = mmap.end_;
79 #ifndef target_cpu_arm
80     uint64_t fdeTableElfOffset, fdeTableSize, 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, SectionSize, SectionFileOffset;
128     if ((UNW_INFO_FORMAT_ARM_EXIDX == di.format) &&
129         symbolsFile.GetSectionInfo(ARM_EXIDX, SectionVaddr, SectionSize, SectionFileOffset)) {
130         const MemMapItem *targetMmap = thread.FindMapByFileInfo(mmap.name_, SectionFileOffset);
131         if (targetMmap == nullptr) {
132             HLOGE("no debug mmap found.");
133             return false;
134         }
135         HLOGV(" begin: %" PRIx64 " offset:%" PRIx64 "", targetMmap->begin_,
136               targetMmap->pageoffset_);
137 
138         di.u.rti.table_data = targetMmap->begin_ + SectionFileOffset - targetMmap->pageoffset_;
139         di.u.rti.table_len = SectionSize;
140         HLOGV(" SectionName:           %s", std::string(ARM_EXIDX).c_str());
141         HLOGV(" SectionVaddrt:         0x%016" PRIx64 "", SectionVaddr);
142         HLOGV(" SectionFileOffset      0x%016" PRIx64 "", SectionFileOffset);
143         HLOGV(" SectionSize:           0x%016" PRIx64 "", SectionSize);
144 
145         // GetSectionInfo return true, but SectionVaddr || SectionSize is 0 ???
146         HLOG_ASSERT(SectionVaddr != 0 && SectionSize != 0);
147         return true;
148     } else {
149         HLOGD("SymbolsFile::GetSectionInfo() failed");
150     }
151 #endif
152     return false;
153 }
154 
155 /*
156     https://www.nongnu.org/libunwind/man/libunwind-dynamic(3).html
157 */
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)158 int CallStack::FindUnwindTable(SymbolsFile *symbolsFile, const MemMapItem &mmap,
159                                UnwindInfo *unwindInfoPtr, unw_addr_space_t as, unw_word_t ip,
160                                unw_proc_info_t *pi, int need_unwind_info, void *arg)
161 {
162     HLOGM("try search debug info at %s", symbolsFile->filePath_.c_str());
163     auto &dynInfoProcessMap = unwindInfoPtr->callStack.unwindDynInfoMap_;
164     // all the thread in same process have same mmap and symbols
165     if (dynInfoProcessMap.find(unwindInfoPtr->thread.pid_) == dynInfoProcessMap.end()) {
166         dynInfoProcessMap.emplace(unwindInfoPtr->thread.pid_, dsoUnwDynInfoMap {});
167     }
168     dsoUnwDynInfoMap &dynFileMap = dynInfoProcessMap[unwindInfoPtr->thread.pid_];
169     // find use dso name as key
170     if (dynFileMap.find(symbolsFile->filePath_) == dynFileMap.end()) {
171         unw_dyn_info_t newdi;
172         if (memset_s(&newdi, sizeof(unw_dyn_info_t), 0, sizeof(unw_dyn_info_t)) != EOK) {
173             HLOGE("memset_s() failed");
174             return -UNW_EUNSPEC;
175         }
176 #ifdef target_cpu_arm
177         // arm use .ARM.exidx , not use ehframe
178         newdi.format = UNW_INFO_FORMAT_ARM_EXIDX;
179 #else
180         // otherwise we use EH FRAME
181         newdi.format = UNW_INFO_FORMAT_REMOTE_TABLE;
182 #endif
183         if (fillUDI(newdi, *symbolsFile, mmap, unwindInfoPtr->thread)) {
184             dumpUDI(newdi);
185             // we make a option empty value first
186             std::optional<unw_dyn_info_t> &odi = dynFileMap[symbolsFile->filePath_];
187             odi = newdi;
188         } else {
189             HLOGV("fillUDI failed()");
190             return -UNW_EUNSPEC;
191         }
192     }
193 
194     HLOG_ASSERT(dynInfoProcessMap.find(unwindInfoPtr->thread.pid_) != dynInfoProcessMap.end());
195     HLOG_ASSERT_MESSAGE(dynFileMap.find(symbolsFile->filePath_) != dynFileMap.end(), "%s",
196                         symbolsFile->filePath_.c_str());
197     std::optional<unw_dyn_info_t> &odi =
198         dynInfoProcessMap.at(unwindInfoPtr->thread.pid_).at(symbolsFile->filePath_);
199 
200     if (odi.has_value()) {
201         unw_dyn_info_t &di = odi.value();
202         /*
203             we don't use dwarf_search_unwind_table
204             because in arm it will search two function:
205             1 arm_search_unwind_table first
206             2 dwarf_search_unwind_table
207 
208             see libunwind_i.h for arm
209             define tdep_search_unwind_table UNW_OBJ(search_unwind_table)
210 
211         */
212         int ret = static_cast<unw_error_t>(
213             tdep_search_unwind_table(as, ip, &di, pi, need_unwind_info, arg));
214 
215         HLOGM("search_unwind_table ret %d:%s", ret, GetUnwErrorName(ret).c_str());
216 
217         if (UNW_ESUCCESS != ret) {
218             if (UNW_ENOINFO != ret) {
219                 HLOGW("search_unwind_table ret error %d:%s", ret, GetUnwErrorName(ret).c_str());
220             }
221             return -UNW_EUNSPEC;
222         } else {
223             return UNW_ESUCCESS;
224         }
225     } else {
226         HLOGW("no debug info found for thread %d:%s", unwindInfoPtr->thread.tid_,
227               unwindInfoPtr->thread.name_.c_str());
228         return -UNW_EUNSPEC;
229     }
230 }
231 
FindProcInfo(unw_addr_space_t as,unw_word_t ip,unw_proc_info_t * pi,int need_unwind_info,void * arg)232 int CallStack::FindProcInfo(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
233                             int need_unwind_info, void *arg)
234 {
235     UnwindInfo *unwindInfoPtr = static_cast<UnwindInfo *>(arg);
236 
237     HLOGM("need_unwind_info ret %d ip %" UNW_WORD_PFLAG "", need_unwind_info, ip);
238     const MemMapItem *mmap = unwindInfoPtr->thread.FindMapByAddr(ip);
239     if (mmap != nullptr) {
240         SymbolsFile *symbolsFile = unwindInfoPtr->thread.FindSymbolsFileByMap(*mmap);
241         if (symbolsFile != nullptr) {
242             return FindUnwindTable(symbolsFile, *mmap, unwindInfoPtr, as, ip, pi, need_unwind_info,
243                                    arg);
244         } else {
245             HLOGW("no symbols file found for thread %d:%s", unwindInfoPtr->thread.tid_,
246                   unwindInfoPtr->thread.name_.c_str());
247         }
248     } else {
249         HLOGE("ip 0x%016" UNW_WORD_PFLAG " not found in thread %d:%s", ip,
250               unwindInfoPtr->thread.tid_, unwindInfoPtr->thread.name_.c_str());
251     }
252 
253     return -UNW_EUNSPEC;
254 }
255 
ReadVirtualThreadMemory(UnwindInfo & unwindInfoPtr,unw_word_t addr,unw_word_t * data)256 bool CallStack::ReadVirtualThreadMemory(UnwindInfo &unwindInfoPtr, unw_word_t addr,
257                                         unw_word_t *data)
258 {
259     auto process = unwindInfoPtr.callStack.porcessMemoryMap_.find(unwindInfoPtr.thread.pid_);
260     if (process != unwindInfoPtr.callStack.porcessMemoryMap_.end()) {
261         auto memory = process->second.find(addr);
262         if (memory != process->second.end()) {
263             *data = memory->second;
264             return true;
265         }
266     }
267 
268     if (unwindInfoPtr.thread.ReadRoMemory(addr, (uint8_t *)data, sizeof(unw_word_t))) {
269         unwindInfoPtr.callStack.porcessMemoryMap_[unwindInfoPtr.thread.pid_][addr] = *data;
270         return true;
271     } else {
272         return false;
273     }
274 }
275 
AccessMem(unw_addr_space_t as,unw_word_t addr,unw_word_t * valuePoint,int writeOperation,void * arg)276 int CallStack::AccessMem([[maybe_unused]] unw_addr_space_t as, unw_word_t addr,
277                          unw_word_t *valuePoint, int writeOperation, void *arg)
278 {
279     UnwindInfo *unwindInfoPtr = static_cast<UnwindInfo *>(arg);
280     *valuePoint = 0;
281     HLOGDUMMY("try access addr 0x%" UNW_WORD_PFLAG " ", addr);
282     HLOG_ASSERT(writeOperation == 0);
283 
284     /* Check overflow. */
285     if (addr + sizeof(unw_word_t) < addr) {
286         HLOGE("address overfolw at 0x%" UNW_WORD_PFLAG " increase 0x%zu", addr, sizeof(unw_word_t));
287         return -UNW_EUNSPEC;
288     }
289 
290     if (addr < unwindInfoPtr->callStack.stackPoint_ or
291         addr + sizeof(unw_word_t) >= unwindInfoPtr->callStack.stackEnd_) {
292         if (ReadVirtualThreadMemory(*unwindInfoPtr, addr, valuePoint)) {
293             HLOGM("access_mem addr %p get val 0x%" UNW_WORD_PFLAG ", from mmap",
294                   reinterpret_cast<void *>(addr), *valuePoint);
295         } else {
296             HLOGW("access_mem addr %p failed, from mmap, ", reinterpret_cast<void *>(addr));
297             HLOGW("stack range 0x%" PRIx64 " -  0x%" PRIx64 "(0x%" PRIx64 ")",
298                   unwindInfoPtr->callStack.stackPoint_, unwindInfoPtr->callStack.stackEnd_,
299                   unwindInfoPtr->callStack.stackEnd_ - unwindInfoPtr->callStack.stackPoint_);
300             return -UNW_EUNSPEC;
301         }
302     } else {
303         size_t stackOffset = addr - unwindInfoPtr->callStack.stackPoint_;
304         *valuePoint = *(unw_word_t *)&unwindInfoPtr->callStack.stack_[stackOffset];
305         HLOGM("access_mem addr %p val %" UNW_WORD_PFLAG ", from stack offset %zu",
306               reinterpret_cast<void *>(addr), *valuePoint, stackOffset);
307     }
308 
309     return UNW_ESUCCESS;
310 }
311 
AccessReg(unw_addr_space_t as,unw_regnum_t regnum,unw_word_t * valuePoint,int writeOperation,void * arg)312 int CallStack::AccessReg([[maybe_unused]] unw_addr_space_t as, unw_regnum_t regnum,
313                          unw_word_t *valuePoint, int writeOperation, void *arg)
314 {
315     UnwindInfo *unwindInfoPtr = static_cast<UnwindInfo *>(arg);
316     uint64_t val;
317     int perfRegIndex = LibunwindRegIdToPerfReg(regnum);
318     if (perfRegIndex < 0) {
319         HLOGE("can't read reg %d", perfRegIndex);
320         return perfRegIndex;
321     }
322     /* Don't support write, I suspect we don't need it. */
323     if (writeOperation) {
324         HLOGE("access_reg %d", regnum);
325         return -UNW_EINVAL;
326     }
327 
328     if (unwindInfoPtr->callStack.regsNum_ == 0) {
329         return -UNW_EUNSPEC;
330     }
331 
332     if (!RegisterGetValue(val, unwindInfoPtr->callStack.regs_, static_cast<size_t>(perfRegIndex),
333                           unwindInfoPtr->callStack.regsNum_)) {
334         HLOGE("can't read reg %d", perfRegIndex);
335         return -UNW_EUNSPEC;
336     }
337 
338     *valuePoint = (unw_word_t)val;
339     HLOGM("reg %d:%s, val 0x%" UNW_WORD_PFLAG "", regnum, RegisterGetName(static_cast<size_t>(perfRegIndex)).c_str(),
340           *valuePoint);
341     return UNW_ESUCCESS;
342 }
343 
PutUnwindInfo(unw_addr_space_t as,unw_proc_info_t * pi,void * arg)344 void CallStack::PutUnwindInfo([[maybe_unused]] unw_addr_space_t as,
345                               [[maybe_unused]] unw_proc_info_t *pi, [[maybe_unused]] void *arg)
346 {
347 }
348 
AccessFpreg(unw_addr_space_t as,unw_regnum_t num,unw_fpreg_t * val,int writeOperation,void * arg)349 int CallStack::AccessFpreg([[maybe_unused]] unw_addr_space_t as, [[maybe_unused]] unw_regnum_t num,
350                            [[maybe_unused]] unw_fpreg_t *val, [[maybe_unused]] int writeOperation,
351                            [[maybe_unused]] void *arg)
352 {
353     return -UNW_EINVAL;
354 }
355 
GetDynInfoListAaddr(unw_addr_space_t as,unw_word_t * dil_vaddr,void * arg)356 int CallStack::GetDynInfoListAaddr([[maybe_unused]] unw_addr_space_t as,
357                                    [[maybe_unused]] unw_word_t *dil_vaddr,
358                                    [[maybe_unused]] void *arg)
359 {
360     return -UNW_ENOINFO;
361 }
362 
Resume(unw_addr_space_t as,unw_cursor_t * cu,void * arg)363 int CallStack::Resume([[maybe_unused]] unw_addr_space_t as, [[maybe_unused]] unw_cursor_t *cu,
364                       [[maybe_unused]] void *arg)
365 {
366     return -UNW_EINVAL;
367 }
368 
getProcName(unw_addr_space_t as,unw_word_t addr,char * bufp,size_t buf_len,unw_word_t * offp,void * arg)369 int CallStack::getProcName([[maybe_unused]] unw_addr_space_t as, [[maybe_unused]] unw_word_t addr,
370                            [[maybe_unused]] char *bufp, [[maybe_unused]] size_t buf_len,
371                            [[maybe_unused]] unw_word_t *offp, [[maybe_unused]] void *arg)
372 {
373     return -UNW_EINVAL;
374 }
375 
UnwindStep(unw_cursor_t & c,std::vector<CallFrame> & callStack,size_t maxStackLevel)376 void CallStack::UnwindStep(unw_cursor_t &c, std::vector<CallFrame> &callStack, size_t maxStackLevel)
377 {
378     while (callStack.size() < maxStackLevel) {
379         int ret = unw_step(&c);
380         if (ret > 0) {
381             unw_word_t ip, 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, sp;
490     if (!GetIpSP(ip, sp, regs_, regsNum_)) {
491         HLOGW("unable get sp or sp , unable unwind");
492         return false;
493     } else {
494         if (ip != 0) {
495             HLOGV("unwind:%zu: ip 0x%" PRIx64 " sp 0x%" PRIx64 "", callStack.size(), ip, sp);
496             callStack.emplace_back(ip, sp);
497         }
498     }
499 
500     /*
501      * If we need more than one entry, do the DWARF
502      * unwind itself.
503      */
504     if (maxStackLevel - 1 > 0) {
505         return DoUnwind(thread, callStack, maxStackLevel);
506     }
507 #endif
508     return true;
509 }
510 
LogFrame(const std::string msg,const std::vector<CallFrame> & frames)511 void CallStack::LogFrame(const std::string msg, const std::vector<CallFrame> &frames)
512 {
513     HLOGM("%s", msg.c_str());
514 #ifndef NDEBUG
515     int level = 0;
516     for (auto frame : frames) {
517         HLOGM("%d:%s", level++, frame.ToString().c_str());
518     }
519 #endif
520 }
521 
522 /*
523 we should have CallStack cache for each thread
524 end                    begin
525 0. A -> B -> C -> E -> F
526 1.           C -> E -> F
527 2.      B -> C
528 3. A -> B -> C
529 4.      B -> G -> H
530 5.      J -> C
531 
532 0 is our cache
533 1 2 3... is from record
534 
535 use expandLimit to setup how may frame match is needs
536 
537 */
DoExpandCallStack(std::vector<CallFrame> & newCallFrames,const std::vector<CallFrame> & cachedCallFrames,size_t expandLimit)538 size_t CallStack::DoExpandCallStack(std::vector<CallFrame> &newCallFrames,
539                                     const std::vector<CallFrame> &cachedCallFrames,
540                                     size_t expandLimit)
541 {
542     if (expandLimit == 0 or newCallFrames.size() < expandLimit or
543         cachedCallFrames.size() < expandLimit) {
544         HLOGM("expandLimit %zu not match new %zu cache %zu", expandLimit, newCallFrames.size(),
545               cachedCallFrames.size());
546         return 0; // size not enough
547     }
548 
549     // called (Stack Buttom) , this will NOT change when compare
550     // in case1 newIt -> C
551     // in case2 newIt -> B
552     const auto newIt = newCallFrames.end() - expandLimit;
553 
554     HLOGM("try find new call chain bottom %s for limit %zu", newIt->ToString().c_str(),
555           expandLimit);
556 
557     // first frame search, from called - > caller
558     // for case 2 it should found B
559     auto cachedIt = find(cachedCallFrames.begin(), cachedCallFrames.end(), *newIt);
560     if (cachedIt == cachedCallFrames.end()) {
561         HLOGM("not found in first search");
562     }
563 
564     // cache frame found
565     while (std::distance(cachedIt, cachedCallFrames.end()) >= signed(expandLimit)) {
566         HLOG_ASSERT_MESSAGE(maxCycle++ < MAX_CALL_FRAME_EXPAND_CYCLE, "MAX_UNWIND_CYCLE = %d reach",
567                             MAX_CALL_FRAME_EXPAND_CYCLE);
568 
569         if (std::equal(newIt, newIt + expandLimit, cachedIt)) {
570             HLOGM("match %s + %zu", newIt->ToString().c_str(), expandLimit);
571             cachedIt += expandLimit; // in while we check the boundary safe
572             if (cachedIt == cachedCallFrames.end()) {
573                 // same but no more need expand
574                 break;
575             }
576 
577             // expand the frame and make some log ?
578             LogFrame("newCallStack:", newCallFrames);
579             LogFrame("cachedCallStack:", cachedCallFrames);
580 
581             newCallFrames.insert(newCallFrames.end(), cachedIt, cachedCallFrames.end());
582             auto expands = std::distance(cachedIt, cachedCallFrames.end());
583             HLOGV("merge callstack increse to %zu (+%zd) ", newCallFrames.size(), expands);
584             // we done the deal
585             return expands;
586         } else {
587             // quick search next same farme again
588             cachedIt++;
589             if (cachedIt != cachedCallFrames.end()) {
590                 HLOGM("search next");
591                 cachedIt = find(cachedIt, cachedCallFrames.end(), *newIt);
592             }
593         }
594     }
595     HLOGM("cachedIt distance %zd , need %zd", std::distance(cachedCallFrames.begin(), cachedIt),
596           distances);
597     return 0u; // nothing expand
598 }
599 
ExpandCallStack(pid_t tid,std::vector<CallFrame> & callFrames,size_t expandLimit)600 size_t CallStack::ExpandCallStack(pid_t tid, std::vector<CallFrame> &callFrames, size_t expandLimit)
601 {
602     size_t expand = 0u;
603     if (expandLimit == 0) {
604         return expand; // nothing need to do
605     } else if (callFrames.size() < expandLimit) {
606         HLOGM("new callstack is too small, skip it");
607         return expand;
608     }
609     if (!cachedCallFramesMap_.count(tid)) {
610         cachedCallFramesMap_[tid].reserve(MAX_CALL_FRAME_EXPAND_CACHE_SIZE);
611     }
612     if (callFrames.size() >= 1u) {
613         // get top  (Earliest caller)
614         HashList<uint64_t, std::vector<CallFrame>> &cachedCallFrames = cachedCallFramesMap_[tid];
615         HLOGV("find call stack frames in cache size %zu", cachedCallFrames.size());
616         // compare
617         using namespace std::rel_ops; // enable complement comparing operators
618         for (auto itr = cachedCallFrames.begin(); itr < cachedCallFrames.end(); ++itr) {
619             // each cached callstack
620             /*
621                 stack 2    1    0
622                 cache A -> B -> C
623                 new        B -> C
624                 check:
625                 1 if new B == cache C
626                 2 if new B == cache B
627                 3 if new C == new C (if limit > 0)
628                 4 insert A after B in new stack
629             */
630             const std::vector<CallFrame> &cachedCallStack = *itr;
631             if (cachedCallStack.size() < expandLimit) {
632                 HLOGM("cache callstack is too small, skip it");
633                 continue; // check next
634             }
635             expand = DoExpandCallStack(callFrames, cachedCallStack, expandLimit);
636             if (expand > 0) {
637                 break;
638             }
639         }
640         // add new one in to cache cachedCallFrames.
641         // further optimization can be done by caching pointer which avoids copying
642         // vector
643         cachedCallFrames[callFrames[0].ip_] = callFrames;
644     }
645     HLOGM("expand %zu", expand);
646     return expand;
647 }
648 
CallStack()649 CallStack::CallStack() {}
650 
~CallStack()651 CallStack::~CallStack()
652 {
653 #if HAVE_LIBUNWIND
654     for (auto &pair : unwindAddrSpaceMap_) {
655         unw_destroy_addr_space(pair.second);
656     }
657 #endif
658 }
659 } // namespace HiPerf
660 } // namespace Developtools
661 } // namespace OHOS
662