mirror of
https://github.com/adelphes/android-dev-ext.git
synced 2025-12-23 09:59:25 +00:00
Android debugger class enhancements
Support for thread info - name and status Reference count global suspends to allow correct resuming when multiple events are triggered. Added methods for suspend/resume individual threads
This commit is contained in:
@@ -141,6 +141,7 @@ Debugger.prototype = {
|
|||||||
cpfilters: [],
|
cpfilters: [],
|
||||||
preparedclasses: [],
|
preparedclasses: [],
|
||||||
stepids: {}, // hashmap<threadid,stepid>
|
stepids: {}, // hashmap<threadid,stepid>
|
||||||
|
suspendcount: 0, // refcount of suspend-all-threads
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
@@ -415,6 +416,35 @@ Debugger.prototype = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
threadinfos: function(thread_ids, extra) {
|
||||||
|
if (!Array.isArray(thread_ids))
|
||||||
|
thread_ids = [thread_ids];
|
||||||
|
var o = {
|
||||||
|
dbgr: this, thread_ids, extra, threadinfos:[], idx:0,
|
||||||
|
next() {
|
||||||
|
var thread_id = this.thread_ids[this.idx];
|
||||||
|
if (typeof(thread_id) === 'undefined')
|
||||||
|
return $.Deferred().resolveWith(this.dbgr, [this.threadinfos, this.extra]);
|
||||||
|
var info = {
|
||||||
|
threadid: thread_id,
|
||||||
|
name:'',
|
||||||
|
status:null,
|
||||||
|
};
|
||||||
|
return this.dbgr.session.adbclient.jdwp_command({ ths:this.dbgr, extra:info, cmd:this.dbgr.JDWP.Commands.threadname(info.threadid) })
|
||||||
|
.then((name,info) => {
|
||||||
|
info.name = name;
|
||||||
|
return this.dbgr.session.adbclient.jdwp_command({ ths:this.dbgr, extra:info, cmd:this.dbgr.JDWP.Commands.threadstatus(info.threadid) })
|
||||||
|
})
|
||||||
|
.then((status, info) => {
|
||||||
|
info.status = status;
|
||||||
|
this.threadinfos.push(info);
|
||||||
|
})
|
||||||
|
.always(() => (this.idx++,this.next()))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return this.ensureconnected(o).then(o => o.next());
|
||||||
|
},
|
||||||
|
|
||||||
suspend: function (extra) {
|
suspend: function (extra) {
|
||||||
return this.ensureconnected(extra)
|
return this.ensureconnected(extra)
|
||||||
.then(function (extra) {
|
.then(function (extra) {
|
||||||
@@ -426,37 +456,67 @@ Debugger.prototype = {
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
.then(function () {
|
.then(function () {
|
||||||
|
this.session.suspendcount++;
|
||||||
this._trigger('suspended');
|
this._trigger('suspended');
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
resume: function (extra) {
|
suspendthread: function (threadid, extra) {
|
||||||
return this.ensureconnected(extra)
|
return this.ensureconnected(extra)
|
||||||
.then(function (extra) {
|
.then(function (extra) {
|
||||||
this._trigger('resuming');
|
return this.session.adbclient.jdwp_command({
|
||||||
this.session.stoppedlocation = null;
|
ths: this,
|
||||||
|
extra: extra,
|
||||||
|
cmd: this.JDWP.Commands.suspendthread(threadid),
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then((res,extra) => extra);
|
||||||
|
},
|
||||||
|
|
||||||
|
_resume:function(triggers, extra) {
|
||||||
|
return this.ensureconnected(extra)
|
||||||
|
.then(function (extra) {
|
||||||
|
if (triggers) this._trigger('resuming');
|
||||||
|
const resume_cmd = (decoded,extra) => {
|
||||||
return this.session.adbclient.jdwp_command({
|
return this.session.adbclient.jdwp_command({
|
||||||
ths: this,
|
ths: this,
|
||||||
extra: extra,
|
extra: extra,
|
||||||
cmd: this.JDWP.Commands.resume(),
|
cmd: this.JDWP.Commands.resume(),
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
// we must resume with the same number of suspends
|
||||||
|
var def = resume_cmd(null, extra);
|
||||||
|
for (var i=1; i < this.session.suspendcount; i++) {
|
||||||
|
def = def.then(resume_cmd);
|
||||||
|
}
|
||||||
|
this.session.stoppedlocation = null;
|
||||||
|
this.session.suspendcount = 0;
|
||||||
|
return def;
|
||||||
})
|
})
|
||||||
.then(function (decoded, extra) {
|
.then(function (decoded, extra) {
|
||||||
this._trigger('resumed');
|
if (triggers) this._trigger('resumed');
|
||||||
return extra;
|
return extra;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
resume: function (extra) {
|
||||||
|
return this._resume(true, extra);
|
||||||
|
},
|
||||||
|
|
||||||
_resumesilent: function () {
|
_resumesilent: function () {
|
||||||
return this.ensureconnected()
|
return this._resume(false);
|
||||||
.then(function () {
|
},
|
||||||
this.session.stoppedlocation = null;
|
|
||||||
|
resumethread: function (threadid, extra) {
|
||||||
|
return this.ensureconnected(extra)
|
||||||
|
.then(function (extra) {
|
||||||
return this.session.adbclient.jdwp_command({
|
return this.session.adbclient.jdwp_command({
|
||||||
ths: this,
|
ths: this,
|
||||||
//extra: extra,
|
extra: extra,
|
||||||
cmd: this.JDWP.Commands.resume(),
|
cmd: this.JDWP.Commands.resumethread(threadid),
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
})
|
||||||
|
.then((res,extra) => extra);
|
||||||
},
|
},
|
||||||
|
|
||||||
step: function (steptype, threadid) {
|
step: function (steptype, threadid) {
|
||||||
@@ -1311,6 +1371,8 @@ Debugger.prototype = {
|
|||||||
},
|
},
|
||||||
fn: function (e) {
|
fn: function (e) {
|
||||||
var x = e.data;
|
var x = e.data;
|
||||||
|
// each class prepare contributes a global suspend
|
||||||
|
x.dbgr.session.suspendcount++;
|
||||||
x.onprepare.apply(x.dbgr, [e.event]);
|
x.onprepare.apply(x.dbgr, [e.event]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -1339,6 +1401,8 @@ Debugger.prototype = {
|
|||||||
dbgr: this,
|
dbgr: this,
|
||||||
},
|
},
|
||||||
fn: function (e) {
|
fn: function (e) {
|
||||||
|
// each step hit contributes a global suspend
|
||||||
|
e.data.dbgr.session.suspendcount++;
|
||||||
e.data.dbgr._clearLastStepRequest(e.event.threadid, e)
|
e.data.dbgr._clearLastStepRequest(e.event.threadid, e)
|
||||||
.then(function (e) {
|
.then(function (e) {
|
||||||
var x = e.data;
|
var x = e.data;
|
||||||
@@ -1392,6 +1456,8 @@ Debugger.prototype = {
|
|||||||
bp: x.dbgr.breakpoints.enabled[cmlkey].bp,
|
bp: x.dbgr.breakpoints.enabled[cmlkey].bp,
|
||||||
};
|
};
|
||||||
x.dbgr.session.stoppedlocation = stoppedloc;
|
x.dbgr.session.stoppedlocation = stoppedloc;
|
||||||
|
// each breakpoint hit contributes a global suspend
|
||||||
|
x.dbgr.session.suspendcount++;
|
||||||
// if this was a conditional breakpoint, it will have been automatically cleared
|
// if this was a conditional breakpoint, it will have been automatically cleared
|
||||||
// - set a new (unconditional) breakpoint in it's place
|
// - set a new (unconditional) breakpoint in it's place
|
||||||
if (bp.conditions.hitcount) {
|
if (bp.conditions.hitcount) {
|
||||||
@@ -1554,6 +1620,8 @@ Debugger.prototype = {
|
|||||||
dbgr: this,
|
dbgr: this,
|
||||||
},
|
},
|
||||||
fn: function (e) {
|
fn: function (e) {
|
||||||
|
// each exception hit contributes a global suspend
|
||||||
|
x.dbgr.session.suspendcount++;
|
||||||
// if this exception break occurred during a step request, we must manually clear the event
|
// if this exception break occurred during a step request, we must manually clear the event
|
||||||
// or the (device-side) debugger will crash on next step
|
// or the (device-side) debugger will crash on next step
|
||||||
this._clearLastStepRequest(e.event.threadid, e).then(e => {
|
this._clearLastStepRequest(e.event.threadid, e).then(e => {
|
||||||
|
|||||||
56
src/jdwp.js
56
src/jdwp.js
@@ -250,6 +250,12 @@ function _JDWP() {
|
|||||||
decodeStatus : function(o) {
|
decodeStatus : function(o) {
|
||||||
return this.mapflags(this.decodeInt(o), ['verified','prepared','initialized','error']);
|
return this.mapflags(this.decodeInt(o), ['verified','prepared','initialized','error']);
|
||||||
},
|
},
|
||||||
|
decodeThreadStatus : function(o) {
|
||||||
|
return ['zombie','running','sleeping','monitor','wait'][this.decodeInt(o)] || '';
|
||||||
|
},
|
||||||
|
decodeSuspendStatus : function(o) {
|
||||||
|
return this.decodeInt(o) ? 'suspended': '';
|
||||||
|
},
|
||||||
decodeTaggedObjectID : function(o) {
|
decodeTaggedObjectID : function(o) {
|
||||||
return this.decodeValue(o);
|
return this.decodeValue(o);
|
||||||
},
|
},
|
||||||
@@ -721,6 +727,7 @@ function _JDWP() {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
// nestedTypes is not implemented on android
|
||||||
nestedTypes:function(ci) {
|
nestedTypes:function(ci) {
|
||||||
return new Command('NestedTypes:'+ci.name, 2, 8,
|
return new Command('NestedTypes:'+ci.name, 2, 8,
|
||||||
function() {
|
function() {
|
||||||
@@ -733,7 +740,7 @@ function _JDWP() {
|
|||||||
var arrlen = DataCoder.decodeInt(o);
|
var arrlen = DataCoder.decodeInt(o);
|
||||||
while (--arrlen>=0) {
|
while (--arrlen>=0) {
|
||||||
var v = DataCoder.decodeList(o, [{reftype:'reftype'},{typeid:'tref'}]);
|
var v = DataCoder.decodeList(o, [{reftype:'reftype'},{typeid:'tref'}]);
|
||||||
res.vars.push(v);
|
res.push(v);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@@ -1163,6 +1170,26 @@ function _JDWP() {
|
|||||||
resume:function() {
|
resume:function() {
|
||||||
return new Command('resume',1, 9, null, null);
|
return new Command('resume',1, 9, null, null);
|
||||||
},
|
},
|
||||||
|
suspendthread:function(threadid) {
|
||||||
|
return new Command('suspendthread:'+threadid,11, 2,
|
||||||
|
function() {
|
||||||
|
var res = [];
|
||||||
|
DataCoder.encodeRef(res, this);
|
||||||
|
return res;
|
||||||
|
}.bind(threadid),
|
||||||
|
null
|
||||||
|
);
|
||||||
|
},
|
||||||
|
resumethread:function(threadid) {
|
||||||
|
return new Command('resumethread:'+threadid,11, 3,
|
||||||
|
function() {
|
||||||
|
var res = [];
|
||||||
|
DataCoder.encodeRef(res, this);
|
||||||
|
return res;
|
||||||
|
}.bind(threadid),
|
||||||
|
null
|
||||||
|
);
|
||||||
|
},
|
||||||
allthreads:function() {
|
allthreads:function() {
|
||||||
return new Command('allthreads',1, 4,
|
return new Command('allthreads',1, 4,
|
||||||
null,
|
null,
|
||||||
@@ -1175,7 +1202,34 @@ function _JDWP() {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
},
|
||||||
|
threadname:function(threadid) {
|
||||||
|
return new Command('threadname',11,1,
|
||||||
|
function() {
|
||||||
|
var res=[];
|
||||||
|
DataCoder.encodeRef(res, this);
|
||||||
|
return res;
|
||||||
|
}.bind(threadid),
|
||||||
|
function(o) {
|
||||||
|
return DataCoder.decodeString(o);
|
||||||
}
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
threadstatus:function(threadid) {
|
||||||
|
return new Command('threadstatus',11,4,
|
||||||
|
function() {
|
||||||
|
var res=[];
|
||||||
|
DataCoder.encodeRef(res, this);
|
||||||
|
return res;
|
||||||
|
}.bind(threadid),
|
||||||
|
function(o) {
|
||||||
|
return {
|
||||||
|
thread: DataCoder.decodeThreadStatus(o),
|
||||||
|
suspend: DataCoder.decodeSuspendStatus(o),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user