From 507096fd283f58d56cabfd07c350e5e46b80869d Mon Sep 17 00:00:00 2001 From: shaoyijiang Date: Wed, 7 Feb 2024 03:00:56 +0000 Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E6=8C=81hiperf=20ark=E5=B8=A7?= =?UTF-8?q?=E8=A7=A3=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit issue: https://gitee.com/openharmony/arkcompiler_ets_runtime/issues/I8VCJK Signed-off-by: shaoyijiang Change-Id: Ic0fa1ecdc04d7e17ed40d720c67e53937458f742 --- BUILD.gn | 2 +- ecmascript/dfx/stackinfo/js_stackinfo.cpp | 270 +++++++++++++++++++- ecmascript/dfx/stackinfo/js_stackinfo.h | 48 +++- ecmascript/extractortool/src/source_map.cpp | 11 + ecmascript/extractortool/src/source_map.h | 6 +- libark_jsruntime.map | 3 + 6 files changed, 322 insertions(+), 18 deletions(-) diff --git a/BUILD.gn b/BUILD.gn index dc89d6f7ec..97f3294ecb 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -656,6 +656,7 @@ ecma_source = [ "ecmascript/ecma_vm.cpp", "ecmascript/element_accessor.cpp", "ecmascript/elements.cpp", + "ecmascript/extractortool/src/source_map.cpp", "ecmascript/frames.cpp", "ecmascript/free_object.cpp", "ecmascript/generator_helper.cpp", @@ -900,7 +901,6 @@ ecma_stackinfo_source = [ "ecmascript/extractortool/src/extractor.cpp", "ecmascript/extractortool/src/file_mapper.cpp", "ecmascript/extractortool/src/file_path_utils.cpp", - "ecmascript/extractortool/src/source_map.cpp", "ecmascript/extractortool/src/zip_file_reader_io.cpp", "ecmascript/extractortool/src/zip_file_reader_mem.cpp", "ecmascript/extractortool/src/zip_file_reader.cpp", diff --git a/ecmascript/dfx/stackinfo/js_stackinfo.cpp b/ecmascript/dfx/stackinfo/js_stackinfo.cpp index a82ca1eb53..eb77797080 100644 --- a/ecmascript/dfx/stackinfo/js_stackinfo.cpp +++ b/ecmascript/dfx/stackinfo/js_stackinfo.cpp @@ -16,6 +16,7 @@ #include "ecmascript/dfx/stackinfo/js_stackinfo.h" #include "ecmascript/base/builtins_base.h" #include "ecmascript/ecma_vm.h" +#include "ecmascript/extractortool/src/source_map.h" #include "ecmascript/interpreter/frame_handler.h" #include "ecmascript/interpreter/interpreter.h" #include "ecmascript/jspandafile/js_pandafile_manager.h" @@ -25,7 +26,6 @@ #include "ecmascript/platform/os.h" #if defined(PANDA_TARGET_OHOS) #include "ecmascript/extractortool/src/extractor.h" -#include "ecmascript/extractortool/src/source_map.h" #endif #if defined(ENABLE_EXCEPTION_BACKTRACE) #include "ecmascript/platform/backtrace.h" @@ -379,6 +379,240 @@ bool GetTypeOffsetAndPrevOffsetFromFrameType(uintptr_t frameType, uintptr_t &typ return true; } +bool ArkFrameCheck(uintptr_t frameType) +{ + return static_cast(frameType) == FrameType::OPTIMIZED_ENTRY_FRAME || + static_cast(frameType) == FrameType::ASM_INTERPRETER_ENTRY_FRAME; +} + +bool IsFunctionFrame(uintptr_t frameType) +{ + // ato need stackmaps, the pc value is not set. + // FrameType::OPTIMIZED_JS_FAST_CALL_FUNCTION_FRAME: + // FrameType::OPTIMIZED_JS_FUNCTION_FRAME: + return static_cast(frameType) == FrameType::ASM_INTERPRETER_FRAME || + static_cast(frameType) == FrameType::INTERPRETER_CONSTRUCTOR_FRAME || + static_cast(frameType) == FrameType::INTERPRETER_FRAME || + static_cast(frameType) == FrameType::INTERPRETER_FAST_NEW_FRAME; +} + +std::optional ReadMethodInfo(panda_file::MethodDataAccessor &mda) +{ + uintptr_t methodId = mda.GetMethodId().GetOffset(); + auto codeId = mda.GetCodeId(); + if (!codeId) { + return std::nullopt; + } + panda_file::CodeDataAccessor cda(mda.GetPandaFile(), codeId.value()); + uint32_t codeSize = cda.GetCodeSize(); + uintptr_t codeBegin = reinterpret_cast(cda.GetInstructions()); + return std::make_optional(methodId, codeBegin, codeSize); +} + +std::vector ReadAllMethodInfos(std::shared_ptr jsPandaFile) +{ + std::vector result; + const panda_file::File *pf = jsPandaFile->GetPandaFile(); + Span classIndexes = jsPandaFile->GetClasses(); + for (const uint32_t index : classIndexes) { + panda_file::File::EntityId classId(index); + if (jsPandaFile->IsExternal(classId)) { + continue; + } + panda_file::ClassDataAccessor cda(*pf, classId); + cda.EnumerateMethods([&result, jsPandaFile](panda_file::MethodDataAccessor &mda) { + auto info = ReadMethodInfo(mda); + if (!info) { + return; + } + result.push_back(info.value()); + }); + } + + std::sort(result.begin(), result.end()); + return result; +} + +std::optional TranslateByteCodePc(uintptr_t realPc, const std::vector &vec) +{ + uint32_t left = 0; + uint32_t right = vec.size() - 1; + for (; left <= right;) { + uint32_t mid = (left + right) / 2; + bool isRight = realPc >= (vec[mid].codeBegin + vec[mid].codeSize); + bool isLeft = realPc < vec[mid].codeBegin; + // codeBegin <= realPc < codeBegin + codeSize + if (!isRight && !isLeft) { + return std::make_optional(realPc - vec[mid].codeBegin, vec[mid].methodId, vec[mid].codeSize); + } else if (isRight) { + left = mid + 1; + } else { + right = mid -1; + } + } + return std::nullopt; +} + +bool ArkParseJsFrameInfo(uintptr_t byteCodePc, uintptr_t mapBase, uintptr_t loadOffset, + uint8_t *data, uint64_t dataSize, JsFunction *jsFunction) +{ + loadOffset = loadOffset % PageSize(); + auto pf = panda_file::OpenPandaFileFromSecureMemory(data, dataSize); + CString fileName = ""; + std::shared_ptr jsPandaFile = std::make_shared(pf.release(), fileName); + auto methodInfos = ReadAllMethodInfos(jsPandaFile); + uintptr_t realOffset = byteCodePc - mapBase - loadOffset; + uintptr_t pfBasePtr = reinterpret_cast(jsPandaFile->GetBase()); + auto codeInfo = TranslateByteCodePc(realOffset + pfBasePtr, methodInfos); + if (!codeInfo) { + return false; + } + auto methodId = EntityId(codeInfo.value().methodId); + std::string name = MethodLiteral::ParseFunctionName(jsPandaFile.get(), methodId); + name = name.empty() ? "anonymous" : name; + + auto debugExtractor = std::make_unique(jsPandaFile.get()); + std::string url = debugExtractor->GetSourceFile(methodId); + auto offset = codeInfo.value().offset; + // line number and column number + int lineNumber = 0; + int columnNumber = 0; + auto callbackLineFunc = [&lineNumber](int32_t line) -> bool { + lineNumber = line + 1; + return true; + }; + auto callbackColumnFunc = [&columnNumber](int32_t column) -> bool { + columnNumber = column + 1; + return true; + }; + if (!debugExtractor->MatchLineWithOffset(callbackLineFunc, methodId, offset) || + !debugExtractor->MatchColumnWithOffset(callbackColumnFunc, methodId, offset)) { + lineNumber = 0; + columnNumber = 0; + } + + jsFunction->codeBegin = byteCodePc - offset; + jsFunction->codeSize = codeInfo.value().codeSize; + jsFunction->line = lineNumber; + jsFunction->column = columnNumber; + size_t nameSize = name.size() + 1; + size_t urlSize = url.size() + 1; + if (strcpy_s(jsFunction->functionName, nameSize, name.c_str()) != EOK || + strcpy_s(jsFunction->url, urlSize, url.c_str()) != EOK) { + LOG_FULL(FATAL) << "strcpy_s failed"; + UNREACHABLE(); + } + return true; +} + +bool ArkTranslateJsFrameInfo(uint8_t *data, size_t dataSize, [[maybe_unused]]JsFunction *jsFunction) +{ + SourceMap sourceMap; + std::string strUrl = jsFunction->url; + sourceMap.Init(data, dataSize, strUrl); + bool ret = sourceMap.TranslateUrlPositionBySourceMap(strUrl, jsFunction->line, jsFunction->column); + size_t strUrlSize = strUrl.size() + 1; + if (strcpy_s(jsFunction->url, strUrlSize, strUrl.c_str()) != EOK) { + LOG_FULL(FATAL) << "strcpy_s failed"; + UNREACHABLE(); + } + return ret; +} + +uintptr_t GetBytecodeOffset(void *ctx, ReadMemFunc readMem, uintptr_t frameType, uintptr_t currentPtr) +{ + // currentPtr points to the frametype. + uintptr_t bytecodePc = 0; + FrameType type = static_cast(frameType); + switch (type) { + case FrameType::ASM_INTERPRETER_FRAME: + case FrameType::INTERPRETER_CONSTRUCTOR_FRAME: { + currentPtr -= AsmInterpretedFrame::GetTypeOffset(); + currentPtr += AsmInterpretedFrame::GetPcOffset(false); + readMem(ctx, currentPtr, &bytecodePc); + return bytecodePc; + } + case FrameType::INTERPRETER_FRAME: + case FrameType::INTERPRETER_FAST_NEW_FRAME: { + currentPtr -= InterpretedFrame::GetTypeOffset(); + currentPtr += InterpretedFrame::GetPcOffset(false); + readMem(ctx, currentPtr, &bytecodePc); + return bytecodePc; + } + // aot need stackmaps + case FrameType::OPTIMIZED_JS_FAST_CALL_FUNCTION_FRAME: + case FrameType::OPTIMIZED_JS_FUNCTION_FRAME: { + break; + } + default: { + break; + } + } + return 0; +} + +bool ArkGetNextFrame(void *ctx, ReadMemFunc readMem, uintptr_t ¤tPtr, uintptr_t &frameType, uintptr_t &pc) +{ + currentPtr -= sizeof(FrameType); + if (!readMem(ctx, currentPtr, &frameType)) { + return false; + } + if (ArkFrameCheck(frameType)) { + return true; + } + bool ret = false; + if (IsFunctionFrame(frameType)) { + pc = GetBytecodeOffset(ctx, readMem, frameType, currentPtr); + ret = true; + } + + uintptr_t typeOffset = 0; + uintptr_t prevOffset = 0; + if (!GetTypeOffsetAndPrevOffsetFromFrameType(frameType, typeOffset, prevOffset)) { + return false; + } + currentPtr -= typeOffset; + currentPtr += prevOffset; + if (!readMem(ctx, currentPtr, ¤tPtr)) { + return false; + } + + if (ret) { + return true; + } + return ArkGetNextFrame(ctx, readMem, currentPtr, frameType, pc); +} + +bool StepArk(void *ctx, ReadMemFunc readMem, uintptr_t *fp, uintptr_t *sp, uintptr_t *pc, bool *isJsFrame) +{ + constexpr size_t FP_SIZE = 8; + constexpr size_t LR_SIZE = 8; + uintptr_t currentPtr = *fp; + if (currentPtr == 0) { + LOG_ECMA(ERROR) << "fp is nullptr in StepArk()!"; + return false; + } + + uintptr_t frameType = 0; + if (ArkGetNextFrame(ctx, readMem, currentPtr, frameType, *pc)) { + if (ArkFrameCheck(frameType)) { + currentPtr += sizeof(FrameType); + bool ret = readMem(ctx, currentPtr, fp); + currentPtr += FP_SIZE; + ret &= readMem(ctx, currentPtr, pc); + currentPtr += LR_SIZE; + ret &= readMem(ctx, currentPtr, sp); + *isJsFrame = false; + return ret; + } else { + *fp = currentPtr; + *sp = currentPtr; + *isJsFrame = true; + } + } + return true; +} + #if defined(PANDA_TARGET_OHOS) uintptr_t ArkGetFunction(int pid, uintptr_t currentPtr, uintptr_t frameType) { @@ -740,12 +974,6 @@ bool ArkGetNextFrame(uintptr_t ¤tPtr, uintptr_t typeOffset, return true; } -bool ArkFrameCheck(uintptr_t frameType) -{ - return static_cast(frameType) == FrameType::OPTIMIZED_ENTRY_FRAME || - static_cast(frameType) == FrameType::ASM_INTERPRETER_ENTRY_FRAME; -} - bool GetArkNativeFrameInfo(int pid, uintptr_t *pc, uintptr_t *fp, uintptr_t *sp, panda::ecmascript::JsFrame *jsFrame, size_t &size) { @@ -1010,6 +1238,34 @@ bool GetArkJSHeapCrashInfo(int pid, uintptr_t *bytecodePc, uintptr_t *fp, bool o } } // namespace panda::ecmascript +__attribute__((visibility("default"))) int step_ark( + void *ctx, panda::ecmascript::ReadMemFunc readMem, uintptr_t *fp, uintptr_t *sp, uintptr_t *pc, bool *isJsFrame) +{ + if (panda::ecmascript::StepArk(ctx, readMem, fp, sp, pc, isJsFrame)) { + return 1; + } + return -1; +} + +__attribute__((visibility("default"))) int ark_parse_js_frame_info( + uintptr_t byteCodePc, uintptr_t mapBase, uintptr_t loadOffset, uint8_t *data, + uint64_t dataSize, panda::ecmascript::JsFunction *jsFunction) +{ + if (panda::ecmascript::ArkParseJsFrameInfo(byteCodePc, mapBase, loadOffset, data, dataSize, jsFunction)) { + return 1; + } + return -1; +} + +__attribute__((visibility("default"))) int ark_translate_js_frame_info( + uint8_t *data, size_t dataSize, panda::ecmascript::JsFunction *jsFunction) +{ + if (panda::ecmascript::ArkTranslateJsFrameInfo(data, dataSize, jsFunction)) { + return 1; + } + return -1; +} + __attribute__((visibility("default"))) int step_ark_managed_native_frame( int pid, uintptr_t *pc, uintptr_t *fp, uintptr_t *sp, char *buf, size_t buf_sz) { diff --git a/ecmascript/dfx/stackinfo/js_stackinfo.h b/ecmascript/dfx/stackinfo/js_stackinfo.h index e21f06dbb1..60457879a4 100644 --- a/ecmascript/dfx/stackinfo/js_stackinfo.h +++ b/ecmascript/dfx/stackinfo/js_stackinfo.h @@ -20,12 +20,12 @@ #include "ecmascript/compiler/aot_file/aot_file_manager.h" #include "ecmascript/js_thread.h" -#if defined(PANDA_TARGET_OHOS) -constexpr uint16_t URL_MAX = 1024; -constexpr uint16_t FUNCTIONNAME_MAX = 1024; -#endif - namespace panda::ecmascript { +typedef bool (*ReadMemFunc)(void *ctx, uintptr_t addr, uintptr_t *val); + +static constexpr uint16_t URL_MAX = 1024; +static constexpr uint16_t FUNCTIONNAME_MAX = 1024; + struct JsFrameInfo { std::string functionName; std::string fileName; @@ -33,10 +33,33 @@ struct JsFrameInfo { uintptr_t *nativePointer = nullptr; }; -struct JsFrameParam { - std::string PandaFileName; - panda_file::File::EntityId methodId; - uint32_t byteCodeOffset; +struct JsFunction { + char functionName[FUNCTIONNAME_MAX]; + char url[URL_MAX]; + int32_t line; + int32_t column; + uintptr_t codeBegin; + uintptr_t codeSize; +}; + +struct MethodInfo { + uintptr_t methodId; + uintptr_t codeBegin; + uint32_t codeSize; + MethodInfo(uintptr_t methodId, uintptr_t codeBegin, uint32_t codeSize) + : methodId(methodId), codeBegin(codeBegin), codeSize(codeSize) {} + friend bool operator<(const MethodInfo &lhs, const MethodInfo &rhs) + { + return lhs.codeBegin < rhs.codeBegin; + } +}; + +struct CodeInfo { + uintptr_t offset; + uintptr_t methodId; + uint32_t codeSize; + CodeInfo(uintptr_t offset, uintptr_t methodId, uint32_t codeSize) + : offset(offset), methodId(methodId), codeSize(codeSize) {} }; #if defined(PANDA_TARGET_OHOS) @@ -63,6 +86,13 @@ extern "C" int step_ark_managed_native_frame( int pid, uintptr_t *pc, uintptr_t *fp, uintptr_t *sp, char *buf, size_t buf_sz); extern "C" int get_ark_js_heap_crash_info( int pid, uintptr_t *x20, uintptr_t *fp, int out_js_info, char *buf, size_t buf_sz); +extern "C" int ark_parse_js_frame_info( + uintptr_t byteCodePc, uintptr_t mapBase, uintptr_t loadOffset, uint8_t *data, uint64_t dataSize, + panda::ecmascript::JsFunction *jsFunction); +extern "C" int ark_translate_js_frame_info( + uint8_t *data, size_t dataSize, panda::ecmascript::JsFunction *jsFunction); +extern "C" int step_ark( + void *ctx, panda::ecmascript::ReadMemFunc readMem, uintptr_t *fp, uintptr_t *sp, uintptr_t *pc, bool *isJsFrame); #if defined(PANDA_TARGET_OHOS) extern "C" int get_ark_native_frame_info( int pid, uintptr_t *pc, uintptr_t *fp, uintptr_t *sp, panda::ecmascript::JsFrame *jsFrame, size_t &size); diff --git a/ecmascript/extractortool/src/source_map.cpp b/ecmascript/extractortool/src/source_map.cpp index 57d6f26f2b..a9a8d27823 100644 --- a/ecmascript/extractortool/src/source_map.cpp +++ b/ecmascript/extractortool/src/source_map.cpp @@ -47,6 +47,7 @@ constexpr int32_t DIGIT_NUM = 64; const std::string MEGER_SOURCE_MAP_PATH = "ets/sourceMaps.map"; } // namespace +#if defined(PANDA_TARGET_OHOS) bool SourceMap::ReadSourceMapData(const std::string& hapPath, std::string& content) { if (hapPath.empty()) { @@ -66,6 +67,7 @@ bool SourceMap::ReadSourceMapData(const std::string& hapPath, std::string& conte content.assign(dataPtr.get(), dataPtr.get() + len); return true; } +#endif int32_t StringToInt(const std::string& value) { @@ -100,6 +102,7 @@ uint32_t Base64CharToInt(char charCode) return DIGIT_NUM; }; +#if defined(PANDA_TARGET_OHOS) void SourceMap::Init(const std::string& url, const std::string& hapPath) { std::string sourceMapData; @@ -107,6 +110,14 @@ void SourceMap::Init(const std::string& url, const std::string& hapPath) SplitSourceMap(url, sourceMapData); } } +#endif + +void SourceMap::Init(uint8_t *data, size_t dataSize, const std::string& url) +{ + std::string content; + content.assign(data, data + dataSize); + SplitSourceMap(url, content); +} void SourceMap::SplitSourceMap(const std::string& url, const std::string& sourceMapData) { diff --git a/ecmascript/extractortool/src/source_map.h b/ecmascript/extractortool/src/source_map.h index 4378eca79c..90b817ac76 100644 --- a/ecmascript/extractortool/src/source_map.h +++ b/ecmascript/extractortool/src/source_map.h @@ -68,7 +68,10 @@ public: SourceMap() = default; ~SourceMap() = default; +#if defined(PANDA_TARGET_OHOS) void Init(const std::string& url, const std::string& hapPath); +#endif + void Init(uint8_t *data, size_t dataSize, const std::string& url); bool TranslateUrlPositionBySourceMap(std::string& url, int& line, int& column); static void ExtractStackInfo(const std::string& stackStr, std::vector& res); @@ -81,8 +84,9 @@ private: MappingInfo Find(int32_t row, int32_t col, const SourceMapData& targetMap, const std::string& key); void GetPosInfo(const std::string& temp, int32_t start, std::string& line, std::string& column); bool GetLineAndColumnNumbers(int& line, int& column, SourceMapData& targetMap, std::string& key); +#if defined(PANDA_TARGET_OHOS) bool ReadSourceMapData(const std::string& hapPath, std::string& content); - +#endif private: std::unordered_map> sourceMaps_; }; diff --git a/libark_jsruntime.map b/libark_jsruntime.map index d3768d7100..c19b448df9 100644 --- a/libark_jsruntime.map +++ b/libark_jsruntime.map @@ -347,6 +347,9 @@ get_ark_js_heap_crash_info; step_ark_managed_native_frame; get_ark_native_frame_info; + ark_parse_js_frame_info; + ark_translate_js_frame_info; + step_ark; }; local: *;