4 Commits

Author SHA1 Message Date
Dave Holoway
b5701dae7d identify breakpoint source locations by package and filename instead of type name 2021-05-30 19:31:33 +01:00
Dave Holoway
33dd93da0c version 1.3.2 2020-07-21 21:01:11 +01:00
Dave Holoway
05b3877bcb update analytics (#99)
* replace mixpanel with amplitude

* remove unused uuid require

* include partial count fields to allow easier analysis

* enable real logging

* record extension deactivation
2020-07-21 20:47:37 +01:00
dependabot[bot]
d15a7bd911 Bump lodash from 4.17.15 to 4.17.19 (#97)
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.15 to 4.17.19.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.15...4.17.19)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-07-16 18:29:21 +01:00
14 changed files with 121 additions and 130 deletions

View File

@@ -1,5 +1,9 @@
# Change Log
### version 1.3.2
* Update analytics library
* Update lodash version - security advisory https://www.npmjs.com/advisories/1523
### version 1.3.0
* Support `ADB_SERVER_SOCKET`, `ANDROID_ADB_SERVER_ADDRESS` & `ANDROID_ADB_SERVER_PORT` env vars when connecting to ADB.
* Replace `adbPort` configuration option with a new `adbSocket` value to allow ADB server host to be overidden. (`adbPort` is now deprecated).

View File

@@ -12,8 +12,11 @@ const { selectTargetDevice } = require('./src/utils/device');
/**
* @param {vscode.ExtensionContext} context
* @param {string} uid
* @param {number} session_id
* @param {*} vscode_props
*/
async function createLanguageClient(context) {
async function createLanguageClient(context, uid, session_id, vscode_props) {
// The server is implemented in node
let serverModule = context.asAbsolutePath(path.join('langserver', 'server.js'));
// The debug options for the server
@@ -45,8 +48,6 @@ async function createLanguageClient(context) {
}
const sourceFiles = (await vscode.workspace.findFiles(`${globSearchRoot}**/*.java`, null, 1000, null)).map(uri => uri.toString());
const mpids = analytics.getIDs(context);
// Options to control the language client
/** @type {import('vscode-languageclient').LanguageClientOptions} */
let clientOptions = {
@@ -57,11 +58,11 @@ async function createLanguageClient(context) {
initializationOptions: {
// extensionPath points to the root of the extension (the folder where this file is)
extensionPath: context.extensionPath,
mpuid: mpids.uid,
mpsid: mpids.sid,
uid,
session_id,
vscode_props,
initialSettings: config,
sourceFiles,
vscodeVersion: vscode.version,
workspaceFolders: (vscode.workspace.workspaceFolders || []).map(z => z.uri.toString()),
},
synchronize: {
@@ -114,10 +115,18 @@ function activate(context) {
/* Only the logcat stuff is configured here. The debugger is launched from src/debugMain.js */
AndroidContentProvider.register(context, vscode.workspace);
const mpids = analytics.getIDs(context);
analytics.init(undefined, mpids.uid, mpids.sid, package_json, { vscode_version: vscode.version });
const { uid } = analytics.getIDs(context);
const session_id = Math.trunc(Math.random() * Number.MAX_SAFE_INTEGER);
const vscode_props = {
appName: vscode.env.appName,
language: vscode.env.language,
shell: vscode.env.shell,
uiKind: vscode.env.uiKind,
vscode_version: vscode.version,
}
analytics.init(undefined, uid, session_id, '', package_json, vscode_props, 'extension-start');
createLanguageClient(context).then(client => {
createLanguageClient(context, uid, session_id, vscode_props).then(client => {
languageClient = client;
refreshLanguageServerEnabledState();
});
@@ -172,6 +181,7 @@ function activate(context) {
// this method is called when your extension is deactivated
function deactivate() {
analytics.event('extension-deactivate');
}
exports.activate = activate;

View File

@@ -1,43 +1,53 @@
const os = require('os');
let mp;
const uuid = require('uuid').v4;
let client;
/** @type {string} */
let uid;
/** @type {string} */
let sid;
let did = uuid();
/** @type {number} */
let session_id;
/** @type {Map<string,[number,number]>} */
const timeLabels = new Map();
let session_start = Date.now();
/** @type {string|Promise<string>} */
let ip = getCurrentIP()
.catch(() => null)
.then(res => ip = res);
let ip = '';
let queued_events = null;
let package_info = null;
let vscode_info = null;
/**
* @param {string} [t]
* @param {string} [u]
* @param {string} [s]
* @param {{name:string,version:string}} [package_json]
* @param {*} [props]
* @param {string} u
* @param {number} s
* @param {string} ipaddr
* @param {{name:string,version:string}} package_json
* @param {*} vscode_props
* @param {string} caller
*/
function init(t = '0cca95950055c6553804a46ce7e3df18', u, s, package_json, props) {
if (mp) {
function init(t = '94635b4642d80407accd3739fa35bed6', u, s, ipaddr, package_json, vscode_props, caller) {
if (client) {
return;
}
try {
mp = require('mixpanel').init(t);
client = require('@amplitude/node').init(t);
}
catch {
return;
}
uid = u;
sid = s;
session_id = s || Math.trunc(Math.random() * Number.MAX_SAFE_INTEGER);
ip = ipaddr || (getCurrentIP()
.catch(() => '')
.then(res => ip = res));
package_info = package_json;
vscode_info = vscode_props;
if (!props) {
if (!caller) {
return;
}
const now = new Date();
event(`${package_json.name}-start`, {
event(caller, {
extension: package_json.name,
ext_version: package_json.version,
arch: process.arch,
@@ -48,7 +58,7 @@ function init(t = '0cca95950055c6553804a46ce7e3df18', u, s, package_json, props)
release: os.release(),
localtime: now.toTimeString(),
tz: now.getTimezoneOffset(),
...props
...vscode_props,
});
}
@@ -69,7 +79,7 @@ function getCurrentIP() {
* @param {*} [properties]
*/
function event(eventName, properties) {
if (!mp) {
if (!client || !eventName || (!uid && !did) || !ip) {
return;
}
if (queued_events) {
@@ -86,23 +96,21 @@ function event(eventName, properties) {
return;
}
try {
if (uid) {
mp.track(eventName, {
ip,
distinct_id: uid,
session_id: sid,
client.logEvent({
event_type: eventName,
user_id: uid,
device_id: uid ? undefined : did,
app_version: package_info.version,
ip,
language: vscode_info.language,
os_name: process.platform,
os_version: os.release(),
session_id,
event_properties: {
session_length: Math.trunc((Date.now() - session_start) / 60e3),
...properties,
});
} else {
mp.track(eventName, {
ip,
platform: process.platform,
release: os.release(),
node_version: process.version,
...properties,
});
}
}
});
} catch {}
}
@@ -149,29 +157,18 @@ function timeEnd(label, time_unit = 'ms', additionalProps = {}) {
function getIDs(context) {
if (!context || !context.globalState) {
return {
uid: '', sid: ''
uid: '',
};
}
let uuidv4 = () => {
try {
uuidv4 = require('uuid').v4;
return uuidv4();
} catch {
return '';
}
}
let u = uid || (uid = context.globalState.get('mix-panel-id'));
if (typeof u !== 'string' || u.length > 36) {
u = uid = uuidv4();
u = uid = uuid();
context.globalState.update('mix-panel-id', u);
}
let s = sid || (sid = uuidv4());
return {
uid: u,
sid: s,
}
}
exports.init = init;
exports.event = event;
exports.time = time;

View File

@@ -370,7 +370,10 @@ async function getCompletionItems(params, liveParsers, androidLibrary) {
completionRequestCount += 1;
if ((completionRequestCount === 1) || (completionRequestCount === 5) || ((completionRequestCount % 25) === 0)) {
event('completion-requests', { comp_req_count: completionRequestCount });
event('completion-requests', {
comp_req_count: completionRequestCount, // total count for this session
comp_req_partial_count: (completionRequestCount % 25) || 25,
});
}
let parsed = docinfo.parsed;

View File

@@ -41,7 +41,10 @@ async function getSignatureHelp(request, liveParsers) {
methodsigRequestCount += 1;
if ((methodsigRequestCount === 1) || (methodsigRequestCount === 5) || ((methodsigRequestCount % 25) === 0)) {
event('method-sig-requests', { methsig_req_count: methodsigRequestCount });
event('method-sig-requests', {
methsig_req_count: methodsigRequestCount,
methsig_req_partial_count: (methodsigRequestCount % 25) || 25,
});
}
// locate the token at the requested position

View File

@@ -1,23 +1,29 @@
{
"name": "langserver",
"version": "1.0.3",
"version": "1.0.4",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@amplitude/node": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/@amplitude/node/-/node-0.3.3.tgz",
"integrity": "sha512-Uzg4MRAuD053Ex67Iu2lm2GovnVte1uKI3q7CXlMCYZ9ylZmAkPbTnjg9OVyD4f+IiUfgK4p3bE7r9p7jqSDLA==",
"requires": {
"@amplitude/types": "^0.3.2",
"tslib": "^1.9.3"
}
},
"@amplitude/types": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/@amplitude/types/-/types-0.3.2.tgz",
"integrity": "sha512-7+m7nhJMFGbpsppOUsCH8f4FOFyAxgKFuXkKknU/LP2CMYVjWEIoLTKKgaJPc2c8wXaK5KPXVetb8VeiGbuaGg=="
},
"@types/node": {
"version": "13.13.4",
"resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.4.tgz",
"integrity": "sha512-x26ur3dSXgv5AwKS0lNfbjpCakGIduWU1DU91Zz58ONRWrIKGunmZBNv4P7N+e27sJkiGDsw/3fT4AtsqQBrBA==",
"dev": true
},
"agent-base": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz",
"integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==",
"requires": {
"es6-promisify": "^5.0.0"
}
},
"balanced-match": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
@@ -79,14 +85,6 @@
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
},
"debug": {
"version": "3.2.6",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
"integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
"requires": {
"ms": "^2.1.1"
}
},
"duplexer2": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz",
@@ -95,19 +93,6 @@
"readable-stream": "^2.0.2"
}
},
"es6-promise": {
"version": "4.2.8",
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
"integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w=="
},
"es6-promisify": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz",
"integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=",
"requires": {
"es6-promise": "^4.0.3"
}
},
"fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@@ -142,15 +127,6 @@
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
"integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw=="
},
"https-proxy-agent": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-3.0.0.tgz",
"integrity": "sha512-y4jAxNEihqvBI5F3SaO2rtsjIOnnNA8sEbuiP+UhJZJHeM2NRm6c09ax2tgqme+SgUUvjao2fJXF4h3D6Cb2HQ==",
"requires": {
"agent-base": "^4.3.0",
"debug": "^3.1.0"
}
},
"inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
@@ -195,14 +171,6 @@
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
},
"mixpanel": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/mixpanel/-/mixpanel-0.11.0.tgz",
"integrity": "sha512-TS7AkCmfC+vGshlCOjEcITFoFxlt5fdSEqmN+d+pTXAhE5v+jPQW2uUcn9W+Oq4NVXz+kdskU09dsm9vmNl0ig==",
"requires": {
"https-proxy-agent": "3.0.0"
}
},
"mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
@@ -211,11 +179,6 @@
"minimist": "^1.2.5"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -279,6 +242,11 @@
"resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz",
"integrity": "sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk="
},
"tslib": {
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz",
"integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q=="
},
"unzipper": {
"version": "0.10.11",
"resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.11.tgz",

View File

@@ -1,6 +1,6 @@
{
"name": "langserver",
"version": "1.0.3",
"version": "1.0.4",
"description": "Language server for Android development",
"main": "server.js",
"scripts": {
@@ -9,8 +9,8 @@
"author": "",
"license": "ISC",
"dependencies": {
"@amplitude/node": "^0.3.3",
"java-mti": "adelphes/java-mti#ec164ac",
"mixpanel": "0.11.0",
"uuid": "8.2.0",
"vscode-languageserver": "6.1.1",
"vscode-languageserver-textdocument": "1.0.1",

View File

@@ -135,7 +135,7 @@ connection.onInitialize((params) => {
}
Settings.set(startupOpts.initialSettings);
analytics.init(undefined, startupOpts.mpuid, uuidv4(), package_json, { vscode_version: startupOpts.vscodeVersion });
analytics.init(undefined, startupOpts.uid, startupOpts.session_id, '', package_json, startupOpts.vscode_props, 'langserver-start');
loadCodeCompletionLibrary(startupOpts.extensionPath, Settings.codeCompletionLibraries);

8
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{
"name": "android-dev-ext",
"version": "1.3.1",
"version": "1.3.2",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -710,9 +710,9 @@
"integrity": "sha1-hMinKrWcRyUyFIDJdeZQg0LnCTc="
},
"lodash": {
"version": "4.17.15",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
"version": "4.17.19",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==",
"dev": true
},
"long": {

View File

@@ -2,7 +2,7 @@
"name": "android-dev-ext",
"displayName": "Android",
"description": "Android debugging support for VS Code",
"version": "1.3.1",
"version": "1.3.2",
"publisher": "adelphes",
"preview": true,
"license": "MIT",

View File

@@ -6,7 +6,6 @@ const {
// node and external modules
const os = require('os');
const path = require('path');
const uuidv4 = require('uuid').v4;
// our stuff
const { ADBClient } = require('./adbclient');
@@ -111,9 +110,8 @@ class AndroidDebugSession extends DebugSession {
this.terminate_reason = '';
this.session_id = uuidv4();
this.session_start = new Date();
analytics.init();
analytics.init(undefined, undefined, undefined, '', require('../package.json'), {}, 'debugger-start');
// this debugger uses one-based lines and columns
this.setDebuggerLinesStartAt1(true);
@@ -139,8 +137,8 @@ class AndroidDebugSession extends DebugSession {
response.body.supportsExceptionInfoRequest = true;
response.body.supportsHitConditionalBreakpoints = true;
this.sendResponse(response);
}
this.sendResponse(response);
}
/**
* @param {string} msg
@@ -445,8 +443,7 @@ class AndroidDebugSession extends DebugSession {
await this.dbgr.resume();
analytics.event('debug-started', {
dbg_session_id: this.session_id,
dbg_start: this.session_start.toLocaleTimeString(),
dbg_start: this.session_start.toTimeString(),
dbg_tz: this.session_start.getTimezoneOffset(),
dbg_kind: 'attach',
dbg_device_api: this.device_api_level,
@@ -611,8 +608,7 @@ class AndroidDebugSession extends DebugSession {
await this.dbgr.resume();
analytics.event('debug-started', {
dbg_session_id: this.session_id,
dbg_start: this.session_start.toLocaleTimeString(),
dbg_start: this.session_start.toTimeString(),
dbg_tz: this.session_start.getTimezoneOffset(),
dbg_kind: 'debug',
dbg_device_api: this.device_api_level,
@@ -817,7 +813,6 @@ class AndroidDebugSession extends DebugSession {
D('disconnectRequest');
this._isDisconnecting = true;
analytics.event('debug-end', {
dbg_session_id: this.session_id,
dbg_elapsed: Math.trunc((Date.now() - this.session_start.getTime())/1e3),
dbg_kind: this.debug_mode,
dbg_term_reason: this.terminate_reason,

View File

@@ -610,12 +610,16 @@ class DebuggerBreakpoint {
const cls = splitSourcePath(srcfpn);
this.id = DebuggerBreakpoint.makeBreakpointID(srcfpn, linenum);
this.srcfpn = srcfpn;
this.file = cls.file;
this.qtype = cls.qtype;
this.pkg = cls.pkg;
this.type = cls.type;
this.linenum = linenum;
this.options = options;
this.sigpattern = new RegExp(`^L${cls.qtype}([$][$a-zA-Z0-9_]+)?;$`),
// sigpattern is used to match up source files with class signatures but because
// kotlin allows filenames that differ from class names, we now only match up to
// the package level and use the source name retuned by JDWP to narrow it down futher.
this.sigpattern = new RegExp(`^L${cls.pkg.replace(/[.]/g, '/')}/[^/]+;$`)
this.state = initialState; // set,notloaded,enabled,removed
this.hitcount = 0; // number of times this bp was hit during execution
this.stopcount = 0; // number of times this bp caused a break into the debugger

View File

@@ -567,13 +567,15 @@ class Debugger extends EventEmitter {
* @param {DebuggerBreakpoint} bp
*/
async initialiseBreakpoint(bp) {
// try and load the class - if the runtime hasn't loaded it yet, this will just return a TypeNotAvailable instance
// try and load the classes in the package - if the runtime hasn't loaded it yet, this will just return a TypeNotAvailable instance
let classes = await Promise.all(
[...this.session.loadedClasses]
.filter(signature => bp.sigpattern.test(signature))
.map(signature => this.loadClassInfo(signature))
);
let bploc = Debugger.findBreakpointLocation(classes, bp);
// find classes with a matching sourcefile name
classes = classes.filter(c => c.src.sourcefile === bp.file);
const bploc = Debugger.findBreakpointLocation(classes, bp);
if (!bploc) {
// we couldn't identify a matching location - either the class is not yet loaded or the
// location doesn't correspond to any code. In case it's the former, make sure we are notified
@@ -1661,6 +1663,7 @@ class Debugger extends EventEmitter {
let bploc = null;
classes.find(c =>
bp.sigpattern.test(c.type.signature)
&& bp.file === c.src.sourcefile
&& c.methods.find(m => {
const line = m.linetable.lines.find(line => line.linenum === bp.linenum);
if (line) {

View File

@@ -6,12 +6,16 @@ function hasValidSourceFileExtension(s) {
return /\.(java|kt)$/i.test(s);
}
/**
* @param {string} filepath
*/
function splitSourcePath(filepath) {
const m = filepath.match(/^\/([^/]+(?:\/[^/]+)*)?\/([^./]+)\.(java|kt)$/);
const m = filepath.match(/^\/([^/]+(?:\/[^/]+)*)?\/([^./]+)\.(java|kt)$/i);
return {
pkg: m[1].replace(/\/+/g, '.'),
type: m[2],
qtype: `${m[1]}/${m[2]}`,
file: `${m[2]}.${m[3]}`,
}
}