mirror of
https://github.com/adelphes/android-dev-ext.git
synced 2025-12-22 17:39:19 +00:00
Version 1.1 improvements (#88)
* fix 0 alignment in binary xml decoding * output reason for APK manifest read failure * try and match package name against process name when determining which pid to attach * make post launch pause user-configurable * code tidy, jsdocs and types * more types in expression parse classes * fix issue with expandable objects not evaluating * update build task example * fix package/type evaluation * improve handling of targetDevice and processID combinations * show full call stack by default * implement a queue for evaluations * improve performance of retrieving single fields * check root term identifiers against this fields
This commit is contained in:
200
src/debugMain.js
200
src/debugMain.js
@@ -11,7 +11,7 @@ const path = require('path');
|
||||
const { ADBClient } = require('./adbclient');
|
||||
const { APKFileInfo } = require('./apk-file-info');
|
||||
const { Debugger } = require('./debugger');
|
||||
const { BreakpointOptions, BuildInfo, DebuggerException, DebuggerValue, JavaBreakpointEvent, JavaClassType, JavaExceptionEvent, SourceLocation } = require('./debugger-types');
|
||||
const { AttachBuildInfo, BreakpointOptions, DebuggerException, DebuggerValue, JavaBreakpointEvent, JavaClassType, JavaExceptionEvent, LaunchBuildInfo, SourceLocation } = require('./debugger-types');
|
||||
const { evaluate } = require('./expression/evaluate');
|
||||
const { PackageInfo } = require('./package-searcher');
|
||||
const ADBSocket = require('./sockets/adbsocket');
|
||||
@@ -77,7 +77,7 @@ class AndroidDebugSession extends DebugSession {
|
||||
this._android_sources_path = '';
|
||||
|
||||
// number of call stack entries to display above the project source
|
||||
this.callStackDisplaySize = 1;
|
||||
this.callStackDisplaySize = 0;
|
||||
|
||||
/**
|
||||
* the fifo queue of evaluations (watches, hover, etc)
|
||||
@@ -118,26 +118,18 @@ class AndroidDebugSession extends DebugSession {
|
||||
/**
|
||||
* The 'initialize' request is the first request called by the frontend
|
||||
* to interrogate the features the debug adapter provides.
|
||||
* @param {import('vscode-debugprotocol').DebugProtocol.InitializeResponse} response
|
||||
*/
|
||||
initializeRequest(response/*: DebugProtocol.InitializeResponse, args: DebugProtocol.InitializeRequestArguments*/) {
|
||||
|
||||
// This debug adapter implements the configurationDoneRequest.
|
||||
response.body.supportsConfigurationDoneRequest = true;
|
||||
|
||||
// we support some exception options
|
||||
initializeRequest(response) {
|
||||
response.body.exceptionBreakpointFilters = [
|
||||
{ label:'All Exceptions', filter:'all', default:false },
|
||||
{ label:'Uncaught Exceptions', filter:'uncaught', default:true },
|
||||
];
|
||||
|
||||
// we support modifying variable values
|
||||
response.body.supportsConfigurationDoneRequest = true;
|
||||
response.body.supportsSetVariable = true;
|
||||
|
||||
// we support hit-count conditional breakpoints
|
||||
response.body.supportsHitConditionalBreakpoints = true;
|
||||
|
||||
// we support the new ExceptionInfoRequest
|
||||
response.body.supportsEvaluateForHovers = true;
|
||||
response.body.supportsExceptionInfoRequest = true;
|
||||
response.body.supportsHitConditionalBreakpoints = true;
|
||||
|
||||
this.sendResponse(response);
|
||||
}
|
||||
@@ -167,6 +159,7 @@ class AndroidDebugSession extends DebugSession {
|
||||
|
||||
/**
|
||||
* @param {string} msg
|
||||
* @param {import('vscode-debugprotocol').DebugProtocol.Response} response
|
||||
* @param {boolean} silent
|
||||
*/
|
||||
failRequest(msg, response, silent = false) {
|
||||
@@ -185,7 +178,7 @@ class AndroidDebugSession extends DebugSession {
|
||||
/**
|
||||
* @param {string} requestName
|
||||
* @param {number} threadId
|
||||
* @param {*} response
|
||||
* @param {import('vscode-debugprotocol').DebugProtocol.Response} response
|
||||
*/
|
||||
failRequestNoThread(requestName, threadId, response) {
|
||||
this.failRequest(`${requestName} failed. Thread ${threadId} not found`, response);
|
||||
@@ -194,7 +187,7 @@ class AndroidDebugSession extends DebugSession {
|
||||
/**
|
||||
* @param {string} requestName
|
||||
* @param {number} threadId
|
||||
* @param {*} response
|
||||
* @param {import('vscode-debugprotocol').DebugProtocol.Response} response
|
||||
*/
|
||||
failRequestThreadNotSuspended(requestName, threadId, response) {
|
||||
this.failRequest(`${requestName} failed. Thread ${threadId} is not suspended`, response);
|
||||
@@ -203,7 +196,7 @@ class AndroidDebugSession extends DebugSession {
|
||||
/**
|
||||
* @param {string} requestName
|
||||
* @param {number} threadId
|
||||
* @param {*} response
|
||||
* @param {import('vscode-debugprotocol').DebugProtocol.Response} response
|
||||
*/
|
||||
cancelRequestThreadNotSuspended(requestName, threadId, response) {
|
||||
// now that vscode can resume threads before the locals,callstack,etc are retrieved, we only need to cancel the request
|
||||
@@ -258,10 +251,11 @@ class AndroidDebugSession extends DebugSession {
|
||||
// configure the thread names
|
||||
threadinfos.forEach(threadinfo => {
|
||||
const thread = this.getThread(threadinfo.threadid);
|
||||
if (thread.name === null) {
|
||||
if (typeof thread.name !== 'string') {
|
||||
thread.name = threadinfo.name;
|
||||
} else if (thread.name !== threadinfo.name) {
|
||||
// give the thread a new id for VS code
|
||||
// - note: this will invalidate all current variable references for this thread
|
||||
delete this._threads[thread.vscode_threadid];
|
||||
thread.allocateNewThreadID();
|
||||
this._threads[thread.vscode_threadid] = thread;
|
||||
@@ -321,6 +315,17 @@ class AndroidDebugSession extends DebugSession {
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef AndroidAttachArguments
|
||||
* @property {string} appSrcRoot
|
||||
* @property {boolean} autoStartADB
|
||||
* @property {number} processId
|
||||
* @property {string} targetDevice
|
||||
* @property {boolean} trace
|
||||
*
|
||||
* @param {import('vscode-debugprotocol').DebugProtocol.AttachResponse} response
|
||||
* @param {import('vscode-debugprotocol').DebugProtocol.AttachRequestArguments & AndroidAttachArguments} args
|
||||
*/
|
||||
async attachRequest(response, args) {
|
||||
this.debug_mode = 'attach';
|
||||
if (args && args.trace) {
|
||||
@@ -382,7 +387,7 @@ class AndroidDebugSession extends DebugSession {
|
||||
// try and determine the relevant path for the API sources (based upon the API level of the connected device)
|
||||
await this.configureAPISourcePath();
|
||||
|
||||
const build = new BuildInfo(null, new Map(this.src_packages.packages), null);
|
||||
const build = new AttachBuildInfo(new Map(this.src_packages.packages));
|
||||
this.LOG(`Attaching to pid ${processId} on device ${this._device.serial} [API:${this.device_api_level||'?'}]`);
|
||||
|
||||
// try and attach to the specified pid
|
||||
@@ -427,12 +432,28 @@ class AndroidDebugSession extends DebugSession {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @typedef AndroidLaunchArguments
|
||||
* @property {number} adbPort
|
||||
* @property {string[]} amStartArgs
|
||||
* @property {string} apkFile
|
||||
* @property {string} appSrcRoot
|
||||
* @property {boolean} autoStartADB
|
||||
* @property {number} callStackDisplaySize
|
||||
* @property {string} launchActivity
|
||||
* @property {string} manifestFile
|
||||
* @property {string[]} pmInstallArgs
|
||||
* @property {number} postLaunchPause
|
||||
* @property {number} processId
|
||||
* @property {StaleBuildSetting} staleBuild
|
||||
* @property {string} targetDevice
|
||||
* @property {boolean} trace
|
||||
|
||||
* The entry point to the debugger
|
||||
* @param {*} response
|
||||
* @param {*} args
|
||||
* @param {import('vscode-debugprotocol').DebugProtocol.LaunchResponse} response
|
||||
* @param {import('vscode-debugprotocol').DebugProtocol.LaunchRequestArguments & AndroidLaunchArguments} args
|
||||
*/
|
||||
async launchRequest(response/*: DebugProtocol.LaunchResponse*/, args/*: LaunchRequestArguments*/) {
|
||||
async launchRequest(response, args) {
|
||||
this.debug_mode = 'launch';
|
||||
if (args && args.trace) {
|
||||
this.trace = args.trace;
|
||||
@@ -512,7 +533,7 @@ class AndroidDebugSession extends DebugSession {
|
||||
await this.configureAPISourcePath();
|
||||
|
||||
// launch the app
|
||||
await this.startLaunchActivity(args.launchActivity);
|
||||
await this.startLaunchActivity(args.launchActivity, args.postLaunchPause);
|
||||
|
||||
this.debuggerAttached = true;
|
||||
|
||||
@@ -552,6 +573,10 @@ class AndroidDebugSession extends DebugSession {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the build is out of date (i.e a source file has been modified since the last build)
|
||||
* @param {StaleBuildSetting} staleBuild
|
||||
*/
|
||||
checkBuildIsUpToDate(staleBuild) {
|
||||
// check if any source file was modified after the apk
|
||||
if (this.src_packages.last_src_modified >= this.apk_file_info.app_modified) {
|
||||
@@ -564,7 +589,12 @@ class AndroidDebugSession extends DebugSession {
|
||||
}
|
||||
}
|
||||
|
||||
async startLaunchActivity(launchActivity) {
|
||||
/**
|
||||
*
|
||||
* @param {string} launchActivity
|
||||
* @param {number} postLaunchPause
|
||||
*/
|
||||
async startLaunchActivity(launchActivity, postLaunchPause) {
|
||||
if (!launchActivity) {
|
||||
// we're allowed no launchActivity if we have a custom am start command
|
||||
if (!this.am_start_args) {
|
||||
@@ -574,7 +604,12 @@ class AndroidDebugSession extends DebugSession {
|
||||
}
|
||||
}
|
||||
|
||||
const build = new BuildInfo(this.apk_file_info.manifest.package, new Map(this.src_packages.packages), launchActivity, this.am_start_args);
|
||||
const build = new LaunchBuildInfo(
|
||||
new Map(this.src_packages.packages),
|
||||
this.apk_file_info.manifest.package,
|
||||
launchActivity,
|
||||
this.am_start_args,
|
||||
postLaunchPause);
|
||||
|
||||
this.LOG(`Launching on device ${this._device.serial} [API:${this.device_api_level||'?'}]`);
|
||||
if (this.am_start_args) {
|
||||
@@ -686,7 +721,11 @@ class AndroidDebugSession extends DebugSession {
|
||||
throw new Error(reject);
|
||||
}
|
||||
|
||||
configurationDoneRequest(response/*, args*/) {
|
||||
/**
|
||||
*
|
||||
* @param {import('vscode-debugprotocol').DebugProtocol.ConfigurationDoneResponse} response
|
||||
*/
|
||||
configurationDoneRequest(response) {
|
||||
D('configurationDoneRequest');
|
||||
this.waitForConfigurationDone();
|
||||
this.sendResponse(response);
|
||||
@@ -702,7 +741,11 @@ class AndroidDebugSession extends DebugSession {
|
||||
}
|
||||
}
|
||||
|
||||
async disconnectRequest(response/*, args*/) {
|
||||
/**
|
||||
*
|
||||
* @param {import('vscode-debugprotocol').DebugProtocol.DisconnectResponse} response
|
||||
*/
|
||||
async disconnectRequest(response) {
|
||||
D('disconnectRequest');
|
||||
this._isDisconnecting = true;
|
||||
if (this.debuggerAttached) {
|
||||
@@ -733,7 +776,7 @@ class AndroidDebugSession extends DebugSession {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Called by the debugger in response to a JDWP breakpoint hit event
|
||||
* @param {JavaBreakpointEvent} e
|
||||
*/
|
||||
onBreakpointHit(e) {
|
||||
@@ -745,13 +788,15 @@ class AndroidDebugSession extends DebugSession {
|
||||
/**
|
||||
* Called when the user requests a change to breakpoints in a source file
|
||||
* Note: all breakpoints in a file are always sent in args, even if they are not changing
|
||||
* @param {import('vscode-debugprotocol').DebugProtocol.SetBreakpointsResponse} response
|
||||
* @param {import('vscode-debugprotocol').DebugProtocol.SetBreakpointsArguments} args
|
||||
*/
|
||||
async setBreakPointsRequest(response/*: DebugProtocol.SetBreakpointsResponse*/, args/*: DebugProtocol.SetBreakpointsArguments*/) {
|
||||
async setBreakPointsRequest(response, args) {
|
||||
const source_filename = args.source && args.source.path;
|
||||
D('setBreakPointsRequest: ' + source_filename);
|
||||
|
||||
const unverified_breakpoint = (src_bp,reason) => {
|
||||
const bp = new Breakpoint(false,src_bp.line);
|
||||
const bp = new Breakpoint(false, src_bp.line);
|
||||
bp['id'] = ++this._breakpointId;
|
||||
bp['message'] = reason;
|
||||
return bp;
|
||||
@@ -814,8 +859,7 @@ class AndroidDebugSession extends DebugSession {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*[]} breakpoints
|
||||
* @param {import('vscode-debugprotocol').DebugProtocol.SourceBreakpoint[]} breakpoints
|
||||
* @param {string} relative_fpn
|
||||
*/
|
||||
async setupBreakpointsInFile(breakpoints, relative_fpn) {
|
||||
@@ -852,7 +896,11 @@ class AndroidDebugSession extends DebugSession {
|
||||
return java_breakpoints;
|
||||
};
|
||||
|
||||
async setExceptionBreakPointsRequest(response /*: SetExceptionBreakpointsResponse*/, args /*: SetExceptionBreakpointsArguments*/) {
|
||||
/**
|
||||
* @param {import('vscode-debugprotocol').DebugProtocol.SetExceptionBreakpointsResponse} response
|
||||
* @param {import('vscode-debugprotocol').DebugProtocol.SetExceptionBreakpointsArguments} args
|
||||
*/
|
||||
async setExceptionBreakPointsRequest(response, args) {
|
||||
await this.dbgr.clearBreakOnExceptions();
|
||||
switch(true) {
|
||||
case args.filters.includes('all'):
|
||||
@@ -865,7 +913,11 @@ class AndroidDebugSession extends DebugSession {
|
||||
this.sendResponse(response);
|
||||
}
|
||||
|
||||
async threadsRequest(response/*: DebugProtocol.ThreadsResponse*/) {
|
||||
/**
|
||||
*
|
||||
* @param {import('vscode-debugprotocol').DebugProtocol.ThreadsResponse} response
|
||||
*/
|
||||
async threadsRequest(response) {
|
||||
if (!this._threads.length) {
|
||||
try {
|
||||
await this.refreshThreads();
|
||||
@@ -889,8 +941,10 @@ class AndroidDebugSession extends DebugSession {
|
||||
|
||||
/**
|
||||
* Returns a stack trace for the given threadId
|
||||
* @param {import('vscode-debugprotocol').DebugProtocol.StackTraceResponse} response
|
||||
* @param {import('vscode-debugprotocol').DebugProtocol.StackTraceArguments} args
|
||||
*/
|
||||
async stackTraceRequest(response/*: DebugProtocol.StackTraceResponse*/, args/*: DebugProtocol.StackTraceArguments*/) {
|
||||
async stackTraceRequest(response, args) {
|
||||
D(`stackTraceRequest thread:${args.threadId}`);
|
||||
// only retrieve the stack if the thread is paused
|
||||
const thread = this.getThread(args.threadId);
|
||||
@@ -972,7 +1026,11 @@ class AndroidDebugSession extends DebugSession {
|
||||
}
|
||||
}
|
||||
|
||||
async scopesRequest(response/*: DebugProtocol.ScopesResponse*/, args/*: DebugProtocol.ScopesArguments*/) {
|
||||
/**
|
||||
* @param {import('vscode-debugprotocol').DebugProtocol.ScopesResponse} response
|
||||
* @param {import('vscode-debugprotocol').DebugProtocol.ScopesArguments} args
|
||||
*/
|
||||
async scopesRequest(response, args) {
|
||||
D(`scopesRequest frame:${args.frameId}`);
|
||||
const threadId = AndroidThread.variableRefToThreadId(args.frameId);
|
||||
const thread = this.getThread(threadId);
|
||||
@@ -998,10 +1056,14 @@ class AndroidDebugSession extends DebugSession {
|
||||
} catch(e) {
|
||||
}
|
||||
this.sendResponse(response);
|
||||
}
|
||||
}
|
||||
|
||||
sourceRequest(response/*: DebugProtocol.SourceResponse*/, args/*: DebugProtocol.SourceArguments*/) {
|
||||
D(`sourceRequest: ${args.sourceId}`);
|
||||
/**
|
||||
* @param {import('vscode-debugprotocol').DebugProtocol.SourceResponse} response
|
||||
* @param {import('vscode-debugprotocol').DebugProtocol.SourceArguments} args
|
||||
*/
|
||||
sourceRequest(response, args) {
|
||||
D(`sourceRequest: ${args.sourceReference}`);
|
||||
const content =
|
||||
`/*
|
||||
The source for this class is unavailable.
|
||||
@@ -1021,11 +1083,10 @@ class AndroidDebugSession extends DebugSession {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} response
|
||||
* @param {{variablesReference:VSCVariableReference}} args
|
||||
* @param {import('vscode-debugprotocol').DebugProtocol.VariablesResponse} response
|
||||
* @param {import('vscode-debugprotocol').DebugProtocol.VariablesArguments} args
|
||||
*/
|
||||
async variablesRequest(response/*: DebugProtocol.VariablesResponse*/, args/*: DebugProtocol.VariablesArguments*/) {
|
||||
async variablesRequest(response, args) {
|
||||
D(`variablesRequest variablesReference:${args.variablesReference}`);
|
||||
const threadId = AndroidThread.variableRefToThreadId(args.variablesReference);
|
||||
const thread = this.getThread(threadId);
|
||||
@@ -1109,7 +1170,11 @@ class AndroidDebugSession extends DebugSession {
|
||||
await this.dbgr.resumeThread(thread.threadid);
|
||||
}
|
||||
|
||||
continueRequest(response/*: DebugProtocol.ContinueResponse*/, args/*: DebugProtocol.ContinueArguments*/) {
|
||||
/**
|
||||
* @param {import('vscode-debugprotocol').DebugProtocol.ContinueResponse} response
|
||||
* @param {import('vscode-debugprotocol').DebugProtocol.ContinueArguments} args
|
||||
*/
|
||||
continueRequest(response, args) {
|
||||
D(`Continue thread:${args.threadId}`);
|
||||
|
||||
const thread = this.getThread(args.threadId);
|
||||
@@ -1132,8 +1197,8 @@ class AndroidDebugSession extends DebugSession {
|
||||
/**
|
||||
* Called by the user to start a step operation
|
||||
* @param {DebuggerStepType} which
|
||||
* @param {*} response
|
||||
* @param {*} args
|
||||
* @param {import('vscode-debugprotocol').DebugProtocol.NextResponse} response
|
||||
* @param {import('vscode-debugprotocol').DebugProtocol.NextArguments} args
|
||||
*/
|
||||
doStep(which, response, args) {
|
||||
D(`step ${which}`);
|
||||
@@ -1156,15 +1221,27 @@ class AndroidDebugSession extends DebugSession {
|
||||
this.dbgr.step(which, thread.threadid);
|
||||
}
|
||||
|
||||
stepInRequest(response/*: DebugProtocol.NextResponse*/, args/*: DebugProtocol.StepInArguments*/) {
|
||||
/**
|
||||
* @param {import('vscode-debugprotocol').DebugProtocol.NextResponse} response
|
||||
* @param {import('vscode-debugprotocol').DebugProtocol.StepInArguments} args
|
||||
*/
|
||||
stepInRequest(response, args) {
|
||||
this.doStep('in', response, args);
|
||||
}
|
||||
|
||||
nextRequest(response/*: DebugProtocol.NextResponse*/, args/*: DebugProtocol.NextArguments*/) {
|
||||
/**
|
||||
* @param {import('vscode-debugprotocol').DebugProtocol.NextResponse} response
|
||||
* @param {import('vscode-debugprotocol').DebugProtocol.NextArguments} args
|
||||
*/
|
||||
nextRequest(response, args) {
|
||||
this.doStep('over', response, args);
|
||||
}
|
||||
|
||||
stepOutRequest(response/*: DebugProtocol.NextResponse*/, args/*: DebugProtocol.StepOutArguments*/) {
|
||||
/**
|
||||
* @param {import('vscode-debugprotocol').DebugProtocol.NextResponse} response
|
||||
* @param {import('vscode-debugprotocol').DebugProtocol.StepOutArguments} args
|
||||
*/
|
||||
stepOutRequest(response, args) {
|
||||
this.doStep('out', response, args);
|
||||
}
|
||||
|
||||
@@ -1182,7 +1259,11 @@ class AndroidDebugSession extends DebugSession {
|
||||
this.reportStoppedEvent("exception", e.throwlocation, last_exception);
|
||||
}
|
||||
|
||||
async exceptionInfoRequest(response /*DebugProtocol.ExceptionInfoResponse*/, args /**/) {
|
||||
/**
|
||||
* @param {import('vscode-debugprotocol').DebugProtocol.ExceptionInfoResponse} response
|
||||
* @param {import('vscode-debugprotocol').DebugProtocol.ExceptionInfoArguments} args
|
||||
*/
|
||||
async exceptionInfoRequest(response, args) {
|
||||
D(`exceptionInfoRequest: ${args.threadId}`);
|
||||
const thread = this.getThread(args.threadId);
|
||||
if (!thread) return this.failRequestNoThread('Exception info', args.threadId, response);
|
||||
@@ -1252,15 +1333,10 @@ class AndroidDebugSession extends DebugSession {
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef SetVariableArgs
|
||||
* @property {string} name
|
||||
* @property {string} value
|
||||
* @property {number} variablesReference
|
||||
*
|
||||
* @param {*} response
|
||||
* @param {SetVariableArgs} args
|
||||
* @param {import('vscode-debugprotocol').DebugProtocol.SetVariableResponse} response
|
||||
* @param {import('vscode-debugprotocol').DebugProtocol.SetVariableArguments} args
|
||||
*/
|
||||
async setVariableRequest(response/*: DebugProtocol.SetVariableResponse*/, args/*: DebugProtocol.SetVariableArguments*/) {
|
||||
async setVariableRequest(response, args) {
|
||||
|
||||
const threadId = AndroidThread.variableRefToThreadId(args.variablesReference);
|
||||
const thread = this.getThread(threadId);
|
||||
@@ -1289,8 +1365,10 @@ class AndroidDebugSession extends DebugSession {
|
||||
|
||||
/**
|
||||
* Called by VSCode to perform watch, console and hover evaluations
|
||||
* @param {import('vscode-debugprotocol').DebugProtocol.EvaluateResponse} response
|
||||
* @param {import('vscode-debugprotocol').DebugProtocol.EvaluateArguments} args
|
||||
*/
|
||||
async evaluateRequest(response/*: DebugProtocol.EvaluateResponse*/, args/*: DebugProtocol.EvaluateArguments*/) {
|
||||
async evaluateRequest(response, args) {
|
||||
|
||||
// Some notes to remember:
|
||||
// annoyingly, during stepping, the step can complete before the resume has called evaluateRequest on watches.
|
||||
|
||||
Reference in New Issue
Block a user