Debugger breakpoint adapts sendable features

Reconstruct and revise debugger breakpoint module to adapt sendable features

Issue:  #IAE06X
Signed-off-by: huangtianzhi <huangtianzhi1@huawei.com>
This commit is contained in:
huangtianzhi 2024-07-22 15:16:31 +08:00
parent 9175348584
commit f516e8568f
7 changed files with 616 additions and 73 deletions

View File

@ -337,9 +337,24 @@ void DebuggerApi::GetObjectClassName(const EcmaVM *ecmaVM, Local<JSValueRef> &ta
}
}
bool DebuggerApi::RemoveBreakpointsByUrl(JSDebugger *debugger, const std::string &url)
bool DebuggerApi::SetBreakpointWithNoMatchUrl(JSDebugger *debugger, const JSPtLocation &location)
{
return debugger->RemoveBreakpointsByUrl(url);
return debugger->SetUrlNotMatchedBreakpoint(location);
}
std::vector<bool> DebuggerApi::SetBreakpointByList(JSDebugger *debugger, std::vector<JSPtLocation> &list)
{
return debugger->SetBreakpointUsingList(list);
}
bool DebuggerApi::RemoveBreakpointWithNoMatchUrl(JSDebugger *debugger, const JSPtLocation &location)
{
return debugger->RemoveUrlNotMatchedBreakpoint(location);
}
bool DebuggerApi::RemoveAllBreakpointsByUrl(JSDebugger *debugger, const std::string &url, bool skipGlobal)
{
return debugger->RemoveAllBreakpointsByUrl(url, skipGlobal);
}
// ScopeInfo
@ -1365,4 +1380,4 @@ DebuggerApi::DebuggerManagedScope::~DebuggerManagedScope()
thread_->UpdateState(static_cast<ecmascript::ThreadState>(oldThreadState_));
}
}
} // namespace panda::ecmascript::tooling
} // namespace panda::ecmascript::tooling

View File

@ -157,7 +157,11 @@ public:
static int32_t GetObjectHash(const EcmaVM *ecmaVM, const JSHandle<JSTaggedValue> &tagged);
static void GetObjectClassName(const EcmaVM *ecmaVM, Local<JSValueRef> &tagged, std::string &className);
static bool RemoveBreakpointsByUrl(JSDebugger *debugger, const std::string &url);
// JSDebugger new added
static bool SetBreakpointWithNoMatchUrl(JSDebugger *debugger, const JSPtLocation &location);
static std::vector<bool> SetBreakpointByList(JSDebugger *debugger, std::vector<JSPtLocation> &list);
static bool RemoveBreakpointWithNoMatchUrl(JSDebugger *debugger, const JSPtLocation &location);
static bool RemoveAllBreakpointsByUrl(JSDebugger *debugger, const std::string &url, bool skipGlobal);
// HotReload
static std::vector<DebugInfoExtractor *> GetPatchExtractors(const EcmaVM *ecmaVm, const std::string &url);
@ -203,4 +207,4 @@ public:
};
} // namespace panda::ecmascript::tooling
#endif // ECMASCRIPT_DEBUGGER_DEBUGGER_API_H
#endif // ECMASCRIPT_DEBUGGER_DEBUGGER_API_H

View File

@ -27,27 +27,332 @@
namespace panda::ecmascript::tooling {
using panda::ecmascript::base::BuiltinsBase;
std::shared_mutex JSDebugger::listMutex_;
CUnorderedMap<CString, CUnorderedMap<std::string,
CUnorderedSet<JSBreakpoint, HashJSBreakpoint>>> JSDebugger::globalBpList_ {};
bool JSDebugger::SetBreakpoint(const JSPtLocation &location, Local<FunctionRef> condFuncRef)
{
// acquire write lock of global list
std::unique_lock<std::shared_mutex> globalListLock(listMutex_);
Global<FunctionRef> funcRef = Global<FunctionRef>(ecmaVm_, condFuncRef);
return SetBreakpointWithMatchedUrl(location, funcRef);
}
bool JSDebugger::SetUrlNotMatchedBreakpoint(const JSPtLocation &location)
{
// acquire write lock of global list
std::unique_lock<std::shared_mutex> globalListLock(listMutex_);
return SetBreakpointWithoutMatchedUrl(location);
}
std::vector<bool> JSDebugger::SetBreakpointUsingList(std::vector<JSPtLocation> &list)
{
// acquire write lock of global list
std::unique_lock<std::shared_mutex> globalListLock(listMutex_);
return SetBreakpointByList(list);
}
std::vector<bool> JSDebugger::SetBreakpointByList(std::vector<JSPtLocation> &list)
{
std::vector<bool> result;
for (const auto &location : list) {
if (location.GetJsPandaFile() != nullptr) {
// URL matched breakpoint
auto funcRef = location.GetCondFuncRef();
result.emplace_back(SetBreakpointWithMatchedUrl(location, funcRef));
} else if (location.GetSourceFile() == "invalid") {
// Invalid breakpoint (condition check failed or match location failed)
result.emplace_back(false);
} else {
// URL not matched breakpoint
result.emplace_back(SetBreakpointWithoutMatchedUrl(location));
}
}
DumpBreakpoints();
DumpGlobalBreakpoints();
ASSERT(list.size() == result.size());
return result;
}
bool JSDebugger::SetBreakpointWithMatchedUrl(const JSPtLocation &location, Global<FunctionRef> condFuncRef)
{
std::unique_ptr<PtMethod> ptMethod = FindMethod(location);
if (ptMethod == nullptr) {
LOG_DEBUGGER(ERROR) << "SetBreakpoint: Cannot find MethodLiteral";
LOG_DEBUGGER(ERROR) << "SetBreakpointWithMatchedUrl: Cannot find MethodLiteral";
return false;
}
if (location.GetBytecodeOffset() >= ptMethod->GetCodeSize()) {
LOG_DEBUGGER(ERROR) << "SetBreakpoint: Invalid breakpoint location";
LOG_DEBUGGER(ERROR) << "SetBreakpointWithMatchedUrl: Invalid breakpoint location";
return false;
}
auto ptMethodPtr = ptMethod.get();
JSBreakpoint jsBreakpoint{location.GetSourceFile(), ptMethod.release(), location.GetBytecodeOffset(),
Global<FunctionRef>(ecmaVm_, condFuncRef), location.GetSourceFile(), location.GetLine(), location.GetColumn()};
auto [_, success] = breakpoints_.emplace(location.GetSourceFile(), ptMethod.release(),
location.GetBytecodeOffset(), Global<FunctionRef>(ecmaVm_, condFuncRef));
bool success = PushBreakpointToLocal(jsBreakpoint, location);
if (!success) {
// also return true
LOG_DEBUGGER(WARN) << "SetBreakpoint: Breakpoint already exists";
LOG_DEBUGGER(WARN) << "SetBreakpointWithMatchedUrl: Breakpoint already exists in localList";
}
DumpBreakpoints();
auto breakpoint = SearchBreakpointInGlobalList(location, ptMethodPtr);
if (!breakpoint.has_value() && ecmaVm_->GetJsDebuggerManager()->IsBreakpointSyncEnabled()) {
// global list does not have this breakpoint, push it to the global list
PushBreakpointToGlobal(jsBreakpoint, location);
}
return true;
}
bool JSDebugger::SetBreakpointWithoutMatchedUrl(const JSPtLocation &location)
{
// try to find this breakpoint in local list
auto breakpointList = SearchNoMatchBreakpointInLocalList(location);
if (!breakpointList.empty()) {
// found this breakpoint in local list, return success
return true;
}
// if not found in local, try to find it in global list
breakpointList = SearchNoMatchBreakpointInGlobalList(location);
if (!breakpointList.empty()) {
// found this breakpoint in global list, return success
for (const auto &breakpoint : breakpointList) {
PullBreakpointFromGlobal(breakpoint);
}
return true;
}
return false;
}
bool JSDebugger::PushBreakpointToLocal(const JSBreakpoint &breakpoint, const JSPtLocation &location)
{
auto pandaFileKey = location.GetJsPandaFile()->GetJSPandaFileDesc();
auto urlKey = location.GetSourceFile();
if (breakpoints_.find(pandaFileKey) == breakpoints_.end()) {
CUnorderedMap<std::string, CUnorderedSet<JSBreakpoint, HashJSBreakpoint>> urlHashMap;
CUnorderedSet<JSBreakpoint, HashJSBreakpoint> breakpointSet;
breakpointSet.emplace(breakpoint);
urlHashMap[urlKey] = breakpointSet;
breakpoints_[pandaFileKey] = urlHashMap;
return true;
} else {
auto urlHashMap = breakpoints_[pandaFileKey];
if (urlHashMap.find(urlKey) == urlHashMap.end()) {
CUnorderedSet<JSBreakpoint, HashJSBreakpoint> breakpointSet;
breakpointSet.emplace(breakpoint);
urlHashMap[urlKey] = breakpointSet;
breakpoints_[pandaFileKey] = urlHashMap;
return true;
} else {
auto [_, success] = breakpoints_[pandaFileKey][urlKey].emplace(breakpoint);
return success;
}
}
}
void JSDebugger::PushBreakpointToGlobal(const JSBreakpoint &breakpoint, const JSPtLocation &location)
{
auto pandaFileKey = location.GetJsPandaFile()->GetJSPandaFileDesc();
auto urlKey = location.GetSourceFile();
if (globalBpList_.find(pandaFileKey) == globalBpList_.end()) {
CUnorderedMap<std::string, CUnorderedSet<JSBreakpoint, HashJSBreakpoint>> urlHashMap;
CUnorderedSet<JSBreakpoint, HashJSBreakpoint> breakpointSet;
breakpointSet.emplace(breakpoint);
urlHashMap[urlKey] = breakpointSet;
globalBpList_[pandaFileKey] = urlHashMap;
} else {
auto urlHashMap = breakpoints_[pandaFileKey];
if (urlHashMap.find(urlKey) == urlHashMap.end()) {
CUnorderedSet<JSBreakpoint, HashJSBreakpoint> breakpointSet;
breakpointSet.emplace(breakpoint);
urlHashMap[urlKey] = breakpointSet;
globalBpList_[pandaFileKey] = urlHashMap;
} else {
globalBpList_[pandaFileKey][urlKey].emplace(breakpoint);
}
}
}
bool JSDebugger::PullBreakpointFromGlobal(const JSBreakpoint &breakpoint)
{
auto pandaFileKey = breakpoint.GetPtMethod()->GetJSPandaFile()->GetJSPandaFileDesc();
auto urlKey = breakpoint.GetUrl();
if (breakpoints_.find(pandaFileKey) == breakpoints_.end()) {
CUnorderedMap<std::string, CUnorderedSet<JSBreakpoint, HashJSBreakpoint>> urlHashMap;
CUnorderedSet<JSBreakpoint, HashJSBreakpoint> breakpointSet;
breakpointSet.emplace(breakpoint);
urlHashMap[urlKey] = breakpointSet;
breakpoints_[pandaFileKey] = urlHashMap;
return true;
} else {
auto urlHashMap = breakpoints_[pandaFileKey];
if (urlHashMap.find(urlKey) == urlHashMap.end()) {
CUnorderedSet<JSBreakpoint, HashJSBreakpoint> breakpointSet;
breakpointSet.emplace(breakpoint);
urlHashMap[urlKey] = breakpointSet;
breakpoints_[pandaFileKey] = urlHashMap;
return true;
} else {
auto [_, success] = breakpoints_[pandaFileKey][urlKey].emplace(breakpoint);
return success;
}
}
}
std::vector<JSBreakpoint> JSDebugger::SearchNoMatchBreakpointInLocalList(const JSPtLocation &location)
{
std::vector<JSBreakpoint> result{};
if (breakpoints_.empty()) {
return result;
}
for (const auto &entry : breakpoints_) {
auto urlHashMap = breakpoints_[entry.first];
auto urlKey = location.GetSourceFile();
if (urlHashMap.find(urlKey) != urlHashMap.end()) {
auto breakpointSet = urlHashMap[urlKey];
for (const auto &bp : breakpointSet) {
if ((bp.GetLine() == location.GetLine()) &&
(bp.GetColumn() == location.GetColumn())) {
result.emplace_back(bp);
}
}
}
}
return result;
}
std::vector<JSBreakpoint> JSDebugger::SearchNoMatchBreakpointInGlobalList(const JSPtLocation &location)
{
std::vector<JSBreakpoint> result{};
if (breakpoints_.empty()) {
return result;
}
for (const auto &entry : globalBpList_) {
auto urlHashMap = globalBpList_[entry.first];
auto urlKey = location.GetSourceFile();
if (urlHashMap.find(urlKey) != urlHashMap.end()) {
auto breakpointSet = urlHashMap[urlKey];
for (const auto &bp : breakpointSet) {
if ((bp.GetLine() == location.GetLine()) &&
(bp.GetColumn() == location.GetColumn())) {
result.emplace_back(bp);
}
}
}
}
return result;
}
std::optional<JSBreakpoint> JSDebugger::SearchBreakpointInGlobalList(const JSPtLocation &location, PtMethod *ptMethod)
{
auto pandaFileKey = ptMethod->GetJSPandaFile()->GetJSPandaFileDesc();
if (globalBpList_.empty() || globalBpList_.find(pandaFileKey) == globalBpList_.end()) {
return {};
}
auto urlHashMap = globalBpList_[pandaFileKey];
if (urlHashMap.empty() || urlHashMap.find(location.GetSourceFile()) == urlHashMap.end()) {
return {};
}
for (const auto &bp : urlHashMap[location.GetSourceFile()]) {
if ((bp.GetBytecodeOffset() == location.GetBytecodeOffset()) &&
(bp.GetPtMethod()->GetJSPandaFile() == ptMethod->GetJSPandaFile()) &&
(bp.GetPtMethod()->GetMethodId() == ptMethod->GetMethodId())) {
return bp;
}
}
return {};
}
std::optional<JSBreakpoint> JSDebugger::SearchBreakpointInGlobalList(JSHandle<Method> method, uint32_t bcOffset)
{
auto pandaFileKey = method->GetJSPandaFile()->GetJSPandaFileDesc();
if (globalBpList_.empty() || globalBpList_.find(pandaFileKey) == globalBpList_.end()) {
return {};
}
auto urlHashMap = globalBpList_.at(pandaFileKey);
for (const auto &entry : urlHashMap) {
for (const auto &bp : urlHashMap[entry.first]) {
if ((bp.GetBytecodeOffset() == bcOffset) &&
(bp.GetPtMethod()->GetJSPandaFile() == method->GetJSPandaFile()) &&
(bp.GetPtMethod()->GetMethodId() == method->GetMethodId())) {
return bp;
}
}
}
return {};
}
bool JSDebugger::RemoveUrlNotMatchedBreakpoint(const JSPtLocation &location)
{
for (const auto &pandafileEntry : breakpoints_) {
for (const auto &urlHashMapEntry : breakpoints_[pandafileEntry.first]) {
if (location.GetSourceFile() == urlHashMapEntry.first) {
for (auto it = breakpoints_[pandafileEntry.first][urlHashMapEntry.first].begin();
it != breakpoints_[pandafileEntry.first][urlHashMapEntry.first].end();) {
const auto &bp = *it;
if ((bp.GetLine() == location.GetLine()) && (bp.GetColumn() == location.GetColumn())) {
it = breakpoints_[pandafileEntry.first][urlHashMapEntry.first].erase(it);
} else {
it++;
}
}
}
}
}
{
std::unique_lock<std::shared_mutex> lock(listMutex_);
RemoveGlobalBreakpoint(location);
}
return true;
}
bool JSDebugger::RemoveGlobalBreakpoint(const std::unique_ptr<PtMethod> &ptMethod, const JSPtLocation &location)
{
auto pandaFileKey = location.GetJsPandaFile()->GetJSPandaFileDesc();
if (globalBpList_.empty() || globalBpList_.find(pandaFileKey) == globalBpList_.end()) {
return false;
}
auto urlHashMap = globalBpList_[pandaFileKey];
if (urlHashMap.empty() || urlHashMap.find(location.GetSourceFile()) == urlHashMap.end()) {
return false;
}
for (auto it = urlHashMap[location.GetSourceFile()].begin(); it != urlHashMap[location.GetSourceFile()].end();) {
const auto &bp = *it;
if ((bp.GetBytecodeOffset() == location.GetBytecodeOffset()) &&
(bp.GetPtMethod()->GetJSPandaFile() == ptMethod->GetJSPandaFile()) &&
(bp.GetPtMethod()->GetMethodId() == ptMethod->GetMethodId())) {
it = urlHashMap[location.GetSourceFile()].erase(it);
} else {
it++;
}
}
return true;
}
bool JSDebugger::RemoveGlobalBreakpoint(const JSPtLocation &location)
{
for (const auto &entry : globalBpList_) {
if (globalBpList_[entry.first].find(location.GetSourceFile()) != globalBpList_[entry.first].end()) {
for (auto it = globalBpList_[entry.first][location.GetSourceFile()].begin();
it != globalBpList_[entry.first][location.GetSourceFile()].end();) {
const auto &bp = *it;
if ((bp.GetLine() == location.GetLine()) && (bp.GetColumn() == location.GetColumn())) {
it = globalBpList_[entry.first][location.GetSourceFile()].erase(it);
} else {
it++;
}
}
}
}
return true;
}
bool JSDebugger::RemoveGlobalBreakpoint(const std::string &url)
{
for (const auto &entry : globalBpList_) {
if (globalBpList_[entry.first].find(url) != globalBpList_[entry.first].end()) {
globalBpList_[entry.first].erase(url);
}
}
return true;
}
@ -65,7 +370,8 @@ bool JSDebugger::SetSmartBreakpoint(const JSPtLocation &location)
}
auto [_, success] = smartBreakpoints_.emplace(location.GetSourceFile(), ptMethod.release(),
location.GetBytecodeOffset(), Global<FunctionRef>(ecmaVm_, FunctionRef::Undefined(ecmaVm_)));
location.GetBytecodeOffset(), Global<FunctionRef>(ecmaVm_, FunctionRef::Undefined(ecmaVm_)),
location.GetSourceFile(), location.GetLine(), location.GetColumn());
if (!success) {
// also return true
LOG_DEBUGGER(WARN) << "SetSmartBreakpoint: Breakpoint already exists";
@ -83,32 +389,51 @@ bool JSDebugger::RemoveBreakpoint(const JSPtLocation &location)
return false;
}
if (!RemoveBreakpoint(ptMethod, location.GetBytecodeOffset())) {
if (!RemoveLocalBreakpoint(ptMethod, location.GetBytecodeOffset())) {
LOG_DEBUGGER(ERROR) << "RemoveBreakpoint: Breakpoint not found";
return false;
}
DumpBreakpoints();
{
std::unique_lock<std::shared_mutex> lock(listMutex_);
RemoveGlobalBreakpoint(ptMethod, location);
}
return true;
}
void JSDebugger::RemoveAllBreakpoints()
{
breakpoints_.clear();
if (!globalBpList_.empty()) {
{
// acquire write lock of global list
std::unique_lock<std::shared_mutex> globalListLock(listMutex_);
globalBpList_.clear();
}
}
}
bool JSDebugger::RemoveBreakpointsByUrl(const std::string &url)
{
for (auto it = breakpoints_.begin(); it != breakpoints_.end();) {
const auto &bp = *it;
if (bp.GetSourceFile() == url) {
it = breakpoints_.erase(it);
} else {
it++;
for (const auto &entry : breakpoints_) {
if (breakpoints_[entry.first].find(url) != breakpoints_[entry.first].end()) {
breakpoints_[entry.first].erase(url);
}
}
return true;
}
DumpBreakpoints();
bool JSDebugger::RemoveAllBreakpointsByUrl(const std::string &url, bool skipGlobal)
{
RemoveBreakpointsByUrl(url);
if (!skipGlobal && !globalBpList_.empty()) {
{
// acquire write lock of global list
std::unique_lock<std::shared_mutex> globalListLock(listMutex_);
RemoveGlobalBreakpoint(url);
}
}
return true;
}
@ -144,25 +469,51 @@ bool JSDebugger::HandleBreakpoint(JSHandle<Method> method, uint32_t bcOffset)
if (hooks_ == nullptr) {
return false;
}
Global<FunctionRef> funcRef = Global<FunctionRef>(ecmaVm_, FunctionRef::Undefined(ecmaVm_));
auto smartBreakpoint = FindSmartBreakpoint(method, bcOffset);
if (smartBreakpoint.has_value()) {
JSPtLocation smartLocation {method->GetJSPandaFile(), method->GetMethodId(), bcOffset,
smartBreakpoint.value().GetSourceFile()};
JSPtLocation smartLocation {method->GetJSPandaFile(), method->GetMethodId(), bcOffset, funcRef,
smartBreakpoint.value().GetLine(),
smartBreakpoint.value().GetColumn(), smartBreakpoint.value().GetSourceFile()};
std::unique_ptr<PtMethod> ptMethod = FindMethod(smartLocation);
RemoveSmartBreakpoint(ptMethod, bcOffset);
hooks_->Breakpoint(smartLocation);
return true;
}
auto breakpoint = FindBreakpoint(method, bcOffset);
if (!breakpoint.has_value() || !IsBreakpointCondSatisfied(breakpoint)) {
return false;
auto breakpoint = FindLocalBreakpoint(method, bcOffset);
if (breakpoint.has_value()) {
// find breakpoint in Local list
if (!IsBreakpointCondSatisfied(breakpoint)) {
return false;
}
JSPtLocation location {method->GetJSPandaFile(), method->GetMethodId(), bcOffset, funcRef,
breakpoint.value().GetLine(), breakpoint.value().GetColumn(),
breakpoint.value().GetSourceFile(), false, method->GetRecordNameStr()};
hooks_->Breakpoint(location);
} else {
// if not found in local, try to find it in global list
{
// acquire read lock of global list
std::shared_lock<std::shared_mutex> globalListLock(listMutex_);
breakpoint = SearchBreakpointInGlobalList(method, bcOffset);
}
if (!breakpoint.has_value()) {
return false;
}
if (!IsBreakpointCondSatisfied(breakpoint)) {
return false;
}
JSPtLocation location {method->GetJSPandaFile(), method->GetMethodId(), bcOffset, funcRef,
breakpoint.value().GetLine(), breakpoint.value().GetColumn(),
breakpoint.value().GetSourceFile(), true, method->GetRecordNameStr()};
hooks_->Breakpoint(location);
if (ecmaVm_->GetJsDebuggerManager()->IsBreakpointSyncEnabled()) {
PullBreakpointFromGlobal(breakpoint.value());
}
}
JSPtLocation location {method->GetJSPandaFile(), method->GetMethodId(), bcOffset,
breakpoint.value().GetSourceFile()};
hooks_->Breakpoint(location);
return true;
}
@ -176,13 +527,25 @@ bool JSDebugger::HandleDebuggerStmt(JSHandle<Method> method, uint32_t bcOffset)
if (singleStepOnDebuggerStmt_) {
return false;
}
auto breakpointAtDebugger = FindBreakpoint(method, bcOffset);
auto breakpointAtDebugger = FindLocalBreakpoint(method, bcOffset);
// if a breakpoint is set on the same line as debugger stmt,
// the debugger stmt is ineffective
if (breakpointAtDebugger.has_value()) {
return false;
}
JSPtLocation location {method->GetJSPandaFile(), method->GetMethodId(), bcOffset};
{
// acquire read lock of global list
std::shared_lock<std::shared_mutex> globalListLock(listMutex_);
breakpointAtDebugger = SearchBreakpointInGlobalList(method, bcOffset);
}
if (breakpointAtDebugger.has_value()) {
return false;
}
Global<FunctionRef> funcRef = Global<FunctionRef>(ecmaVm_, FunctionRef::Undefined(ecmaVm_));
JSPtLocation location {method->GetJSPandaFile(), method->GetMethodId(), bcOffset, funcRef,
breakpointAtDebugger.value().GetLine(),
breakpointAtDebugger.value().GetColumn(), breakpointAtDebugger.value().GetUrl()};
hooks_->DebuggerStmt(location);
return true;
@ -193,8 +556,11 @@ void JSDebugger::HandleExceptionThrowEvent(const JSThread *thread, JSHandle<Meth
if (hooks_ == nullptr || !thread->HasPendingException()) {
return;
}
JSPtLocation throwLocation {method->GetJSPandaFile(), method->GetMethodId(), bcOffset};
Global<FunctionRef> funcRef = Global<FunctionRef>(ecmaVm_, FunctionRef::Undefined(ecmaVm_));
const std::string emptyUrl = "";
int32_t invalidLine = -1;
JSPtLocation throwLocation {method->GetJSPandaFile(), method->GetMethodId(), bcOffset, funcRef,
invalidLine, invalidLine, emptyUrl};
hooks_->Exception(throwLocation);
}
@ -204,19 +570,27 @@ bool JSDebugger::HandleStep(JSHandle<Method> method, uint32_t bcOffset)
if (hooks_ == nullptr) {
return false;
}
JSPtLocation location {method->GetJSPandaFile(), method->GetMethodId(), bcOffset};
Global<FunctionRef> funcRef = Global<FunctionRef>(ecmaVm_, FunctionRef::Undefined(ecmaVm_));
const std::string emptyUrl = "";
int32_t invalidLine = -1;
JSPtLocation location {method->GetJSPandaFile(), method->GetMethodId(), bcOffset, funcRef,
invalidLine, invalidLine, emptyUrl};
return hooks_->SingleStep(location);
}
std::optional<JSBreakpoint> JSDebugger::FindBreakpoint(JSHandle<Method> method, uint32_t bcOffset) const
std::optional<JSBreakpoint> JSDebugger::FindLocalBreakpoint(JSHandle<Method> method, uint32_t bcOffset)
{
for (const auto &bp : breakpoints_) {
if ((bp.GetBytecodeOffset() == bcOffset) &&
(bp.GetPtMethod()->GetJSPandaFile() == method->GetJSPandaFile()) &&
(bp.GetPtMethod()->GetMethodId() == method->GetMethodId())) {
return bp;
for (const auto &pandafileEntry : breakpoints_) {
for (const auto &urlHashMapEntry : breakpoints_[pandafileEntry.first]) {
auto breakpointSet = breakpoints_[pandafileEntry.first][urlHashMapEntry.first];
for (const auto &bp : breakpointSet) {
if ((bp.GetBytecodeOffset() == bcOffset) &&
(bp.GetPtMethod()->GetJSPandaFile() == method->GetJSPandaFile()) &&
(bp.GetPtMethod()->GetMethodId() == method->GetMethodId())) {
return bp;
}
}
}
}
return {};
@ -234,19 +608,24 @@ std::optional<JSBreakpoint> JSDebugger::FindSmartBreakpoint(JSHandle<Method> met
return {};
}
bool JSDebugger::RemoveBreakpoint(const std::unique_ptr<PtMethod> &ptMethod, uint32_t bcOffset)
bool JSDebugger::RemoveLocalBreakpoint(const std::unique_ptr<PtMethod> &ptMethod, uint32_t bcOffset)
{
for (auto it = breakpoints_.begin(); it != breakpoints_.end(); ++it) {
const auto &bp = *it;
if ((bp.GetBytecodeOffset() == bcOffset) &&
(bp.GetPtMethod()->GetJSPandaFile() == ptMethod->GetJSPandaFile()) &&
(bp.GetPtMethod()->GetMethodId() == ptMethod->GetMethodId())) {
it = breakpoints_.erase(it);
return true;
for (const auto &pandafileEntry : breakpoints_) {
for (const auto &urlHashMapEntry : breakpoints_[pandafileEntry.first]) {
for (auto it = breakpoints_[pandafileEntry.first][urlHashMapEntry.first].begin();
it != breakpoints_[pandafileEntry.first][urlHashMapEntry.first].end();) {
const auto &bp = *it;
if ((bp.GetBytecodeOffset() == bcOffset) &&
(bp.GetPtMethod()->GetJSPandaFile() == ptMethod->GetJSPandaFile()) &&
(bp.GetPtMethod()->GetMethodId() == ptMethod->GetMethodId())) {
it = breakpoints_[pandafileEntry.first][urlHashMapEntry.first].erase(it);
} else {
it++;
}
}
}
}
return false;
return true;
}
bool JSDebugger::RemoveSmartBreakpoint(const std::unique_ptr<PtMethod> &ptMethod, uint32_t bcOffset)
@ -288,10 +667,37 @@ std::unique_ptr<PtMethod> JSDebugger::FindMethod(const JSPtLocation &location) c
void JSDebugger::DumpBreakpoints()
{
LOG_DEBUGGER(INFO) << "dump breakpoints with size " << breakpoints_.size();
for (const auto &bp : breakpoints_) {
LOG_DEBUGGER(DEBUG) << bp.ToString();
int32_t size = 0;
LOG_DEBUGGER(DEBUG) << "Dumping breakpoints in local list:";
for (const auto &entry : breakpoints_) {
auto urlHashMap = breakpoints_[entry.first];
for (const auto &pair : urlHashMap) {
LOG_DEBUGGER(DEBUG) << "URL: " << pair.first;
for (const auto &bp : urlHashMap[pair.first]) {
size++;
LOG_DEBUGGER(DEBUG) << "Local #" << size << ": " << bp.ToString();
}
}
}
LOG_DEBUGGER(INFO) << "Dumpped breakpoints in local list with size " << size;
}
void JSDebugger::DumpGlobalBreakpoints()
{
// Should call this function after acquiring the lock of global list
int32_t size = 0;
LOG_DEBUGGER(DEBUG) << "Dumping breakpoints in global list:";
for (const auto &entry : globalBpList_) {
auto urlHashMap = globalBpList_[entry.first];
for (const auto &pair : urlHashMap) {
LOG_DEBUGGER(DEBUG) << "URL: " << pair.first;
for (const auto &bp : urlHashMap[pair.first]) {
size++;
LOG_DEBUGGER(DEBUG) << "Global #" << size << ": " << bp.ToString();
}
}
}
LOG_DEBUGGER(INFO) << "Dumpped breakpoints in global list with size " << size;
}
bool JSDebugger::IsBreakpointCondSatisfied(std::optional<JSBreakpoint> breakpoint) const
@ -301,7 +707,7 @@ bool JSDebugger::IsBreakpointCondSatisfied(std::optional<JSBreakpoint> breakpoin
}
JSThread *thread = ecmaVm_->GetJSThread();
auto condFuncRef = breakpoint.value().GetConditionFunction();
if (condFuncRef->IsFunction(ecmaVm_)) {
if (!condFuncRef->IsHole() && condFuncRef->IsFunction(ecmaVm_)) {
LOG_DEBUGGER(INFO) << "BreakpointCondition: evaluating condition";
auto handlerPtr = std::make_shared<FrameHandler>(ecmaVm_->GetJSThread());
auto evalResult = DebuggerApi::EvaluateViaFuncCall(const_cast<EcmaVM *>(ecmaVm_),
@ -345,4 +751,4 @@ void JSDebugger::MethodExit([[maybe_unused]] JSHandle<Method> method)
auto *debuggerMgr = ecmaVm_->GetJsDebuggerManager();
debuggerMgr->MethodExit(method);
}
} // namespace panda::tooling::ecmascript
} // namespace panda::tooling::ecmascript

View File

@ -27,8 +27,9 @@ class JSBreakpoint {
public:
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
JSBreakpoint(const std::string &sourceFile, PtMethod *ptMethod, uint32_t bcOffset,
const Global<FunctionRef> &condFuncRef) : sourceFile_(sourceFile), ptMethod_(ptMethod),
bcOffset_(bcOffset), condFuncRef_(condFuncRef) {}
const Global<FunctionRef> &condFuncRef, const std::string &url,
int32_t line, int32_t column) : sourceFile_(sourceFile), ptMethod_(ptMethod),
bcOffset_(bcOffset), condFuncRef_(condFuncRef), url_(url), line_(line), column_(column) {}
~JSBreakpoint() = default;
const std::string &GetSourceFile() const
@ -46,6 +47,21 @@ public:
return bcOffset_;
}
const std::string &GetUrl() const
{
return url_;
}
int32_t GetLine() const
{
return line_;
}
int32_t GetColumn() const
{
return column_;
}
bool operator==(const JSBreakpoint &bpoint) const
{
return bcOffset_ == bpoint.GetBytecodeOffset() &&
@ -62,6 +78,9 @@ public:
breakpoint << "bytecodeOffset:" << bcOffset_ << ", ";
breakpoint << "sourceFile:" << "\""<< sourceFile_ << "\""<< ", ";
breakpoint << "jsPandaFile:" << "\"" << ptMethod_->GetJSPandaFile()->GetJSPandaFileDesc() << "\"";
breakpoint << "url:" << "\""<< url_ << "\""<< ", ";
breakpoint << "line:" << line_ << ", ";
breakpoint << "column:" << column_;
breakpoint << "]";
return breakpoint.str();
}
@ -79,6 +98,11 @@ private:
PtMethod *ptMethod_ {nullptr};
uint32_t bcOffset_;
Global<FunctionRef> condFuncRef_;
// Used by new Breakpoint logic
std::string url_;
int32_t line_;
int32_t column_;
};
class HashJSBreakpoint {
@ -168,11 +192,13 @@ public:
{
singleStepOnDebuggerStmt_ = status;
}
bool SetUrlNotMatchedBreakpoint(const JSPtLocation &location);
std::vector<bool> SetBreakpointUsingList(std::vector<JSPtLocation> &list);
bool RemoveUrlNotMatchedBreakpoint(const JSPtLocation &location);
bool RemoveAllBreakpointsByUrl(const std::string &url, bool skipGlobal);
private:
std::unique_ptr<PtMethod> FindMethod(const JSPtLocation &location) const;
std::optional<JSBreakpoint> FindBreakpoint(JSHandle<Method> method, uint32_t bcOffset) const;
std::optional<JSBreakpoint> FindSmartBreakpoint(JSHandle<Method> method, uint32_t bcOffset) const;
bool RemoveBreakpoint(const std::unique_ptr<PtMethod> &ptMethod, uint32_t bcOffset);
bool RemoveSmartBreakpoint(const std::unique_ptr<PtMethod> &ptMethod, uint32_t bcOffset);
void HandleExceptionThrowEvent(const JSThread *thread, JSHandle<Method> method, uint32_t bcOffset);
bool HandleStep(JSHandle<Method> method, uint32_t bcOffset);
@ -180,15 +206,38 @@ private:
bool HandleBreakpoint(JSHandle<Method> method, uint32_t bcOffset);
void DumpBreakpoints();
bool IsBreakpointCondSatisfied(std::optional<JSBreakpoint> breakpoint) const;
bool PushBreakpointToLocal(const JSBreakpoint &breakpoint, const JSPtLocation &location);
// All methods which manipulates the globalBpList_ should be called with write lock acquired
// All methods thatsearches/pulls breakpoints from the globalBpList_ should be used with read lock acquired
std::optional<JSBreakpoint> FindLocalBreakpoint(JSHandle<Method> method, uint32_t bcOffset);
bool RemoveLocalBreakpoint(const std::unique_ptr<PtMethod> &ptMethod, uint32_t bcOffset);
bool SetBreakpointWithMatchedUrl(const JSPtLocation &location, Global<FunctionRef> condFuncRef);
bool SetBreakpointWithoutMatchedUrl(const JSPtLocation &location);
std::vector<bool> SetBreakpointByList(std::vector<JSPtLocation> &list);
void PushBreakpointToGlobal(const JSBreakpoint &breakpoint, const JSPtLocation &location);
bool PullBreakpointFromGlobal(const JSBreakpoint &breakpoint);
void DumpGlobalBreakpoints();
std::optional<JSBreakpoint> SearchBreakpointInGlobalList(const JSPtLocation &location, PtMethod *ptMethod);
std::optional<JSBreakpoint> SearchBreakpointInGlobalList(JSHandle<Method> method, uint32_t bcOffset);
std::vector<JSBreakpoint> SearchNoMatchBreakpointInLocalList(const JSPtLocation &location);
std::vector<JSBreakpoint> SearchNoMatchBreakpointInGlobalList(const JSPtLocation &location);
bool RemoveGlobalBreakpoint(const std::unique_ptr<PtMethod> &ptMethod, const JSPtLocation &location);
bool RemoveGlobalBreakpoint(const JSPtLocation &location);
bool RemoveGlobalBreakpoint(const std::string &url);
const EcmaVM *ecmaVm_;
PtHooks *hooks_ {nullptr};
NotificationManager *notificationMgr_ {nullptr};
bool singleStepOnDebuggerStmt_ {false};
CUnorderedSet<JSBreakpoint, HashJSBreakpoint> breakpoints_ {};
static std::shared_mutex listMutex_;
// Map<JSPandafile, Map<url, Set<JSBreakpoint>>>
static CUnorderedMap<CString, CUnorderedMap<std::string,
CUnorderedSet<JSBreakpoint, HashJSBreakpoint>>> globalBpList_;
CUnorderedMap<CString, CUnorderedMap<std::string,
CUnorderedSet<JSBreakpoint, HashJSBreakpoint>>> breakpoints_ {};
CUnorderedSet<JSBreakpoint, HashJSBreakpoint> smartBreakpoints_ {};
};
} // namespace panda::ecmascript::tooling
#endif // ECMASCRIPT_DEBUGGER_JS_DEBUGGER_H
#endif // ECMASCRIPT_DEBUGGER_JS_DEBUGGER_H

View File

@ -219,6 +219,21 @@ public:
{
return isObjHashDisplayEnabled_;
}
void EnableBreakpointSync()
{
isBreakpointSyncEnabled_ = true;
}
void DisableBreakpointSync()
{
isBreakpointSyncEnabled_ = false;
}
bool IsBreakpointSyncEnabled()
{
return isBreakpointSyncEnabled_;
}
void EnableSerializationTimeoutCheck()
{
@ -256,6 +271,7 @@ private:
bool isMixedStackEnabled_ { false };
bool isSignalInterrupt_ {false};
bool isObjHashDisplayEnabled_ { true };
bool isBreakpointSyncEnabled_ { true };
ProtocolHandler *debuggerHandler_ {nullptr};
LibraryHandle debuggerLibraryHandle_ {nullptr};
ObjectUpdaterFunc *updaterFunc_ {nullptr};
@ -278,4 +294,4 @@ private:
};
} // panda::ecmascript::tooling
#endif // ECMASCRIPT_DEBUGGER_JS_DEBUGGER_MANAGER_H
#endif // ECMASCRIPT_DEBUGGER_JS_DEBUGGER_MANAGER_H

View File

@ -33,6 +33,20 @@ public:
{
}
JSPtLocation(const JSPandaFile *jsPandaFile, EntityId methodId, uint32_t bytecodeOffset,
Global<FunctionRef> &condFuncRef, int32_t line = -1,
int32_t column = -1, const std::string &sourceFile = "",
bool needResetBreakpoint = false, CString recordName = "") : jsPandaFile_(jsPandaFile),
methodId_(methodId), bytecodeOffset_(bytecodeOffset),
condFuncRef_(condFuncRef), line_(line), column_(column),
sourceFile_(sourceFile), needResetBreakpoint_(needResetBreakpoint), recordName_(recordName)
{
}
JSPtLocation(const std::string &url, int32_t line, int32_t column) : line_(line), column_(column), sourceFile_(url)
{
}
const JSPandaFile *GetJsPandaFile() const
{
return jsPandaFile_;
@ -53,6 +67,31 @@ public:
return bytecodeOffset_;
}
const Global<FunctionRef> &GetCondFuncRef() const
{
return condFuncRef_;
}
int32_t GetLine() const
{
return line_;
}
int32_t GetColumn() const
{
return column_;
}
const bool &GetNeedResetBreakpoint() const
{
return needResetBreakpoint_;
}
const CString &GetRecordName() const
{
return recordName_;
}
bool operator==(const JSPtLocation &location) const
{
return methodId_ == location.methodId_ && bytecodeOffset_ == location.bytecodeOffset_ &&
@ -66,7 +105,12 @@ public:
location << "methodId:" << methodId_ << ", ";
location << "bytecodeOffset:" << bytecodeOffset_ << ", ";
location << "sourceFile:" << "\""<< sourceFile_ << "\""<< ", ";
location << "jsPandaFile:" << "\"" << jsPandaFile_->GetJSPandaFileDesc() << "\"";
if (jsPandaFile_ != nullptr) {
location << "jsPandaFile:" << "\"" << jsPandaFile_->GetJSPandaFileDesc() << "\"";
}
location << "line: " << line_ << ", ";
location << "column: " << column_ << ", ";
location << "needResetBreakpoint: " << needResetBreakpoint_;
location << "]";
return location.str();
}
@ -80,8 +124,13 @@ private:
const JSPandaFile *jsPandaFile_ {nullptr};
EntityId methodId_;
uint32_t bytecodeOffset_ {0};
Global<FunctionRef> condFuncRef_;
int32_t line_;
int32_t column_;
std::string sourceFile_; // mainly used for breakpoint
bool needResetBreakpoint_ {false};
CString recordName_ {};
};
} // namespace panda::ecmascript::tooling
#endif // ECMASCRIPT_DEBUGGER_JS_PT_LOCATION_H
#endif // ECMASCRIPT_DEBUGGER_JS_PT_LOCATION_H

View File

@ -93,7 +93,8 @@ public:
template<class Callback>
bool MatchWithLocation(const Callback &cb, int32_t line, int32_t column,
const std::string &url, const std::unordered_set<std::string> &debugRecordName)
const std::string &url, const std::unordered_set<std::string> &debugRecordName,
Global<FunctionRef> funcRef)
{
if (line == SPECIAL_LINE_MARK) {
return false;
@ -146,7 +147,8 @@ public:
for (const auto &pair : columnTable) {
if (pair.offset >= currentOffset && pair.offset < nextOffset) {
if (pair.column == column) {
return cb(JSPtLocation(jsPandaFile_, methodId, pair.offset, url));
return cb(JSPtLocation(jsPandaFile_, methodId, pair.offset, funcRef,
line, column, url));
} else if (pair.column < minColumn && currentOffset < minColumnOffset) {
minColumn = pair.column;
minColumnOffset = currentOffset;
@ -157,10 +159,12 @@ public:
}
}
if (minColumn != INT32_MAX) { // find the smallest column for the corresponding row
return cb(JSPtLocation(jsPandaFile_, minColumnMethodId, minColumnOffset, url));
return cb(JSPtLocation(jsPandaFile_, minColumnMethodId, minColumnOffset, funcRef,
line, column, url));
}
if (currentOffset != UINT32_MAX) { // find corresponding row, but not find corresponding column
return cb(JSPtLocation(jsPandaFile_, currentMethodId, currentOffset, url));
return cb(JSPtLocation(jsPandaFile_, currentMethodId, currentOffset, funcRef,
line, column, url));
}
}
return false;
@ -242,4 +246,4 @@ private:
};
} // namespace panda::ecmascript
#endif // ECMASCRIPT_JSPANDAFILE_DEBUG_INFO_EXTRACTOR_H
#endif // ECMASCRIPT_JSPANDAFILE_DEBUG_INFO_EXTRACTOR_H