diff --git a/tooling/agent/debugger_impl.cpp b/tooling/agent/debugger_impl.cpp index 40f74b23c424c9ccf028b15da9178da3206c098b..020aec85150c66a3b34727de944a6402e93160fd 100644 --- a/tooling/agent/debugger_impl.cpp +++ b/tooling/agent/debugger_impl.cpp @@ -109,6 +109,30 @@ bool DebuggerImpl::NotifyScriptParsed(ScriptId scriptId, const std::string &file return true; } +bool DebuggerImpl::NotifyNativeOut(const JSPtLocation &location) +{ + // LOG_DEBUGGER(ERROR) << "YY NotifyNativeOut"; + // var a = testNapi.add(333, 777); + // if (nativeOut_ != nullptr && !nativeOut_->InStepRange(location.GetBytecodeOffset())) { + // } + if (nativeOutPause_) { + uint32_t currentDepth = DebuggerApi::GetStackDepth(vm_); + LOG_DEBUGGER(ERROR) << "YY currentDepth: " << currentDepth; + nativeOutPause_ = false; + if (stackDepth_ == currentDepth) { + return true; + } + } + + if (nativeCallJSPause_) { + LOG_DEBUGGER(ERROR) << "YY nativeCallJSPause_"; + nativeCallJSPause_ = false; + return true; + } + + return false; +} + bool DebuggerImpl::NotifySingleStep(const JSPtLocation &location) { if (UNLIKELY(pauseOnNextByteCode_)) { @@ -242,9 +266,12 @@ void DebuggerImpl::NotifyPaused(std::optional location, PauseReaso paused.SetData(std::move(tmpException)); } frontend_.Paused(vm_, paused); - if (reason != BREAK_ON_START) { + if (reason != BREAK_ON_START && reason != NATIVE_OUT) { singleStepper_.reset(); } + LOG_DEBUGGER(ERROR) << "YY NOTIFYPAUSED"; + nativeOutPause_ = false; + nativeCallJSPause_ = false; debuggerState_ = DebuggerState::PAUSED; frontend_.WaitForDebugger(vm_); DebuggerApi::SetException(vm_, exception); @@ -262,7 +289,6 @@ void DebuggerImpl::NotifyNativeCalling(const void *nativeAddress) } frontend_.MixedStack(vm_, mixedStack); } - // native calling only after step into should be reported if (singleStepper_ != nullptr && singleStepper_->GetStepperType() == StepperType::STEP_INTO) { @@ -272,6 +298,53 @@ void DebuggerImpl::NotifyNativeCalling(const void *nativeAddress) frontend_.NativeCalling(vm_, nativeCalling); frontend_.WaitForDebugger(vm_); } + + bool isNativeCalling = false; + uint64_t nativeEntry = reinterpret_cast(nativeAddress); + for (const auto &nativeRange : nativeRanges_) { + if (nativeEntry >= nativeRange.GetStart() && nativeEntry <= nativeRange.GetEnd()) { + isNativeCalling = true; + break; + } + } + if (!isNativeCalling) { + return; + } + + nativeOutPause_ = true; + checkNeedPause_ = true; + stackDepth_ = DebuggerApi::GetStackDepth(vm_); + LOG_DEBUGGER(ERROR) << "YY stackDepth_: " << stackDepth_; + LOG_DEBUGGER(ERROR) << "YY nativeOutPause_: " << nativeOutPause_; + // nativeOut_ = SingleStepper::GetStepOverStepper(vm_); + // if (nativeOut_ == nullptr) { + // LOG_DEBUGGER(ERROR) << "StepOver: singleStepper is null"; + // } +} + +void DebuggerImpl::JSReturnNative() +{ + LOG_DEBUGGER(ERROR) << "YY JSReturnNative"; + if (mixStackEnabled_) { + tooling::MixedStack mixedStack; + nativePointer_ = DebuggerApi::GetNativePointer(vm_); + mixedStack.SetNativePointers(nativePointer_); + std::vector> callFrames; + if (GenerateCallFrames(&callFrames)) { + mixedStack.SetCallFrames(std::move(callFrames)); + } + if (!nativePointer_.empty()) { + frontend_.MixedStack(vm_, mixedStack); + } + } + + if (checkNeedPause_) { + // nativeOut_ = SingleStepper::GetStepOverStepper(vm_); + // if (nativeOut_ == nullptr) { + // LOG_DEBUGGER(ERROR) << "StepOver: singleStepper is null"; + // } + nativeCallJSPause_ = true; + } } // only use for test case @@ -310,7 +383,8 @@ void DebuggerImpl::DispatcherImpl::Dispatch(const DispatchRequest &request) { "setBlackboxPatterns", &DebuggerImpl::DispatcherImpl::SetBlackboxPatterns }, { "replyNativeCalling", &DebuggerImpl::DispatcherImpl::ReplyNativeCalling }, { "getPossibleAndSetBreakpointByUrl", &DebuggerImpl::DispatcherImpl::GetPossibleAndSetBreakpointByUrl }, - { "dropFrame", &DebuggerImpl::DispatcherImpl::DropFrame } + { "dropFrame", &DebuggerImpl::DispatcherImpl::DropFrame }, + { "setNativeRange", &DebuggerImpl::DispatcherImpl::SetNativeRange } }; const std::string &method = request.GetMethod(); @@ -500,6 +574,17 @@ void DebuggerImpl::DispatcherImpl::SetSkipAllPauses(const DispatchRequest &reque SendResponse(request, response); } +void DebuggerImpl::DispatcherImpl::SetNativeRange(const DispatchRequest &request) +{ + std::unique_ptr params = SetNativeRangeParams::Create(request.GetParams()); + if (params == nullptr) { + SendResponse(request, DispatchResponse::Fail("wrong params")); + return; + } + DispatchResponse response = debugger_->SetNativeRange(*params); + SendResponse(request, response); +} + void DebuggerImpl::DispatcherImpl::StepInto(const DispatchRequest &request) { std::unique_ptr params = StepIntoParams::Create(request.GetParams()); @@ -989,6 +1074,16 @@ bool DebuggerImpl::ProcessSingleBreakpoint(const BreakpointInfo &breakpoint, return true; } +DispatchResponse DebuggerImpl::SetNativeRange(const SetNativeRangeParams ¶ms) +{ + nativeRanges_ = params.GetNativeRange(); + // const std::vector> *nativeRanges = params.GetNativeRange(); + // for (const auto &native : *nativeRanges) { + // nativeRanges_.emplace_back(std::make_unique(*native)); + // } + return DispatchResponse::Ok(); +} + DispatchResponse DebuggerImpl::SetPauseOnExceptions(const SetPauseOnExceptionsParams ¶ms) { pauseOnException_ = params.GetState(); diff --git a/tooling/agent/debugger_impl.h b/tooling/agent/debugger_impl.h index 60bf41360b3fb8d902278644f65e73f888a4e279..7e87850116cf0aaac2d3b2a51effedde19a9dd1f 100644 --- a/tooling/agent/debugger_impl.h +++ b/tooling/agent/debugger_impl.h @@ -41,9 +41,11 @@ public: bool NotifyScriptParsed(ScriptId scriptId, const std::string &fileName, std::string_view entryPoint = "func_main_0"); bool NotifySingleStep(const JSPtLocation &location); + bool NotifyNativeOut(const JSPtLocation &location); void NotifyPaused(std::optional location, PauseReason reason); void NotifyHandleProtocolCommand(); void NotifyNativeCalling(const void *nativeAddress); + void JSReturnNative(); void SetDebuggerState(DebuggerState debuggerState); DispatchResponse ContinueToLocation(const ContinueToLocationParams ¶ms); @@ -65,6 +67,7 @@ public: std::vector> &outLocations); DispatchResponse SetPauseOnExceptions(const SetPauseOnExceptionsParams ¶ms); DispatchResponse SetSkipAllPauses(const SetSkipAllPausesParams ¶ms); + DispatchResponse SetNativeRange(const SetNativeRangeParams ¶ms); DispatchResponse StepInto(const StepIntoParams ¶ms); DispatchResponse StepOut(); DispatchResponse StepOver(const StepOverParams ¶ms); @@ -150,6 +153,7 @@ public: void SetBreakpointsActive(const DispatchRequest &request); void SetPauseOnExceptions(const DispatchRequest &request); void SetSkipAllPauses(const DispatchRequest &request); + void SetNativeRange(const DispatchRequest &request); void StepInto(const DispatchRequest &request); void StepOut(const DispatchRequest &request); void StepOver(const DispatchRequest &request); @@ -247,8 +251,16 @@ private: bool mixStackEnabled_ {false}; std::unique_ptr singleStepper_ {nullptr}; Location location_ {}; + + std::unique_ptr nativeOut_ {nullptr}; std::vector nativePointer_; + bool nativeOutPause_ {false}; + bool nativeCallJSPause_ {false}; + bool checkNeedPause_ {false}; + bool isStepOut_ {false}; + uint32_t stackDepth_ {0}; + std::vector nativeRanges_ {}; std::unordered_map scopeObjects_ {}; std::vector> callFrameHandlers_; JsDebuggerManager::ObjectUpdaterFunc updaterFunc_ {nullptr}; diff --git a/tooling/backend/js_pt_hooks.cpp b/tooling/backend/js_pt_hooks.cpp index b7afbd0ece46c9b6ae306cb340363f016ca43c1b..e1765264a1abc14247ed419994cd0b894d13d328 100644 --- a/tooling/backend/js_pt_hooks.cpp +++ b/tooling/backend/js_pt_hooks.cpp @@ -66,6 +66,16 @@ bool JSPtHooks::SingleStep(const JSPtLocation &location) return false; } +bool JSPtHooks::NativeOut(const JSPtLocation &location) +{ + if (debugger_->NotifyNativeOut(location)) { + debugger_->NotifyPaused({}, NATIVE_OUT); + return true; + } + + return false; +} + void JSPtHooks::LoadModule(std::string_view pandaFileName, std::string_view entryPoint) { LOG_DEBUGGER(VERBOSE) << "JSPtHooks: LoadModule: " << pandaFileName; @@ -86,4 +96,13 @@ void JSPtHooks::NativeCalling(const void *nativeAddress) debugger_->NotifyNativeCalling(nativeAddress); } + +void JSPtHooks::JSReturnNative() +{ + LOG_DEBUGGER(INFO) << "JSPtHooks: JSReturnNative"; + + [[maybe_unused]] LocalScope scope(debugger_->vm_); + + debugger_->JSReturnNative(); +} } // namespace panda::ecmascript::tooling diff --git a/tooling/backend/js_pt_hooks.h b/tooling/backend/js_pt_hooks.h index 1df842db7c0308597740e305873a184e52268b7c..0ed8461a8b490b4502906dd3c40b4d4dca1ac632 100644 --- a/tooling/backend/js_pt_hooks.h +++ b/tooling/backend/js_pt_hooks.h @@ -35,7 +35,9 @@ public: void LoadModule(std::string_view pandaFileName, std::string_view entryPoint) override; void Exception(const JSPtLocation &location) override; bool SingleStep(const JSPtLocation &location) override; + bool NativeOut(const JSPtLocation &location) override; void NativeCalling(const void *nativeAddress) override; + void JSReturnNative() override; void VmStart() override {} void VmDeath() override {} diff --git a/tooling/backend/js_single_stepper.cpp b/tooling/backend/js_single_stepper.cpp index 3e5555b9870038c5c3461bb26128cfbcd11fa1e9..6a7bb30301c1406e2b35a8ee0ab5057e8c60a5fb 100644 --- a/tooling/backend/js_single_stepper.cpp +++ b/tooling/backend/js_single_stepper.cpp @@ -93,10 +93,14 @@ std::list SingleStepper::GetStepRanges(DebugInfoExtractor *extrac std::list ranges {}; const LineNumberTable &table = extractor->GetLineNumberTable(methodId); auto callbackFunc = [table, &ranges](int32_t line) -> bool { + LOG_DEBUGGER(ERROR) << "YY line: " << line; for (auto it = table.begin(); it != table.end(); ++it) { auto next = it + 1; if (it->line == line) { JSPtStepRange range {it->offset, next != table.end() ? next->offset : UINT32_MAX}; + uint32_t end = next != table.end() ? next->offset : UINT32_MAX; + LOG_DEBUGGER(ERROR) << "YY start: " << it->offset; + LOG_DEBUGGER(ERROR) << "YY end: " << end; ranges.push_back(range); } } @@ -110,7 +114,10 @@ std::unique_ptr SingleStepper::GetStepper(const EcmaVM *ecmaVm, SingleStepper::Type type) { std::unique_ptr ptMethod = DebuggerApi::GetMethod(ecmaVm); - ASSERT(ptMethod != nullptr); + if (ptMethod == nullptr) { + LOG_DEBUGGER(ERROR) << "GetStepper: ptMethod is null"; + return nullptr; + } DebugInfoExtractor *extractor = JSPandaFileManager::GetInstance()->GetJSPtExtractor( ptMethod->GetJSPandaFile()); @@ -125,6 +132,7 @@ std::unique_ptr SingleStepper::GetStepper(const EcmaVM *ecmaVm, std::list ranges = GetStepRanges(extractor, ptMethod->GetMethodId(), DebuggerApi::GetBytecodeOffset(ecmaVm)); + return std::make_unique(ecmaVm, std::move(ptMethod), std::move(ranges), type); } } // namespace panda::ecmascript::tooling diff --git a/tooling/backend/js_single_stepper.h b/tooling/backend/js_single_stepper.h index d098a26502a9ef3f37689b227740ea85d3522f72..9833ebc365dc79969c543cc4f262441600784621 100644 --- a/tooling/backend/js_single_stepper.h +++ b/tooling/backend/js_single_stepper.h @@ -40,6 +40,7 @@ public: NO_MOVE_SEMANTIC(SingleStepper); bool StepComplete(uint32_t bcOffset) const; + bool InStepRange(uint32_t pc) const; Type GetStepperType() const { return type_; @@ -51,7 +52,7 @@ public: private: uint32_t GetStackDepth() const; - bool InStepRange(uint32_t pc) const; + static std::list GetStepRanges(DebugInfoExtractor *extractor, panda_file::File::EntityId methodId, uint32_t offset); static std::unique_ptr GetStepper(const EcmaVM *ecmaVm, SingleStepper::Type type); diff --git a/tooling/base/pt_events.h b/tooling/base/pt_events.h index 4ebacba27368c9bf52392de804c34f461047b669..8dc8374a164dc91df42fd512a3439a470d2ec75a 100644 --- a/tooling/base/pt_events.h +++ b/tooling/base/pt_events.h @@ -150,6 +150,9 @@ public: case BREAK_ON_START: { return "Break on start"; } + case NATIVE_OUT: { + return "Native out"; + } default: { LOG_DEBUGGER(ERROR) << "Unknown paused reason: " << reason; } diff --git a/tooling/base/pt_params.cpp b/tooling/base/pt_params.cpp index 5e9018004972c9c6b68013806c16fbad829e8ef9..38a67e9a51dc75ac61e494969f5cc0db7ca43f27 100644 --- a/tooling/base/pt_params.cpp +++ b/tooling/base/pt_params.cpp @@ -562,6 +562,41 @@ std::unique_ptr DropFrameParams::Create(const PtJson ¶ms) return paramsObject; } +std::unique_ptr SetNativeRangeParams::Create(const PtJson ¶ms) +{ + auto paramsObject = std::make_unique(); + std::string error; + Result ret; + + std::unique_ptr nativeRange; + ret = params.GetArray("nativeRange", &nativeRange); + if (ret == Result::SUCCESS) { + int32_t len = nativeRange->GetSize(); + std::vector vectorNativeRange; + for (int32_t i = 0; i < len; ++i) { + std::unique_ptr obj = NativeRange::Create(*nativeRange->Get(i)); + if (obj == nullptr) { + error += "'nativeRange' is invalid;"; + break; + } else { + vectorNativeRange.emplace_back(std::move(*obj)); + } + } + if (vectorNativeRange.size()) { + paramsObject->nativeRange_ = std::move(vectorNativeRange); + } + } else if (ret == Result::TYPE_ERROR) { + error += "Unknown 'nativeRange';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "SetNativeRangeParams::Create " << error; + return nullptr; + } + + return paramsObject; +} + std::unique_ptr SetMixedDebugParams::Create(const PtJson ¶ms) { auto paramsObject = std::make_unique(); diff --git a/tooling/base/pt_params.h b/tooling/base/pt_params.h index 5b83277a0d999eca63c7495e32606fcc74ace74e..12d25c5d46334f76e5375c05a9f9ef36c0fdb7d2 100644 --- a/tooling/base/pt_params.h +++ b/tooling/base/pt_params.h @@ -521,6 +521,21 @@ private: std::optional droppedDepth_ {}; }; +class SetNativeRangeParams { +public: + SetNativeRangeParams() = default; + ~SetNativeRangeParams() = default; + static std::unique_ptr Create(const PtJson ¶ms); + + std::vector GetNativeRange() const + { + return nativeRange_; + } +private: + + std::vector nativeRange_ {}; +}; + class SetMixedDebugParams : public PtBaseParams { public: SetMixedDebugParams() = default; diff --git a/tooling/base/pt_types.cpp b/tooling/base/pt_types.cpp index d4b1df5613ea4200029ef3d715b85e6c7d9f88a7..1d15c5d7bc86a211624ab6143b5f0483b87caed2 100644 --- a/tooling/base/pt_types.cpp +++ b/tooling/base/pt_types.cpp @@ -1680,6 +1680,36 @@ std::unique_ptr LocationRange::ToJson() const return result; } +std::unique_ptr NativeRange::Create(const PtJson ¶ms) +{ + std::string error; + auto nativeRange = std::make_unique(); + Result ret; + + uint32_t start; + ret = params.GetUInt("start", &start); + if (ret == Result::SUCCESS) { + nativeRange->start_ = std::move(start); + } else { + error += "Unknown 'start';"; + } + + uint32_t end; + ret = params.GetUInt("end", &end); + if (ret == Result::SUCCESS) { + nativeRange->end_ = std::move(end); + } else { + error += "Unknown 'end';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "NativeRange::Create " << error; + return nullptr; + } + + return nativeRange; +} + std::unique_ptr BreakLocation::Create(const PtJson ¶ms) { std::string error; diff --git a/tooling/base/pt_types.h b/tooling/base/pt_types.h index e811193fd902de9a15b2b27a77b44487ebd8ca41..b9a9630facdb431bd910bc0a5a93abaf872b9cc6 100644 --- a/tooling/base/pt_types.h +++ b/tooling/base/pt_types.h @@ -1301,6 +1301,41 @@ private: std::unique_ptr end_ {nullptr}; }; +class NativeRange { +public: + NativeRange() = default; + ~NativeRange() = default; + + static std::unique_ptr Create(const PtJson ¶ms); + + uint32_t GetStart() const + { + return start_; + } + + NativeRange &SetStart(uint32_t start) + { + start_ = std::move(start); + return *this; + } + + uint32_t GetEnd() const + { + return end_; + } + + NativeRange &SetEnd(uint32_t end) + { + end_ = std::move(end); + return *this; + } + +private: + + uint32_t start_ {0}; + uint32_t end_ {0}; +}; + // Debugger.BreakLocation class BreakLocation final : public PtBaseTypes { public: diff --git a/tooling/test/utils/test_events.h b/tooling/test/utils/test_events.h index 7f7b809b463c2efb753ce0dc526540264a67ef9b..2d08afc06081ea9b10a3353b9ce995581b6da676 100644 --- a/tooling/test/utils/test_events.h +++ b/tooling/test/utils/test_events.h @@ -28,6 +28,7 @@ using BreakpointCallback = std::function; using LoadModuleCallback = std::function; using ExceptionCallback = std::function; using SingleStepCallback = std::function; +using NativeOutCallback = std::function; using VmStartCallback = std::function; using VmDeathCallback = std::function; using Scenario = std::function; @@ -56,6 +57,7 @@ struct TestEvents { LoadModuleCallback loadModule; ExceptionCallback exception; SingleStepCallback singleStep; + NativeOutCallback nativeOut; VmStartCallback vmStart; VmDeathCallback vmDeath; diff --git a/tooling/test/utils/test_hooks.h b/tooling/test/utils/test_hooks.h index d5a51cd27f11a872cc74a5643b866f8939763dfa..680f8c45662a91b07dea9a93fc9a94c000327e0f 100644 --- a/tooling/test/utils/test_hooks.h +++ b/tooling/test/utils/test_hooks.h @@ -87,6 +87,15 @@ public: return false; } + bool NativeOut(const JSPtLocation &location) override + { + if (test_->nativeOut) { + return test_->nativeOut(location); + } + + return false; + } + void VmDeath() override { if (test_->vmDeath) { @@ -105,6 +114,8 @@ public: void NativeCalling([[maybe_unused]] const void *nativeAddress) override {} + void JSReturnNative() override {} + void TerminateTest() { debugInterface_->UnregisterHooks();