1 /**
2 * Copyright (c) 2025 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
16 #include "backtrace.h"
17 #include "libpandafile/class_data_accessor.h"
18 #include "libpandafile/code_data_accessor.h"
19 #include "libpandafile/debug_helpers.h"
20 #include "libpandafile/file.h"
21 #include "libpandafile/method_data_accessor.h"
22 #include "runtime/include/class.h"
23 #include "runtime/include/class_helper.h"
24 #include "runtime/include/stack_walker.h"
25
26 namespace ark::tooling {
27
28 // NOLINTNEXTLINE(google-build-using-namespace)
29 using namespace ark;
30 // CC-OFFNXT(readability-function-size_parameters)
StepArk(void * ctx,ReadMemFunc readMem,uintptr_t * fp,uintptr_t * sp,uintptr_t * pc,uintptr_t * bcOffset)31 int Backtrace::StepArk(void *ctx, ReadMemFunc readMem, uintptr_t *fp, uintptr_t *sp, uintptr_t *pc, uintptr_t *bcOffset)
32 {
33 if (*fp == 0) {
34 LOG(ERROR, RUNTIME) << "fp is invalid";
35 return 0;
36 }
37
38 *sp = *fp;
39 auto prevFpOffset = Frame::GetPrevFrameOffset();
40 auto ret = readMem(ctx, *sp + prevFpOffset, fp, false);
41 if (!ret) {
42 LOG(ERROR, RUNTIME) << "read prev fp addr : " << *sp + prevFpOffset << " failed ";
43 return 0;
44 }
45
46 auto pcOffset = Frame::GetInstructionsOffset();
47 ret = readMem(ctx, *sp + pcOffset, pc, false);
48 if (!ret) {
49 LOG(ERROR, RUNTIME) << "read pc addr : " << *sp + pcOffset << " failed ";
50 return 0;
51 }
52
53 auto bcOffsetOffset = Frame::GetBytecodeOffsetOffset();
54 ret = readMem(ctx, *sp + bcOffsetOffset, bcOffset, true);
55 if (!ret) {
56 LOG(ERROR, RUNTIME) << "read bcOffsetOffset addr : " << *sp + bcOffsetOffset << " failed ";
57 return 0;
58 }
59 return 1;
60 }
61
62 // CC-OFFNXT(readability-function-size_parameters, huge_depth[C++])
Symbolize(uintptr_t pc,uintptr_t mapBase,uint32_t bcOffset,uint8_t * abcData,uint64_t abcSize,Function * function)63 int Backtrace::Symbolize(uintptr_t pc, uintptr_t mapBase, uint32_t bcOffset, uint8_t *abcData, uint64_t abcSize,
64 Function *function)
65 {
66 if (function == nullptr || abcData == nullptr) {
67 LOG(ERROR, RUNTIME) << "parameter invalid!";
68 return 0;
69 }
70 uintptr_t realOffset = pc - mapBase;
71 std::unique_ptr<const panda_file::File> file = panda_file::OpenPandaFileFromMemory(abcData, abcSize);
72 ASSERT(file != nullptr);
73 uintptr_t realPc = realOffset + reinterpret_cast<uintptr_t>(file->GetBase());
74
75 auto methodInfos = ReadAllMethodInfos(file.get());
76 int32_t left = 0;
77 int32_t right = static_cast<int32_t>(methodInfos.size()) - 1;
78 for (; left <= right;) {
79 int32_t mid = (left + right) / 2;
80 bool isRight = realPc >= (methodInfos[mid].codeBegin + methodInfos[mid].codeSize);
81 bool isLeft = realPc < methodInfos[mid].codeBegin;
82 if (!isRight && !isLeft) {
83 panda_file::File::EntityId id(methodInfos[mid].methodId);
84 panda_file::MethodDataAccessor mda(*file, id);
85 panda_file::ClassDataAccessor cda(*file, mda.GetClassId());
86 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
87 int size = snprintf_s(function->functionName, FUNCTIONNAME_MAX, FUNCTIONNAME_MAX - 1, "%s.%s",
88 ClassHelper::GetName(cda.GetDescriptor()).c_str(), mda.GetName().data);
89 if (size < 0) {
90 LOG(ERROR, RUNTIME) << "copy funtionname failed!";
91 }
92 function->line = static_cast<int32_t>(panda_file::debug_helpers::GetLineNumber(mda, bcOffset, file.get()));
93 function->column = 0;
94 function->codeBegin = methodInfos[mid].codeBegin;
95 function->codeSize = methodInfos[mid].codeSize;
96 auto sourceFileId = cda.GetSourceFileId();
97 // NOLINTNEXTLINE(C_RULE_ID_FUNCTION_NESTING_LEVEL)
98 if (sourceFileId.has_value() &&
99 strcpy_s(function->url, URL_MAX,
100 reinterpret_cast<const char *>(file->GetStringData(sourceFileId.value()).data)) == EOK) {
101 return 1;
102 }
103 LOG(ERROR, RUNTIME) << "sourceFile not exist";
104 } else if (isRight) { // NOLINT(readability-else-after-return)
105 left = mid + 1;
106 } else {
107 right = mid - 1;
108 }
109 }
110 return 0;
111 }
112
ReadMethodInfo(panda_file::MethodDataAccessor & mda)113 std::optional<MethodInfo> Backtrace::ReadMethodInfo(panda_file::MethodDataAccessor &mda)
114 {
115 uintptr_t methodId = mda.GetMethodId().GetOffset();
116 auto codeId = mda.GetCodeId();
117 if (!codeId) {
118 return std::nullopt;
119 }
120 panda_file::CodeDataAccessor cda(mda.GetPandaFile(), codeId.value());
121 uint32_t codeSize = cda.GetCodeSize();
122 auto codeBegin = reinterpret_cast<uintptr_t>(cda.GetInstructions());
123 return std::make_optional<MethodInfo>(methodId, codeBegin, codeSize);
124 }
125
ReadAllMethodInfos(const panda_file::File * file)126 std::vector<MethodInfo> Backtrace::ReadAllMethodInfos(const panda_file::File *file)
127 {
128 std::vector<MethodInfo> result;
129 Span<const uint32_t> classIndexes = file->GetClasses();
130 for (const uint32_t index : classIndexes) {
131 panda_file::File::EntityId classId(index);
132 if (file->IsExternal(classId)) {
133 continue;
134 }
135 panda_file::ClassDataAccessor cda(*file, classId);
136 cda.EnumerateMethods([&result](panda_file::MethodDataAccessor &mda) {
137 auto info = ReadMethodInfo(mda);
138 if (!info) {
139 return;
140 }
141 result.push_back(info.value());
142 });
143 }
144
145 std::sort(result.begin(), result.end());
146 return result;
147 }
148 } // namespace ark::tooling
149