支持hiperf ark帧解析

issue: https://gitee.com/openharmony/arkcompiler_ets_runtime/issues/I8VCJK
Signed-off-by: shaoyijiang <shaoyijiang@huawei.com>
Change-Id: Ic0fa1ecdc04d7e17ed40d720c67e53937458f742
This commit is contained in:
shaoyijiang 2024-02-07 03:00:56 +00:00
parent 5212133062
commit 507096fd28
6 changed files with 322 additions and 18 deletions

View File

@ -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",

View File

@ -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) == FrameType::OPTIMIZED_ENTRY_FRAME ||
static_cast<FrameType>(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) == FrameType::ASM_INTERPRETER_FRAME ||
static_cast<FrameType>(frameType) == FrameType::INTERPRETER_CONSTRUCTOR_FRAME ||
static_cast<FrameType>(frameType) == FrameType::INTERPRETER_FRAME ||
static_cast<FrameType>(frameType) == FrameType::INTERPRETER_FAST_NEW_FRAME;
}
std::optional<MethodInfo> 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<uintptr_t>(cda.GetInstructions());
return std::make_optional<MethodInfo>(methodId, codeBegin, codeSize);
}
std::vector<MethodInfo> ReadAllMethodInfos(std::shared_ptr<JSPandaFile> jsPandaFile)
{
std::vector<MethodInfo> result;
const panda_file::File *pf = jsPandaFile->GetPandaFile();
Span<const uint32_t> 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<CodeInfo> TranslateByteCodePc(uintptr_t realPc, const std::vector<MethodInfo> &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<CodeInfo>(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> jsPandaFile = std::make_shared<JSPandaFile>(pf.release(), fileName);
auto methodInfos = ReadAllMethodInfos(jsPandaFile);
uintptr_t realOffset = byteCodePc - mapBase - loadOffset;
uintptr_t pfBasePtr = reinterpret_cast<uintptr_t>(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<DebugInfoExtractor>(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>(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 &currentPtr, 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, &currentPtr)) {
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 &currentPtr, uintptr_t typeOffset,
return true;
}
bool ArkFrameCheck(uintptr_t frameType)
{
return static_cast<FrameType>(frameType) == FrameType::OPTIMIZED_ENTRY_FRAME ||
static_cast<FrameType>(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)
{

View File

@ -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);

View File

@ -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)
{

View File

@ -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<std::string>& 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<std::string, std::shared_ptr<SourceMapData>> sourceMaps_;
};

View File

@ -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:
*;