From c046cfd089aeb266680babb252fc10345613b54d Mon Sep 17 00:00:00 2001 From: cs1111 Date: Fri, 16 May 2025 20:26:57 +0800 Subject: [PATCH] feature:adapt jsvm v8 unwind stack Signed-off-by: cs1111 Change-Id: I5fe9209bc7bb1eae6bf1352258dfb3cb3d191c07 --- interfaces/innerkits/unwinder/BUILD.gn | 1 + .../innerkits/unwinder/include/dfx_jsvm.h | 123 ++++++++++++++ .../innerkits/unwinder/include/dfx_map.h | 1 + .../innerkits/unwinder/include/unwinder.h | 1 + .../innerkits/unwinder/src/jsvm/dfx_jsvm.cpp | 153 ++++++++++++++++++ .../innerkits/unwinder/src/maps/dfx_map.cpp | 30 +++- interfaces/innerkits/unwinder/unwinder.cpp | 39 +++++ 7 files changed, 346 insertions(+), 2 deletions(-) create mode 100644 interfaces/innerkits/unwinder/include/dfx_jsvm.h create mode 100644 interfaces/innerkits/unwinder/src/jsvm/dfx_jsvm.cpp diff --git a/interfaces/innerkits/unwinder/BUILD.gn b/interfaces/innerkits/unwinder/BUILD.gn index dae94de74..e9d5f87fb 100644 --- a/interfaces/innerkits/unwinder/BUILD.gn +++ b/interfaces/innerkits/unwinder/BUILD.gn @@ -33,6 +33,7 @@ dfx_unwinder_sources = [ "src/registers/dfx_regs_riscv64.cpp", "src/registers/dfx_regs_x86_64.cpp", "src/registers/getcontext_x86_64.S", + "src/jsvm/dfx_jsvm.cpp", "src/unwind_local/thread_context.cpp", "src/utils/dfx_config.cpp", "src/utils/dfx_frame_formatter.cpp", diff --git a/interfaces/innerkits/unwinder/include/dfx_jsvm.h b/interfaces/innerkits/unwinder/include/dfx_jsvm.h new file mode 100644 index 000000000..bed281a95 --- /dev/null +++ b/interfaces/innerkits/unwinder/include/dfx_jsvm.h @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DFX_JSVM_H +#define DFX_JSVM_H + +#include +#include +#include +#include +#include + +namespace jsvm { +constexpr uint16_t FUNCTIONNAME_MAX = 1024; + +using ReadMemFunc = bool (*)(void *, uintptr_t, uintptr_t *); + +struct JsvmFunction { + char functionName[FUNCTIONNAME_MAX]; +}; + +struct JsvmStepParam { + uintptr_t *fp; + uintptr_t *sp; + uintptr_t *pc; + bool *isJsvmFrame; + void* jsvmObj; + + JsvmStepParam(uintptr_t *fp, uintptr_t *sp, uintptr_t *pc, bool *isJsvmFrame, void* jsvmObj) + : fp(fp), sp(sp), pc(pc), isJsvmFrame(isJsvmFrame), jsvmObj(jsvmObj) {} +}; +} + +namespace OHOS { +namespace HiviewDFX { +using JsvmFunction = jsvm::JsvmFunction; +using ReadMemFunc = jsvm::ReadMemFunc; +using JsvmStepParam = jsvm::JsvmStepParam; + +class DfxJsvm { +public: + static DfxJsvm& Instance(); + + void InitJsvmObj(uintptr_t mapStart, void* jsvmObj); + /** + * @brief step Jsvm frame + * + * @param obj memory pointer object + * @param readMemFn read memory function + * @param fp fp register + * @param sp sp register + * @param pc pc register + * @param isJsFrame isJsFrame variable + * @return if succeed return 1, otherwise return -1 + */ + int StepJsvmFrame(void *obj, ReadMemFunc readMemFn, + JsvmStepParam* JsvmParam); + /** + * @brief Parse Jsvm Frame Info, for hiperf/hiProfile + * + * @param byteCodePc byteCode Pc + * @param mapBase map base address + * @param loadOffset map offset + * @param data abc data + * @param dataSize abc data size + * @param extractorPtr extractorPtr from JsvmCreateJsSymbolExtractor + * @param JsvmFunction jsvmFunction variable + * @return if succeed return 1, otherwise return -1 + */ + int ParseJsvmFrameInfo(uintptr_t pc, uintptr_t extractorPtr, JsvmFunction *jsvmFunction); + + /** + * @brief create Jsvm js symbol extracrot + * + * @param extractorPtr extractorPtr variable + * @return if succeed return 1, otherwise return -1 + */ + int JsvmCreateJsSymbolExtractor(uintptr_t* extractorPtr, uint32_t pid); + /** + * @brief destory Jsvm js symbol extracrot + * + * @param extractorPtr extractorPtr from JsvmCreateJsSymbolExtractor + * @return if succeed return 1, otherwise return -1 + */ + int JsvmDestoryJsSymbolExtractor(uintptr_t extractorPtr); + +private: + DfxJsvm() = default; + ~DfxJsvm() = default; + DfxJsvm(const DfxJsvm&) = delete; + DfxJsvm& operator=(const DfxJsvm&) = delete; + bool GetLibJsvmHandle(void); + template + void DlsymJsvmFunc(const char* funcName, FuncName& dlsymFuncName); + void* handle_ = nullptr; + using InitJsvmObjFn = void (*)(uintptr_t, void*); + using StepJsvmFn = int (*)(void*, OHOS::HiviewDFX::ReadMemFunc, OHOS::HiviewDFX::JsvmStepParam*); + using ParseJsvmFrameInfoFn = int (*)(uintptr_t, uintptr_t, JsvmFunction*); + using JsvmCreateJsSymbolExtractorFn = int (*)(uintptr_t*); + using JsvmDestoryJsSymbolExtractorFn = int (*)(uintptr_t); + std::mutex mutex_; + InitJsvmObjFn initJsvmObjFn_ = nullptr; + StepJsvmFn stepJsvmFn_ = nullptr; + ParseJsvmFrameInfoFn parseJsvmFrameInfoFn_ = nullptr; + JsvmCreateJsSymbolExtractorFn jsvmCreateJsSymbolExtractorFn_ = nullptr; + JsvmDestoryJsSymbolExtractorFn jsvmDestoryJsSymbolExtractorFn_ = nullptr; +}; +} // namespace HiviewDFX +} // namespace OHOS + +#endif diff --git a/interfaces/innerkits/unwinder/include/dfx_map.h b/interfaces/innerkits/unwinder/include/dfx_map.h index 30a337206..ae8735a80 100644 --- a/interfaces/innerkits/unwinder/include/dfx_map.h +++ b/interfaces/innerkits/unwinder/include/dfx_map.h @@ -45,6 +45,7 @@ public: bool Parse(char* buf, size_t size); bool IsMapExec(); bool IsArkExecutable(); + bool IsJsvmExecutable(); bool IsVdsoMap(); const std::shared_ptr GetHap(); const std::shared_ptr GetElf(pid_t pid = 0); diff --git a/interfaces/innerkits/unwinder/include/unwinder.h b/interfaces/innerkits/unwinder/include/unwinder.h index 12e49c538..6d165fab6 100644 --- a/interfaces/innerkits/unwinder/include/unwinder.h +++ b/interfaces/innerkits/unwinder/include/unwinder.h @@ -47,6 +47,7 @@ public: void EnableFpCheckMapExec(bool enableFpCheckMapExec); void EnableFillFrames(bool enableFillFrames); + void EnableJsvmstack(bool enableJsvmstack, uintptr_t jsvmMapStart); void IgnoreMixstack(bool ignoreMixstack); void SetRegs(std::shared_ptr regs); diff --git a/interfaces/innerkits/unwinder/src/jsvm/dfx_jsvm.cpp b/interfaces/innerkits/unwinder/src/jsvm/dfx_jsvm.cpp new file mode 100644 index 000000000..0a62febbd --- /dev/null +++ b/interfaces/innerkits/unwinder/src/jsvm/dfx_jsvm.cpp @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2023-2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "dfx_jsvm.h" + +#include +#include +#include +#include +#include + +#include "dfx_define.h" +#include "dfx_log.h" +#include "string_util.h" + +namespace OHOS { +namespace HiviewDFX { +namespace { +#undef LOG_DOMAIN +#undef LOG_TAG +#define LOG_DOMAIN 0xD002D11 +#define LOG_TAG "DfxJsvm" + +const char* const JSVM_LIB_NAME = "libjsvm.so"; +} + +bool DfxJsvm::GetLibJsvmHandle() +{ + if (handle_ != nullptr) { + return true; + } + handle_ = dlopen(JSVM_LIB_NAME, RTLD_LAZY); + if (handle_ == nullptr) { + DFXLOGU("Failed to load library(%{public}s).", dlerror()); + return false; + } + return true; +} + +DfxJsvm& DfxJsvm::Instance() +{ + static DfxJsvm instance; + return instance; +} + +void DfxJsvm::InitJsvmObj(uintptr_t mapStart, void* jsvmObj) +{ + if (initJsvmObjFn_ != nullptr) { + initJsvmObjFn_(mapStart, jsvmObj); + } + + const char* jsvmFuncName = "init_jsvm_obj"; + DlsymJsvmFunc(jsvmFuncName, initJsvmObjFn_); + + if (initJsvmObjFn_ != nullptr) { + initJsvmObjFn_(mapStart, jsvmObj); + } + +} + +template +void DfxJsvm::DlsymJsvmFunc(const char* funcName, FuncName& dlsymFuncName) +{ + std::unique_lock lock(mutex_); + do { + if (dlsymFuncName != nullptr) { + break; + } + if (!GetLibJsvmHandle()) { + break; + } + (dlsymFuncName) = reinterpret_cast(dlsym(handle_, (funcName))); + if (dlsymFuncName == nullptr) { + DFXLOGE("Failed to dlsym(%{public}s), error: %{public}s", funcName, dlerror()); + break; + } + } while (false); +} + +int DfxJsvm::JsvmCreateJsSymbolExtractor(uintptr_t* extractorPtr, uint32_t pid) +{ + if (jsvmCreateJsSymbolExtractorFn_ != nullptr) { + return jsvmCreateJsSymbolExtractorFn_(extractorPtr); + } + + const char* jsvmFuncName = "create_jsvm_extractor"; + DlsymJsvmFunc(jsvmFuncName, jsvmCreateJsSymbolExtractorFn_); + + if (jsvmCreateJsSymbolExtractorFn_ != nullptr) { + return jsvmCreateJsSymbolExtractorFn_(extractorPtr); + } + return -1; +} + +int DfxJsvm::JsvmDestoryJsSymbolExtractor(uintptr_t extractorPtr) +{ + if (jsvmDestoryJsSymbolExtractorFn_ != nullptr) { + return jsvmDestoryJsSymbolExtractorFn_(extractorPtr); + } + + const char* jsvmFuncName = "destory_jsvm_extractor"; + DlsymJsvmFunc(jsvmFuncName, jsvmDestoryJsSymbolExtractorFn_); + + if (jsvmDestoryJsSymbolExtractorFn_ != nullptr) { + return jsvmDestoryJsSymbolExtractorFn_(extractorPtr); + } + return -1; +} + +int DfxJsvm::ParseJsvmFrameInfo(uintptr_t pc, uintptr_t extractorPtr, JsvmFunction *jsvmFunction) +{ + if (parseJsvmFrameInfoFn_ != nullptr) { + return parseJsvmFrameInfoFn_(pc, extractorPtr, jsvmFunction); + } + + const char* jsvmFuncName = "jsvm_parse_js_frame_info"; + DlsymJsvmFunc(jsvmFuncName, parseJsvmFrameInfoFn_); + + if (parseJsvmFrameInfoFn_ != nullptr) { + return parseJsvmFrameInfoFn_(pc, extractorPtr, jsvmFunction); + } + return -1; +} + +int DfxJsvm::StepJsvmFrame(void *obj, OHOS::HiviewDFX::ReadMemFunc readMemFn, + OHOS::HiviewDFX::JsvmStepParam* arkParam) +{ + if (stepJsvmFn_ != nullptr) { + return stepJsvmFn_(obj, readMemFn, arkParam); + } + + const char* jsvmFuncName = "step_jsvm"; + DlsymJsvmFunc(jsvmFuncName, stepJsvmFn_); + + if (stepJsvmFn_ != nullptr) { + return stepJsvmFn_(obj, readMemFn, arkParam); + } + return -1; +} +} // namespace HiviewDFX +} // namespace OHOS diff --git a/interfaces/innerkits/unwinder/src/maps/dfx_map.cpp b/interfaces/innerkits/unwinder/src/maps/dfx_map.cpp index f27369f04..68701a55c 100644 --- a/interfaces/innerkits/unwinder/src/maps/dfx_map.cpp +++ b/interfaces/innerkits/unwinder/src/maps/dfx_map.cpp @@ -270,8 +270,13 @@ bool DfxMap::IsArkExecutable() if (name.length() == 0) { return false; } - - if ((!StartsWith(name, "[anon:ArkTS Code")) && (!StartsWith(name, "/dev/zero")) && (!EndsWith(name, "stub.an"))) { + const char* const arkMapTable[] = { + "[anon:ArkTs Code", "/dev/zero", "stub.an" + }; + auto iter = std::find_if(std::begin(arkMapTable), std::end(arkMapTable), [this](const char* arkName) { + return StartsWith(this->name, arkName) || EndsWith(this->name, arkName); + }); + if (iter == std::end(arkMapTable)) { return false; } @@ -283,6 +288,27 @@ bool DfxMap::IsArkExecutable() return true; } +bool DfxMap::IsJsvmExecutable() +{ + if (name.length() == 0) { + return false; + } + const char* const jsvmMapTable[] = { "libv8_shared.so" }; + auto iter = std::find_if(std::begin(jsvmMapTable), std::end(jsvmMapTable), [this](const char* jsvmName) { + return EndsWith(this->name, jsvmName); + }); + if (iter == std::end(jsvmMapTable)) { + return false; + } + + if (!IsMapExec()) { + DFXLOGU("Current jsvm map(%{public}s) is not exec", name.c_str()); + return false; + } + DFXLOGU("Current jsvm map: %{public}s", name.c_str()); + return true; +} + bool DfxMap::IsVdsoMap() { if ((name == "[shmm]" || name == "[vdso]") && IsMapExec()) { diff --git a/interfaces/innerkits/unwinder/unwinder.cpp b/interfaces/innerkits/unwinder/unwinder.cpp index 65ae00269..80b33de43 100644 --- a/interfaces/innerkits/unwinder/unwinder.cpp +++ b/interfaces/innerkits/unwinder/unwinder.cpp @@ -25,6 +25,7 @@ #include "dfx_frame_formatter.h" #include "dfx_hap.h" #include "dfx_instructions.h" +#include "dfx_jsvm.h" #include "dfx_log.h" #include "dfx_memory.h" #include "dfx_param.h" @@ -130,6 +131,11 @@ public: { enableFillFrames_ = enableFillFrames; } + inline void EnableJsvmstack(bool enableJsvmstack, uintptr_t jsvmMapStart) + { + enableJsvmstack_ = enableJsvmstack; + DfxJsvm::Instance().InitJsvmObj(jsvmMapStart, jsvmObj); + } inline void IgnoreMixstack(bool ignoreMixstack) { ignoreMixstack_ = ignoreMixstack; @@ -212,6 +218,7 @@ private: uintptr_t sp = 0; uintptr_t fp = 0; bool isJsFrame {false}; + bool isJsvmFrame {false}; }; struct StepCache { std::shared_ptr map = nullptr; @@ -243,6 +250,7 @@ private: #if defined(ENABLE_MIXSTACK) bool StepArkJsFrame(StepFrame& frame); #endif + bool StepJsvmFrame(StepFrame& frame, const std::shared_ptr& map); inline void SetLocalStackCheck(void* ctx, bool check) const { if ((pid_ == UNWIND_TYPE_LOCAL) && (ctx != nullptr)) { @@ -262,6 +270,7 @@ private: bool isFpStep_ = false; bool isArkCreateLocal_ = false; bool isResetFrames_ = false; + bool enableJsvmstack_ = false; MAYBE_UNUSED bool enableMixstack_ = true; MAYBE_UNUSED bool ignoreMixstack_ = false; MAYBE_UNUSED bool stopWhenArkFrame_ = false; @@ -274,6 +283,7 @@ private: std::unordered_map stepCache_ {}; std::shared_ptr regs_ = nullptr; std::shared_ptr maps_ = nullptr; + void* jsvmObj = nullptr; std::vector pcs_ {}; std::vector frames_ {}; UnwindErrorData lastErrorData_ {}; @@ -316,6 +326,11 @@ void Unwinder::EnableFillFrames(bool enableFillFrames) impl_->EnableFillFrames(enableFillFrames); } +void Unwinder::EnableJsvmstack(bool enableJsvmstack, uintptr_t jsvmMapStart) +{ + impl_->EnableJsvmstack(enableJsvmstack, jsvmMapStart); +} + void Unwinder::IgnoreMixstack(bool ignoreMixstack) { impl_->IgnoreMixstack(ignoreMixstack); @@ -725,6 +740,27 @@ bool Unwinder::Impl::StepArkJsFrame(StepFrame& frame) } #endif +bool Unwinder::Impl::StepJsvmFrame(StepFrame& frame, const std::shared_ptr& map) +{ + if (enableJsvmstack_ && ((map != nullptr && map->IsJsvmExecutable()) || frame.isJsvmFrame)) { + DFX_TRACE_SCOPED_DLSYM("StepJsvmFrame pc: %p", reinterpret_cast(frame.pc)); + std::string timeLimitCheck; + timeLimitCheck += "StepJsvmFrame, ark pc: " + std::to_string(frame.pc) + + ", fp:" + std::to_string(frame.fp) + ", sp:" + std::to_string(frame.sp) + + ", isJsvmFrame:" + std::to_string(frame.isJsvmFrame); + ElapsedTime counter(timeLimitCheck, 20); // 20 : limit cost time 20 ms + JsvmStepParam jsvmParam(&frame.fp, &frame.sp, &frame.pc, &frame.isJsvmFrame, jsvmObj); + if (DfxJsvm::Instance().StepJsvmFrame(memory_.get(), &(Unwinder::AccessMem), &jsvmParam) < 0) { + DFXLOGE("Failed to step ark frame"); + return false; + } + regs_->SetPc(StripPac(frame.pc, pacMask_)); + regs_->SetSp(frame.sp); + regs_->SetFp(frame.fp); + } + return true; +} + bool Unwinder::Impl::UnwindFrame(void *ctx, StepFrame& frame, bool& needAdjustPc) { frame.pc = regs_->GetPc(); @@ -1074,6 +1110,9 @@ bool Unwinder::Impl::StepInner(const bool isSigFrame, StepFrame& frame, void *ct return true; } bool stopUnwind = false; + if (!StepJsvmFrame(frame, map)) { + break; + } bool processFrameResult = UnwindArkFrame(frame, map, stopUnwind); if (stopUnwind) { return processFrameResult; -- Gitee