diff --git a/ecmascript/dfx/stackinfo/js_stackinfo.cpp b/ecmascript/dfx/stackinfo/js_stackinfo.cpp index 4fb7f5a06fe3f0029049ca86a8e5800099461ffe..fff2b65baf8b861803b8385b7a5c59039925f9c5 100644 --- a/ecmascript/dfx/stackinfo/js_stackinfo.cpp +++ b/ecmascript/dfx/stackinfo/js_stackinfo.cpp @@ -537,7 +537,7 @@ void ParseJsFrameInfo(JSPandaFile *jsPandaFile, DebugInfoExtractor *debugExtract return true; }; - if (!debugExtractor->MatchLineWithOffset(callbackLineFunc, methodId, offset) || + if (!debugExtractor->MatchLineAndRevisedOffset(callbackLineFunc, methodId, offset) || !debugExtractor->MatchColumnWithOffset(callbackColumnFunc, methodId, offset)) { lineNumber = 0; columnNumber = 0; diff --git a/ecmascript/dfx/stackinfo/tests/BUILD.gn b/ecmascript/dfx/stackinfo/tests/BUILD.gn index 049bbc58010d346cbbf98fc6d4067cd5c897b0d2..ffd311f735744f1c86d8084b75fcbee1046df9ce 100644 --- a/ecmascript/dfx/stackinfo/tests/BUILD.gn +++ b/ecmascript/dfx/stackinfo/tests/BUILD.gn @@ -16,6 +16,31 @@ import("//arkcompiler/ets_runtime/test/test_helper.gni") module_output_path = "ets_runtime/ets_runtime" +test_js_path = "//arkcompiler/ets_runtime/ecmascript/dfx/stackinfo/tests/js_files/" + +test_js_files = [ + "index", +] + +foreach(file, test_js_files) { + es2abc_gen_abc("gen_${file}_abc") { + test_js = "${test_js_path}${file}.js" + test_abc = "$target_out_dir/abc_files/${file}.abc" + # Only targets in this file can depend on this. + extra_visibility = [ ":*" ] + src_js = rebase_path(test_js) + dst_file = rebase_path(test_abc) + extra_args = [ + "--module", + "--merge-abc", + "--debug", + ] + + in_puts = [ test_js ] + out_puts = [ test_abc ] + } +} + host_unittest_action("JsStackInfoTest") { module_out_path = module_output_path @@ -39,6 +64,21 @@ host_unittest_action("JsStackInfoTest") { deps = [ "../../../../:libark_jsruntime_test", ] + foreach(file, test_js_files) { + deps += [ ":gen_${file}_abc" ] + } + + if (is_ohos && is_standard_system) { + test_abc_dir = "/data/test/" + } else { + test_abc_dir = rebase_path(target_out_dir + "/abc_files/") + } + + test_js_dir = rebase_path(test_js_path) + defines = [ + "STACKINFO_TEST_JS_FILES_DIR=\"${test_js_dir}\"", + "STACKINFO_TEST_ABC_FILES_DIR=\"${test_abc_dir}\"", + ] # hiviewdfx libraries external_deps = hiviewdfx_ext_deps diff --git a/ecmascript/dfx/stackinfo/tests/js_files/index.js b/ecmascript/dfx/stackinfo/tests/js_files/index.js new file mode 100644 index 0000000000000000000000000000000000000000..65dc8a585b09495431fd62f9213eb282472b82d2 --- /dev/null +++ b/ecmascript/dfx/stackinfo/tests/js_files/index.js @@ -0,0 +1,20 @@ +/* + * 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. + */ + +function foo() { + for (let a = 0; a < 3000000; a++) { + let b = 9; + } +} diff --git a/ecmascript/dfx/stackinfo/tests/js_stackinfo_test.cpp b/ecmascript/dfx/stackinfo/tests/js_stackinfo_test.cpp index 0960f77b043a6b77ca0080af2e1a8d8110e3328d..e4898fe7b5e792a354baa77708c648e54edf7c8d 100644 --- a/ecmascript/dfx/stackinfo/tests/js_stackinfo_test.cpp +++ b/ecmascript/dfx/stackinfo/tests/js_stackinfo_test.cpp @@ -41,19 +41,19 @@ public: void SetUp() override { - TestHelper::CreateEcmaVMWithScope(instance, thread, scope); - instance->SetEnableForceGC(false); + TestHelper::CreateEcmaVMWithScope(instance_, thread_, scope_); + instance_->SetEnableForceGC(false); } void TearDown() override { - TestHelper::DestroyEcmaVMWithScope(instance, scope); + TestHelper::DestroyEcmaVMWithScope(instance_, scope_); JsStackInfo::nameMap.clear(); } - EcmaVM *instance {nullptr}; - EcmaHandleScope *scope {nullptr}; - JSThread *thread {nullptr}; + EcmaVM *instance_ {nullptr}; + EcmaHandleScope *scope_ {nullptr}; + JSThread *thread_ {nullptr}; }; template @@ -411,7 +411,7 @@ HWTEST_F_L0(JsStackInfoTest, TestArkParseJsFrameInfo) */ HWTEST_F_L0(JsStackInfoTest, TestBuildJsStackInfo) { - auto jsFrame = JsStackInfo::BuildJsStackInfo(thread); + auto jsFrame = JsStackInfo::BuildJsStackInfo(thread_); EXPECT_TRUE(jsFrame.empty()); } @@ -1607,4 +1607,44 @@ HWTEST_F_L0(JsStackInfoTest, TestNextArkFrame) free(ctx); } #endif + +HWTEST_F_L0(JsStackInfoTest, MatchLineAndRevisedOffset) { + const std::string fileName = STACKINFO_TEST_ABC_FILES_DIR"index.abc"; + std::string entryPoint = "index"; + + bool result = JSNApi::Execute(instance_, fileName, entryPoint); + ASSERT_TRUE(result); + auto jsPandaFile = JSPandaFileManager::GetInstance()->FindJSPandaFile(CString(fileName)); + EXPECT_NE(jsPandaFile, nullptr); + + auto methods = JSStackTrace::ReadAllMethodInfos(jsPandaFile); + DebugInfoExtractor *debugExtractor = JSPandaFileManager::GetInstance()->GetJSPtExtractor(jsPandaFile.get()); + int32_t lineNumber = 0; + auto callbackLineFunc = [&lineNumber](int32_t line) -> bool { + lineNumber = line + 1; + return true; + }; + auto method = methods[0]; + uintptr_t offset = 54; // line = -1 + debugExtractor->MatchLineWithOffset(callbackLineFunc, EntityId(method.methodId), offset); + EXPECT_TRUE(lineNumber == 0); + debugExtractor->MatchLineAndRevisedOffset(callbackLineFunc, EntityId(method.methodId), offset); + EXPECT_TRUE(lineNumber == 20); + EXPECT_TRUE(offset == 55); + + lineNumber = 0; + offset = 55; + debugExtractor->MatchLineWithOffset(callbackLineFunc, EntityId(method.methodId), offset); + EXPECT_TRUE(lineNumber == 20); + lineNumber = 0; + debugExtractor->MatchLineAndRevisedOffset(callbackLineFunc, EntityId(method.methodId), offset); + EXPECT_TRUE(lineNumber == 20); + + offset = 20; + debugExtractor->MatchLineWithOffset(callbackLineFunc, EntityId(method.methodId), offset); + EXPECT_TRUE(lineNumber == 17); + debugExtractor->MatchLineAndRevisedOffset(callbackLineFunc, EntityId(method.methodId), offset); + EXPECT_TRUE(lineNumber == 17); +} + } // namespace panda::test \ No newline at end of file diff --git a/ecmascript/jspandafile/debug_info_extractor.h b/ecmascript/jspandafile/debug_info_extractor.h index 5484e2188ca31068b3f189c7fb3f01959c4133f6..cbe86299527a4dd5633dea7db989936cf91b7fc7 100644 --- a/ecmascript/jspandafile/debug_info_extractor.h +++ b/ecmascript/jspandafile/debug_info_extractor.h @@ -178,6 +178,32 @@ public: return cb(line); } + template + bool MatchLineAndRevisedOffset(const Callback &cb, panda_file::File::EntityId methodId, uintptr_t &offset) + { + int32_t line = 0; + const LineNumberTable &lineTable = GetLineNumberTable(methodId); + + auto iter = std::upper_bound(lineTable.begin(), lineTable.end(), LineTableEntry {offset, 0}); + + if (iter == lineTable.begin()) { + return cb(line); + } + + if (iter == lineTable.end() || ((iter - 1)->line != -1)) { + line = (iter - 1)->line; + return cb(line); + } + + do { + iter++; + } while (iter != lineTable.end() && ((iter - 1)->line == -1)); + + line = (iter - 1)->line; + offset = (iter - 1)->offset; + return cb(line); + } + template bool MatchColumnWithOffset(const Callback &cb, panda_file::File::EntityId methodId, uint32_t offset) { diff --git a/test/resource/js_runtime/ohos_test.xml b/test/resource/js_runtime/ohos_test.xml index b3272d046e00a44e8a41186755b8fd7f340ee6c6..63aa505327500725e75ccad9879648279470c8a9 100755 --- a/test/resource/js_runtime/ohos_test.xml +++ b/test/resource/js_runtime/ohos_test.xml @@ -1794,6 +1794,7 @@ +