• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
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 "stack_builder.h"
17 
18 namespace OHOS::Developtools::NativeDaemon {
19 static thread_local std::vector<std::string> g_fpJsCallStacks;
20 static thread_local std::vector<u64> g_u64regs;
21 const std::string JS_CALL_STACK_DEPTH_SEP = ",";   // ',' is js call stack depth separator
22 const std::string JS_SYMBOL_FILEPATH_SEP = "|";    // '|' is js symbol and filepath separator
23 
FillStatsVirtualFrames(std::vector<CallFrame> & callFrames,HookRecordPtr hookRecord)24 void StackBuilder::FillStatsVirtualFrames(std::vector<CallFrame>& callFrames, HookRecordPtr hookRecord)
25 {
26     auto rawStack = hookRecord->GetRawStack();
27     callFrames.emplace_back(rawStack->stackContext->mallocSize | SIZE_MASK);
28 }
29 
FpStackBuilder(NativeHookConfig * config,std::shared_ptr<VirtualRuntime> runtime,std::mutex & mtx)30 FpStackBuilder::FpStackBuilder(NativeHookConfig* config, std::shared_ptr<VirtualRuntime> runtime,
31                                std::mutex& mtx) : StackBuilder(config, runtime, mtx)
32 {
33     if (hookConfig_->fp_unwind() && hookConfig_->js_stack_report() > 0) {
34         g_fpJsCallStacks.reserve(hookConfig_->max_js_stack_depth());
35     }
36 }
37 
PrepareUnwind(HookRecordPtr hookRecord)38 void FpStackBuilder::PrepareUnwind(HookRecordPtr hookRecord)
39 {
40     auto rawData = hookRecord->GetRawStack();
41     if (runtimeInstance_ != nullptr) {
42         std::lock_guard<std::mutex> guard(mtx_);
43         runtimeInstance_->UpdateThread(rawData->stackContext->pid, rawData->stackContext->tid);
44     }
45 }
46 
FillIps(std::vector<CallFrame> & callFrames,HookRecordPtr hookRecord)47 void FpStackBuilder::FillIps(std::vector<CallFrame>& callFrames, HookRecordPtr hookRecord)
48 {
49     auto rawData = hookRecord->GetRawStack();
50     uint64_t* fpIp = reinterpret_cast<uint64_t *>(rawData->data);
51     uint8_t depth = rawData->fpDepth;
52     for (uint8_t idx = 0; idx < depth; ++idx) {
53         if (fpIp[idx] == 0) {
54             break;
55         }
56         callFrames.emplace_back(StripPac(fpIp[idx], 0));
57     }
58 }
59 
FillJsFrame(CallFrame & jsCallFrame)60 void FpStackBuilder::FillJsFrame(CallFrame& jsCallFrame)
61 {
62     DfxSymbol symbol;
63     if (!runtimeInstance_->ArktsGetSymbolCache(jsCallFrame, symbol)) {
64         symbol.filePathId_ = runtimeInstance_->FillArkTsFilePath(jsCallFrame.filePath_);
65         symbol.symbolName_ = jsCallFrame.symbolName_;
66         symbol.module_ = jsCallFrame.filePath_;
67         symbol.symbolId_ = runtimeInstance_->GetJsSymbolCacheSize();
68         runtimeInstance_->FillSymbolNameId(jsCallFrame, symbol);
69         runtimeInstance_->FillFileSet(jsCallFrame, symbol);
70         jsCallFrame.needReport_ |= CALL_FRAME_REPORT;
71         runtimeInstance_->FillJsSymbolCache(jsCallFrame, symbol);
72     }
73     jsCallFrame.callFrameId_ = symbol.symbolId_;
74     jsCallFrame.symbolNameId_ = symbol.symbolNameId_;
75     jsCallFrame.filePathId_ = symbol.filePathId_;
76     jsCallFrame.filePath_ = symbol.module_;
77     jsCallFrame.symbolName_ = symbol.symbolName_;
78 }
79 
FillJsSymbols(std::vector<CallFrame> & callFrames,HookRecordPtr hookRecord)80 void FpStackBuilder::FillJsSymbols(std::vector<CallFrame>& callFrames, HookRecordPtr hookRecord)
81 {
82     auto rawData = hookRecord->GetRawStack();
83     if (!(rawData->stackContext->jsChainId > 0 && rawData->jsStackData)) {
84         return;
85     }
86     if (hookConfig_->statistics_interval() > 0) {
87         uint16_t type = hookRecord->GetType();
88         if (type == FREE_MSG || type == MUNMAP_MSG || type == MEMORY_UNUSING_MSG) {
89             return;
90         }
91     }
92     g_fpJsCallStacks.clear();
93     if (g_fpJsCallStacks.capacity() == 0) {
94         g_fpJsCallStacks.reserve(hookConfig_->max_js_stack_depth());
95     }
96     AdvancedSplitString(rawData->jsStackData, JS_CALL_STACK_DEPTH_SEP, g_fpJsCallStacks);
97     std::lock_guard<std::mutex> guard(mtx_);
98     for (std::string& jsCallStack: g_fpJsCallStacks) {
99         std::string::size_type jsSymbolFilePathSepPos = jsCallStack.find_first_of(JS_SYMBOL_FILEPATH_SEP);
100         if (jsSymbolFilePathSepPos == std::string::npos) {
101             PROFILER_LOG_ERROR(LOG_CORE, "%s: jsCallStack find FAILED!", __func__);
102             continue;
103         }
104         std::string::size_type jsFilePathPos = jsSymbolFilePathSepPos + 1;
105         jsCallStack[jsSymbolFilePathSepPos] = '\0'; // "ts_malloc1'\0'entry/src/main/ets/pages/Index.ets:5:5"
106         CallFrame& jsCallFrame = callFrames.emplace_back(0, 0, true);
107         jsCallFrame.symbolName_ = StringViewMemoryHold::GetInstance().HoldStringView(jsCallStack.c_str());
108         jsCallFrame.filePath_ = StringViewMemoryHold::GetInstance().HoldStringView(jsCallStack.c_str() + jsFilePathPos);
109         if (hookConfig_->offline_symbolization()) {
110             FillJsFrame(jsCallFrame);
111         } else {
112             runtimeInstance_->GetSymbolName(rawData->stackContext->pid, rawData->stackContext->tid,
113                                             callFrames, 0, true, SymbolType::JS_SYMBOL);
114         }
115     }
116 }
117 
FillNativeSymbols(std::vector<CallFrame> & callFrames,HookRecordPtr hookRecord)118 void FpStackBuilder::FillNativeSymbols(std::vector<CallFrame>& callFrames, HookRecordPtr hookRecord)
119 {
120     auto rawData = hookRecord->GetRawStack();
121     std::lock_guard<std::mutex> guard(mtx_);
122     runtimeInstance_->GetSymbolName(rawData->stackContext->pid, rawData->stackContext->tid,
123                                     callFrames, 0, true, SymbolType::NATIVE_SYMBOL);
124 }
125 
IsRecordInfoValid(HookRecordPtr hookRecord)126 bool FpStackBuilder::IsRecordInfoValid(HookRecordPtr hookRecord)
127 {
128     return hookRecord->IsValid();
129 }
130 
DwarfStackBuilder(NativeHookConfig * config,std::shared_ptr<VirtualRuntime> runtime,std::mutex & mtx)131 DwarfStackBuilder::DwarfStackBuilder(NativeHookConfig* config, std::shared_ptr<VirtualRuntime> runtime,
132                                      std::mutex& mtx) : StackBuilder(config, runtime, mtx)
133 {
134     minStackDepth_ = std::min(hookConfig_->max_stack_depth(), MIN_STACK_DEPTH);
135     if (hookConfig_->blocked()) {
136         minStackDepth_ = static_cast<size_t>(hookConfig_->max_stack_depth());
137     }
138     minStackDepth_ += FILTER_STACK_DEPTH;
139     stackDepth_ = (static_cast<size_t>(hookConfig_->max_stack_depth()) > MAX_CALL_FRAME_UNWIND_SIZE)
140             ? MAX_CALL_FRAME_UNWIND_SIZE
141             : static_cast<size_t>(hookConfig_->max_stack_depth()) + FILTER_STACK_DEPTH;
142 }
143 
FillIps(std::vector<CallFrame> & callFrames,HookRecordPtr hookRecord)144 void DwarfStackBuilder::FillIps(std::vector<CallFrame>& callFrames, HookRecordPtr hookRecord)
145 {
146     auto rawData = hookRecord->GetRawStack();
147 #if defined(__arm__)
148     if (g_u64regs.size() == 0) {
149         g_u64regs.resize(PERF_REG_ARM_MAX);
150     }
151     uint32_t *regAddrArm = reinterpret_cast<uint32_t *>(rawData->data);
152     g_u64regs.assign(regAddrArm, regAddrArm + PERF_REG_ARM_MAX);
153 #else
154     if (g_u64regs.size() == 0) {
155         g_u64regs.resize(PERF_REG_ARM64_MAX);
156     }
157     if (memcpy_s(g_u64regs.data(), sizeof(uint64_t) * PERF_REG_ARM64_MAX, rawData->data,
158         sizeof(uint64_t) * PERF_REG_ARM64_MAX) != EOK) {
159         PROFILER_LOG_ERROR(LOG_CORE, "memcpy_s regs failed");
160         return;
161     }
162 #endif
163     const size_t unwindDepth = rawData->reduceStackFlag ? minStackDepth_ : stackDepth_;
164     std::lock_guard<std::mutex> guard(mtx_);
165     runtimeInstance_->UnwindStack(g_u64regs, rawData->stackData, rawData->stackSize,
166                                   rawData->stackContext->pid, rawData->stackContext->tid, callFrames, unwindDepth);
167 }
168 
FillJsSymbols(std::vector<CallFrame> & callFrames,HookRecordPtr hookRecord)169 void DwarfStackBuilder::FillJsSymbols(std::vector<CallFrame>& callFrames, HookRecordPtr hookRecord)
170 {
171     if (callFrames.size() <= FILTER_STACK_DEPTH) {
172         return;
173     }
174     auto rawData = hookRecord->GetRawStack();
175     std::lock_guard<std::mutex> guard(mtx_);
176     runtimeInstance_->GetSymbolName(rawData->stackContext->pid, rawData->stackContext->tid, callFrames,
177                                     FILTER_STACK_DEPTH, true, SymbolType::JS_SYMBOL);
178 }
179 
FillNativeSymbols(std::vector<CallFrame> & callFrames,HookRecordPtr hookRecord)180 void DwarfStackBuilder::FillNativeSymbols(std::vector<CallFrame>& callFrames, HookRecordPtr hookRecord)
181 {
182     auto rawData = hookRecord->GetRawStack();
183     if (callFrames.size() <= FILTER_STACK_DEPTH) {
184         return;
185     }
186     std::lock_guard<std::mutex> guard(mtx_);
187     runtimeInstance_->GetSymbolName(rawData->stackContext->pid, rawData->stackContext->tid, callFrames,
188                                     FILTER_STACK_DEPTH, true, SymbolType::NATIVE_SYMBOL);
189 }
190 
IsRecordInfoValid(HookRecordPtr hookRecord)191 bool DwarfStackBuilder::IsRecordInfoValid(HookRecordPtr hookRecord)
192 {
193     auto rawData = hookRecord->GetRawStack();
194     return ((hookRecord->IsValid()) && (rawData->stackSize != 0));
195 }
196 
ReplaceErrStack(std::vector<CallFrame> & callFrames)197 void DwarfStackBuilder::ReplaceErrStack(std::vector<CallFrame>& callFrames)
198 {
199     CallFrame& jsCallFrame = callFrames.emplace_back(0);
200     jsCallFrame.symbolName_ = "UnwindErrorDwarf";
201     jsCallFrame.isJsFrame_ = true;
202     jsCallFrame.needReport_ |= (CALL_FRAME_REPORT | SYMBOL_NAME_ID_REPORT | FILE_PATH_ID_REPORT);
203     jsCallFrame.callFrameId_ = DWARF_ERROR_ID;
204     jsCallFrame.symbolNameId_ = DWARF_ERROR_ID;
205     jsCallFrame.filePathId_ = DWARF_ERROR_ID;
206     jsCallFrame.filePath_ = "no-file-path";
207 }
208 
BuildStackDirector(NativeHookConfig * config,std::shared_ptr<VirtualRuntime> runtime,std::mutex & mtx)209 BuildStackDirector::BuildStackDirector(NativeHookConfig* config, std::shared_ptr<VirtualRuntime> runtime,
210                                        std::mutex& mtx) : hookConfig_(config)
211 {
212     if (hookConfig_->fp_unwind()) {
213         builder_ = std::make_shared<FpStackBuilder>(hookConfig_, runtime, mtx);
214     } else {
215         builder_ = std::make_shared<DwarfStackBuilder>(hookConfig_, runtime, mtx);
216     }
217 }
218 
ConstructCallFrames(HookRecordPtr hookRecord)219 std::vector<CallFrame>& BuildStackDirector::ConstructCallFrames(HookRecordPtr hookRecord)
220 {
221     callFrames_.clear();
222     if (builder_ == nullptr || hookConfig_ == nullptr) {
223         return callFrames_;
224     }
225     if (callFrames_.capacity() == 0) {
226         callFrames_.reserve(hookConfig_->max_stack_depth() + hookConfig_->max_js_stack_depth());
227     }
228     if (!IsRecordUnwindable(hookRecord)) {
229         return callFrames_;
230     }
231     if (!builder_->IsRecordInfoValid(hookRecord)) {
232         builder_->ReplaceErrStack(callFrames_);
233         return callFrames_;
234     }
235     builder_->PrepareUnwind(hookRecord);
236     builder_->FillIps(callFrames_, hookRecord);
237     if (hookConfig_->js_stack_report()) {
238         builder_->FillJsSymbols(callFrames_, hookRecord);
239     }
240     if (!hookConfig_->offline_symbolization()) {
241         builder_->FillNativeSymbols(callFrames_, hookRecord);
242     }
243     return callFrames_;
244 }
245 
IsRecordUnwindable(HookRecordPtr hookRecord)246 bool BuildStackDirector::IsRecordUnwindable(HookRecordPtr hookRecord)
247 {
248     return (hookRecord->GetType() != PR_SET_VMA_MSG);
249 }
250 }