diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml new file mode 100644 index 00000000..6e5f21ef --- /dev/null +++ b/.github/.OwlBot.lock.yaml @@ -0,0 +1,4 @@ +docker: + digest: sha256:f556e6e7be625deb1b2429fe608df27be57185c3e6b7d39ee0059f1609f17530 + image: gcr.io/repo-automation-bots/owlbot-nodejs:latest + diff --git a/.github/.OwlBot.yaml b/.github/.OwlBot.yaml new file mode 100644 index 00000000..3a281cc9 --- /dev/null +++ b/.github/.OwlBot.yaml @@ -0,0 +1,19 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +docker: + image: gcr.io/repo-automation-bots/owlbot-nodejs:latest + + +begin-after-commit-hash: 397c0bfd367a2427104f988d5329bc117caafd95 + diff --git a/.github/generated-files-bot.yml b/.github/generated-files-bot.yml new file mode 100644 index 00000000..6b04910c --- /dev/null +++ b/.github/generated-files-bot.yml @@ -0,0 +1,16 @@ +generatedFiles: +- path: '.kokoro/**' + message: '`.kokoro` files are templated and should be updated in [`synthtool`](https://github.com/googleapis/synthtool)' +- path: '.github/CODEOWNERS' + message: 'CODEOWNERS should instead be modified via the `codeowner_team` property in .repo-metadata.json' +- path: '.github/workflows/**' + message: '`.github/workflows` (GitHub Actions) should be updated in [`synthtool`](https://github.com/googleapis/synthtool)' +- path: '.github/generated-files-bot.+(yml|yaml)' + message: '`.github/generated-files-bot.(yml|yaml)` should be updated in [`synthtool`](https://github.com/googleapis/synthtool)' +- path: 'README.md' + message: '`README.md` is managed by [`synthtool`](https://github.com/googleapis/synthtool). However, a partials file can be used to update the README, e.g.: https://github.com/googleapis/nodejs-storage/blob/master/.readme-partials.yaml' +- path: 'samples/README.md' + message: '`samples/README.md` is managed by [`synthtool`](https://github.com/googleapis/synthtool). However, a partials file can be used to update the README, e.g.: https://github.com/googleapis/nodejs-storage/blob/master/.readme-partials.yaml' +ignoreAuthors: +- 'gcf-owl-bot[bot]' +- 'yoshi-automation' diff --git a/CHANGELOG.md b/CHANGELOG.md index db526a06..b1d49fbc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Node.js Agent for Google Cloud Debug ChangeLog +### [5.2.1](https://www.github.com/googleapis/cloud-debug-nodejs/compare/v5.2.0...v5.2.1) (2021-05-31) + + +### Bug Fixes + +* periodically reset v8 session to prevent memory leak ([#957](https://www.github.com/googleapis/cloud-debug-nodejs/issues/957)) ([7735425](https://www.github.com/googleapis/cloud-debug-nodejs/commit/7735425ee8999c6ab1c30706ddf014315309705c)) + ## [5.2.0](https://www.github.com/googleapis/cloud-debug-nodejs/compare/v5.1.3...v5.2.0) (2021-05-04) diff --git a/synth.py b/owlbot.py similarity index 64% rename from synth.py rename to owlbot.py index dcb46941..a7f40972 100644 --- a/synth.py +++ b/owlbot.py @@ -12,18 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -import synthtool as s -import synthtool.gcp as gcp import synthtool.languages.node as node -import logging -logging.basicConfig(level=logging.DEBUG) - -AUTOSYNTH_MULTIPLE_COMMITS = True - -common_templates = gcp.CommonTemplates() -templates = common_templates.node_library() -s.copy(templates, excludes=['.eslintignore', '.mocharc.js', '.github/workflows/ci.yaml']) - -node.install() -node.fix() +node.owlbot_main(templates_excludes=['.eslintignore', '.mocharc.js', '.github/workflows/ci.yaml']) \ No newline at end of file diff --git a/package.json b/package.json index 46157e0e..300c4bdf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@google-cloud/debug-agent", - "version": "5.2.0", + "version": "5.2.1", "author": "Google Inc.", "description": "Stackdriver Debug Agent for Node.js", "main": "./build/src/index", diff --git a/samples/package.json b/samples/package.json index 9d49ce9e..c100321e 100644 --- a/samples/package.json +++ b/samples/package.json @@ -17,7 +17,7 @@ "test": "mocha" }, "dependencies": { - "@google-cloud/debug-agent": "^5.2.0", + "@google-cloud/debug-agent": "^5.2.1", "express": "4.17.1" }, "devDependencies": { diff --git a/src/agent/config.ts b/src/agent/config.ts index e72e7117..45ef4f36 100644 --- a/src/agent/config.ts +++ b/src/agent/config.ts @@ -357,6 +357,14 @@ export interface ResolvedDebugAgentConfig extends GoogleAuthOptions { * used to set a default api url */ apiUrl?: string; + + /** + * Number of times of the V8 breakpoint hits events before resetting the + * breakpoints. This is to release the memory usage held by V8 engine for each + * breakpoint hit to prevent the memory leak. The default value is specified + * in defaultConfig. + */ + resetV8DebuggerThreshold: number; } export interface StackdriverConfig extends GoogleAuthOptions { @@ -406,4 +414,5 @@ export const defaultConfig: ResolvedDebugAgentConfig = { forceNewAgent_: false, testMode_: false, + resetV8DebuggerThreshold: 30, }; diff --git a/src/agent/controller.ts b/src/agent/controller.ts index 45a43d21..1687beb0 100644 --- a/src/agent/controller.ts +++ b/src/agent/controller.ts @@ -134,7 +134,7 @@ export class Controller extends ServiceObject { } else if (response.statusCode === 404) { // The v2 API returns 404 (google.rpc.Code.NOT_FOUND) when the agent // registration expires. We should re-register. - callback(null, (response as {}) as t.Response); + callback(null, response as {} as t.Response); return; } else if (response.statusCode !== 200) { callback( @@ -146,7 +146,7 @@ export class Controller extends ServiceObject { } else { body = body || {}; that.nextWaitToken = body.nextWaitToken; - callback(null, (response as {}) as t.Response, body); + callback(null, response as {} as t.Response, body); } } ); diff --git a/src/agent/debuglet.ts b/src/agent/debuglet.ts index ff4718b9..691b58fe 100644 --- a/src/agent/debuglet.ts +++ b/src/agent/debuglet.ts @@ -457,7 +457,7 @@ export class Debuglet extends EventEmitter { let sourceContext; try { sourceContext = - ((that.config.sourceContext as {}) as SourceContext) || + (that.config.sourceContext as {} as SourceContext) || (await Debuglet.getSourceContextFromFile()); } catch (err5) { that.logger.warn('Unable to discover source context', err5); @@ -741,9 +741,11 @@ export class Debuglet extends EventEmitter { ); // TODO: Handle the case when `that.debuggee` is null. // TODO: Handle the case when `result` is undefined. - (that.debuggee as Debuggee).id = (result as { - debuggee: Debuggee; - }).debuggee.id; + (that.debuggee as Debuggee).id = ( + result as { + debuggee: Debuggee; + } + ).debuggee.id; // TODO: Handle the case when `result` is undefined. that.emit('registered', (result as {debuggee: Debuggee}).debuggee.id); that.debuggeeRegistered.resolve(); @@ -926,9 +928,7 @@ export class Debuglet extends EventEmitter { // field. It is possible that breakpoint.id is always // undefined! // TODO: Make sure the use of `that` here is correct. - delete that.completedBreakpointMap[ - ((breakpoint as {}) as {id: number}).id - ]; + delete that.completedBreakpointMap[(breakpoint as {} as {id: number}).id]; }); // Remove active breakpoints that the server no longer care about. @@ -946,9 +946,9 @@ export class Debuglet extends EventEmitter { * @return {Object.} A map of breakpoint IDs to breakpoints. * @private */ - convertBreakpointListToMap_( - breakpointList: stackdriver.Breakpoint[] - ): {[key: string]: stackdriver.Breakpoint} { + convertBreakpointListToMap_(breakpointList: stackdriver.Breakpoint[]): { + [key: string]: stackdriver.Breakpoint; + } { const map: {[id: string]: stackdriver.Breakpoint} = {}; breakpointList.forEach(breakpoint => { // TODO: Address the case when `breakpoint.id` is `undefined`. @@ -1098,13 +1098,15 @@ export class Debuglet extends EventEmitter { const that = this; // TODO: Address the case when `that.debuggee` is `null`. - that.controller.updateBreakpoint(that.debuggee as Debuggee, breakpoint, ( - err /*, body*/ - ) => { - if (err) { - that.logger.error('Unable to complete breakpoint on server', err); + that.controller.updateBreakpoint( + that.debuggee as Debuggee, + breakpoint, + (err /*, body*/) => { + if (err) { + that.logger.error('Unable to complete breakpoint on server', err); + } } - }); + ); } /** diff --git a/src/agent/io/sourcemapper.ts b/src/agent/io/sourcemapper.ts index 509321fb..84e8787e 100644 --- a/src/agent/io/sourcemapper.ts +++ b/src/agent/io/sourcemapper.ts @@ -74,9 +74,9 @@ async function processSourcemap( // TODO: Resolve the cast of `contents as any` (This is needed because the // type is expected to be of `RawSourceMap` but the existing // working code uses a string.) - consumer = (new sourceMap.SourceMapConsumer( - (contents as {}) as sourceMap.RawSourceMap - ) as {}) as sourceMap.RawSourceMap; + consumer = new sourceMap.SourceMapConsumer( + contents as {} as sourceMap.RawSourceMap + ) as {} as sourceMap.RawSourceMap; } catch (e) { throw new Error( 'An error occurred while reading the ' + @@ -246,7 +246,8 @@ export class SourceMapper { }; // TODO: Determine how to remove the explicit cast here. - const consumer: sourceMap.SourceMapConsumer = (entry.mapConsumer as {}) as sourceMap.SourceMapConsumer; + const consumer: sourceMap.SourceMapConsumer = + entry.mapConsumer as {} as sourceMap.SourceMapConsumer; const allPos = consumer.allGeneratedPositionsFor(sourcePos); /* * Based on testing, it appears that the following code is needed to @@ -270,7 +271,7 @@ export class SourceMapper { // TODO: The `sourceMap.Position` type definition has a `column` // attribute and not a `col` attribute. Determine if the type // definition or this code is correct. - column: ((mappedPos as {}) as {col: number}).col, // SourceMapConsumer uses + column: (mappedPos as {} as {col: number}).col, // SourceMapConsumer uses // zero-based column // numbers which is the // same as the expected diff --git a/src/agent/state/inspector-state.ts b/src/agent/state/inspector-state.ts index 9e8845e0..f0268e67 100644 --- a/src/agent/state/inspector-state.ts +++ b/src/agent/state/inspector-state.ts @@ -151,7 +151,7 @@ class StateResolver { }; // TODO: Determine why _extend is used here - this.resolvedVariableTable = ((util as {}) as {_extend: Function})._extend( + this.resolvedVariableTable = (util as {} as {_extend: Function})._extend( [], this.messageTable ); diff --git a/src/agent/state/legacy-state.ts b/src/agent/state/legacy-state.ts index 1f26e0fd..4e112092 100644 --- a/src/agent/state/legacy-state.ts +++ b/src/agent/state/legacy-state.ts @@ -138,7 +138,7 @@ class StateResolver { }; // TODO: Determine why _extend is used here - this.resolvedVariableTable = ((util as {}) as {_extend: Function})._extend( + this.resolvedVariableTable = (util as {} as {_extend: Function})._extend( [], this.messageTable ); @@ -148,7 +148,7 @@ class StateResolver { // This constructor is only used in situations where the legacy vm // interface is used that has the `runInDebugContext` method. - this.scopeType = ((vm as {}) as LegacyVm).runInDebugContext('ScopeType'); + this.scopeType = (vm as {} as LegacyVm).runInDebugContext('ScopeType'); } /** diff --git a/src/agent/util/debug-assert.ts b/src/agent/util/debug-assert.ts index 56f7b30b..4bc58e35 100644 --- a/src/agent/util/debug-assert.ts +++ b/src/agent/util/debug-assert.ts @@ -62,5 +62,5 @@ const fakeAssert: FakeAssert = { export function debugAssert(enableAssertions: boolean): FakeAssert { // The typecast is needed since the @types/node doesn't cover Node 10 yet - return enableAssertions ? ((realAssert as {}) as FakeAssert) : fakeAssert; + return enableAssertions ? (realAssert as {} as FakeAssert) : fakeAssert; } diff --git a/src/agent/v8/inspector-debugapi.ts b/src/agent/v8/inspector-debugapi.ts index 487bd849..b71815ee 100644 --- a/src/agent/v8/inspector-debugapi.ts +++ b/src/agent/v8/inspector-debugapi.ts @@ -41,6 +41,21 @@ interface InspectorOptions { useWellFormattedUrl: boolean; } +/** Data related to the v8 inspector. */ +interface V8Data { + session: inspector.Session; + // Options for behavior when interfacing with the Inspector API. + inspectorOptions: InspectorOptions; + inspector: V8Inspector; + // Store the v8 setBreakpoint parameters for each v8 breakpoint so that later + // the recorded parameters can be used to reset the breakpoints. + setBreakpointsParams: { + [ + v8BreakpointId: string + ]: inspector.Debugger.SetBreakpointByUrlParameterType; + }; +} + /** * In older versions of Node, the script source as seen by the Inspector * backend is wrapped in `require('module').wrapper`, and in new versions @@ -68,7 +83,6 @@ export class InspectorDebugApi implements debugapi.DebugApi { fileStats: ScanStats; breakpoints: {[id: string]: BreakpointData} = {}; sourcemapper: SourceMapper; - session: inspector.Session; // TODO: listeners, scrpitmapper, location mapper and breakpointmapper can use // Map in the future after resolving Map.prototype.get(key) returns V or // undefined. @@ -81,9 +95,9 @@ export class InspectorDebugApi implements debugapi.DebugApi { // stackdriver breakpoint id. breakpointMapper: {[id: string]: stackdriver.BreakpointId[]} = {}; numBreakpoints = 0; - // Options for behavior when interfacing with the Inspector API. - private inspectorOptions: InspectorOptions; - v8Inspector: V8Inspector; + numBreakpointHitsBeforeReset = 0; + v8: V8Data; + constructor( logger: consoleLogLevel.Logger, config: ResolvedDebugAgentConfig, @@ -94,25 +108,36 @@ export class InspectorDebugApi implements debugapi.DebugApi { this.config = config; this.fileStats = jsFiles; this.sourcemapper = sourcemapper; - this.session = new inspector.Session(); - this.session.connect(); - this.session.on('Debugger.scriptParsed', script => { + this.scriptMapper = {}; + this.v8 = this.createV8Data(); + } + + /** Creates a new V8 Debugging session and the related data. */ + private createV8Data(): V8Data { + const session = new inspector.Session(); + session.connect(); + session.on('Debugger.scriptParsed', script => { this.scriptMapper[script.params.scriptId] = script.params; }); - this.session.post('Debugger.enable'); - this.session.post('Debugger.setBreakpointsActive', {active: true}); - this.session.on('Debugger.paused', message => { + session.post('Debugger.enable'); + session.post('Debugger.setBreakpointsActive', {active: true}); + session.on('Debugger.paused', message => { try { this.handleDebugPausedEvent(message.params); } catch (error) { this.logger.error(error); } }); - this.inspectorOptions = { - // Well-Formatted URL is required in Node 10.11.1+. - useWellFormattedUrl: utils.satisfies(process.version, '>10.11.0'), + + return { + session, + inspectorOptions: { + // Well-Formatted URL is required in Node 10.11.1+. + useWellFormattedUrl: utils.satisfies(process.version, '>10.11.0'), + }, + inspector: new V8Inspector(session), + setBreakpointsParams: {}, }; - this.v8Inspector = new V8Inspector(this.session); } set( @@ -219,7 +244,8 @@ export class InspectorDebugApi implements debugapi.DebugApi { if (!this.breakpointMapper[breakpointData.id]) { // When breakpointmapper does not countain current v8/inspector breakpoint // id, we should remove this breakpoint from v8. - result = this.v8Inspector.removeBreakpoint(breakpointData.id); + result = this.v8.inspector.removeBreakpoint(breakpointData.id); + delete this.v8.setBreakpointsParams[breakpointData.id]; } delete this.breakpoints[breakpoint.id]; delete this.listeners[breakpoint.id]; @@ -297,7 +323,7 @@ export class InspectorDebugApi implements debugapi.DebugApi { } disconnect(): void { - this.session.disconnect(); + this.v8.session.disconnect(); } numBreakpoints_(): number { @@ -487,7 +513,7 @@ export class InspectorDebugApi implements debugapi.DebugApi { let v8BreakpointId; // v8/inspector breakpoint id if (!this.locationMapper[locationStr]) { // The first time when a breakpoint was set to this location. - const rawUrl = this.inspectorOptions.useWellFormattedUrl + const rawUrl = this.v8.inspectorOptions.useWellFormattedUrl ? `file://${matchingScript}` : matchingScript; // on windows on Node 11+, the url must start with file:/// @@ -496,17 +522,20 @@ export class InspectorDebugApi implements debugapi.DebugApi { process.platform === 'win32' && utils.satisfies(process.version, '>=11') ? rawUrl.replace(/^file:\/\//, 'file:///').replace(/\\/g, '/') : rawUrl; - const res = this.v8Inspector.setBreakpointByUrl({ + const params = { lineNumber: line - 1, url, columnNumber: column - 1, condition: breakpoint.condition || undefined, - }); + }; + const res = this.v8.inspector.setBreakpointByUrl(params); if (res.error || !res.response) { // Error case. return null; } v8BreakpointId = res.response.breakpointId; + this.v8.setBreakpointsParams[v8BreakpointId] = params; + this.locationMapper[locationStr] = []; this.breakpointMapper[v8BreakpointId] = []; } else { @@ -559,9 +588,11 @@ export class InspectorDebugApi implements debugapi.DebugApi { // TODO: Address the case where `breakpoint.id` is `null`. breakpoint.expressions[i] = // TODO: Address the case where `compile` is `null`. - (this.breakpoints[breakpoint.id].compile as ( - text: string - ) => string)(breakpoint.expressions[i]); + ( + this.breakpoints[breakpoint.id].compile as ( + text: string + ) => string + )(breakpoint.expressions[i]); } catch (e) { this.logger.info( 'Unable to compile watch expression >> ' + @@ -591,7 +622,7 @@ export class InspectorDebugApi implements debugapi.DebugApi { const evaluatedExpressions = breakpoint.expressions.map(exp => { // returnByValue is set to true here so that the JSON string of the // value will be returned to log. - const result = state.evaluate(exp, frame, that.v8Inspector, true); + const result = state.evaluate(exp, frame, that.v8.inspector, true); if (result.error) { return result.error; } else { @@ -606,7 +637,7 @@ export class InspectorDebugApi implements debugapi.DebugApi { breakpoint, this.config, this.scriptMapper, - this.v8Inspector + this.v8.inspector ); if ( breakpoint.location && @@ -617,7 +648,8 @@ export class InspectorDebugApi implements debugapi.DebugApi { breakpoint.stackFrames = captured.stackFrames; // TODO: This suggests the Status type and Variable type are the same. // Determine if that is the case. - breakpoint.variableTable = captured.variableTable as stackdriver.Variable[]; + breakpoint.variableTable = + captured.variableTable as stackdriver.Variable[]; breakpoint.evaluatedExpressions = expressionErrors.concat( captured.evaluatedExpressions ); @@ -639,5 +671,37 @@ export class InspectorDebugApi implements debugapi.DebugApi { } catch (e) { this.logger.warn('Internal V8 error on breakpoint event: ' + e); } + + this.tryResetV8Debugger(); + } + + /** + * Periodically resets breakpoints to prevent memory leaks in V8 (for holding + * contexts of previous breakpoint hits). + */ + private tryResetV8Debugger() { + this.numBreakpointHitsBeforeReset += 1; + if ( + this.numBreakpointHitsBeforeReset < this.config.resetV8DebuggerThreshold + ) { + return; + } + this.numBreakpointHitsBeforeReset = 0; + + const storedParams = this.v8.setBreakpointsParams; + + // Re-connect the session to clean the memory usage. + this.disconnect(); + this.scriptMapper = {}; + this.v8 = this.createV8Data(); + this.v8.setBreakpointsParams = storedParams; + + // Setting the v8 breakpoints again according to the stored parameters. + for (const params of Object.values(storedParams)) { + const res = this.v8.inspector.setBreakpointByUrl(params); + if (res.error || !res.response) { + this.logger.error('Error upon re-setting breakpoint: ' + res); + } + } } } diff --git a/src/agent/v8/legacy-debugapi.ts b/src/agent/v8/legacy-debugapi.ts index bd0b87e0..8d5b107e 100644 --- a/src/agent/v8/legacy-debugapi.ts +++ b/src/agent/v8/legacy-debugapi.ts @@ -73,7 +73,7 @@ export class V8DebugApi implements debugapi.DebugApi { this.sourcemapper = sourcemapper; // This constructor is only used in situations where the legacy vm // interface is used that has the `runInDebugContext` method. - this.v8 = ((vm as {}) as LegacyVm).runInDebugContext('Debug'); + this.v8 = (vm as {} as LegacyVm).runInDebugContext('Debug'); this.config = config; this.fileStats = jsFiles; this.v8Version = /(\d+\.\d+\.\d+)\.\d+/.exec(process.versions.v8); @@ -522,9 +522,11 @@ export class V8DebugApi implements debugapi.DebugApi { try { breakpoint.expressions[i] = // TODO: Address the case where `compile` is `null`. - (this.breakpoints[breakpoint.id].compile as ( - text: string - ) => string)(breakpoint.expressions[i]); + ( + this.breakpoints[breakpoint.id].compile as ( + text: string + ) => string + )(breakpoint.expressions[i]); } catch (e) { this.logger.info( 'Unable to compile watch expression >> ' + @@ -579,7 +581,8 @@ export class V8DebugApi implements debugapi.DebugApi { breakpoint.stackFrames = captured.stackFrames; // TODO: This suggests the Status type and Variable type are the same. // Determine if that is the case. - breakpoint.variableTable = captured.variableTable as stackdriver.Variable[]; + breakpoint.variableTable = + captured.variableTable as stackdriver.Variable[]; breakpoint.evaluatedExpressions = expressionErrors.concat( captured.evaluatedExpressions ); diff --git a/synth.metadata b/synth.metadata deleted file mode 100644 index 2efd88bb..00000000 --- a/synth.metadata +++ /dev/null @@ -1,18 +0,0 @@ -{ - "sources": [ - { - "git": { - "name": ".", - "remote": "https://github.com/googleapis/cloud-debug-nodejs.git", - "sha": "72b5d590450e53d9a3276fe88371a4577a0d6341" - } - }, - { - "git": { - "name": "synthtool", - "remote": "https://github.com/googleapis/synthtool.git", - "sha": "b33b0e2056a85fc2264b294f2cf47dcd45e95186" - } - } - ] -} \ No newline at end of file diff --git a/system-test/test-install.ts b/system-test/test-install.ts index c1a612ca..ac0d3e16 100644 --- a/system-test/test-install.ts +++ b/system-test/test-install.ts @@ -18,7 +18,7 @@ import {ncp} from 'ncp'; import * as tmp from 'tmp-promise'; import {promisify} from 'util'; -const mvp = (promisify(mv) as {}) as (...args: string[]) => Promise; +const mvp = promisify(mv) as {} as (...args: string[]) => Promise; const ncpp = promisify(ncp); const stagingDir = tmp.dirSync({keep: false, unsafeCleanup: true}); const stagingPath = stagingDir.name; diff --git a/test/test-controller.ts b/test/test-controller.ts index 20d43a78..0f825168 100644 --- a/test/test-controller.ts +++ b/test/test-controller.ts @@ -28,14 +28,14 @@ delete process.env.GCLOUD_PROJECT; import {Controller} from '../src/agent/controller'; // TODO: Fix fakeDebug to actually implement Debug. -const fakeDebug = ({ +const fakeDebug = { apiEndpoint: 'clouddebugger.googleapis.com', request: (options: t.Options, cb: t.RequestCallback) => { teenyRequest(options, (err, r) => { cb(err, r ? r.body : undefined, r); }); }, -} as {}) as Debug; +} as {} as Debug; const agentVersion = 'SomeName/client/SomeVersion'; const url = 'https://clouddebugger.googleapis.com'; @@ -226,7 +226,7 @@ describe('Controller API', () => { // TODO: Fix this error that states `body` is not a property // of `ServerResponse`. assert( - ((response as {}) as {body: {waitExpired: {}}}).body.waitExpired, + (response as {} as {body: {waitExpired: {}}}).body.waitExpired, 'should have expired set' ); scope.done(); diff --git a/test/test-debug-assert.ts b/test/test-debug-assert.ts index 793b13bd..0e0414fb 100644 --- a/test/test-debug-assert.ts +++ b/test/test-debug-assert.ts @@ -34,7 +34,7 @@ describe('debug-assert', () => { it.skip('should cover the full assert API', () => { Object.keys(realAssert).forEach(key => { realAssert.strictEqual( - typeof ((assert as {}) as {[key: string]: Function})[key], + typeof (assert as {} as {[key: string]: Function})[key], 'function', `${key} does not exist on the debug assert library` ); diff --git a/test/test-debuglet.ts b/test/test-debuglet.ts index 4439861f..6c923236 100644 --- a/test/test-debuglet.ts +++ b/test/test-debuglet.ts @@ -39,7 +39,8 @@ import {Debug} from '../src/client/stackdriver/debug'; const DEBUGGEE_ID = 'bar'; const REGISTER_PATH = '/v2/controller/debuggees/register'; const BPS_PATH = '/v2/controller/debuggees/' + DEBUGGEE_ID + '/breakpoints'; -const EXPRESSIONS_REGEX = /Expressions and conditions are not allowed.*https:\/\/goo\.gl\/ShSm6r/; +const EXPRESSIONS_REGEX = + /Expressions and conditions are not allowed.*https:\/\/goo\.gl\/ShSm6r/; // eslint-disable-next-line @typescript-eslint/no-var-requires const fakeCredentials = require('./fixtures/gcloud-credentials.json'); @@ -64,11 +65,11 @@ const bp: stackdriver.Breakpoint = { location: {path: 'build/test/fixtures/foo.js', line: 2}, } as stackdriver.Breakpoint; // TODO: Have this actually implement Breakpoint. -const errorBp: stackdriver.Breakpoint = ({ +const errorBp: stackdriver.Breakpoint = { id: 'testLog', action: 'FOO', location: {path: 'build/test/fixtures/foo.js', line: 2}, -} as {}) as stackdriver.Breakpoint; +} as {} as stackdriver.Breakpoint; function verifyBreakpointRejection( re: RegExp, @@ -285,6 +286,20 @@ describe('Debuglet', () => { debuglet.start(); }); + it('should have default resetV8DebuggerThreshold value', done => { + const debuglet = new Debuglet(new Debug({}, packageInfo), {}); + assert.strictEqual(debuglet.config.resetV8DebuggerThreshold, 30); + done(); + }); + + it('should overwrite resetV8DebuggerThreshold when available', done => { + const debuglet = new Debuglet(new Debug({}, packageInfo), { + resetV8DebuggerThreshold: 123, + }); + assert.strictEqual(debuglet.config.resetV8DebuggerThreshold, 123); + done(); + }); + it('should not fail if files cannot be read', done => { const MOCKED_DIRECTORY = process.cwd(); const errors: Array<{filename: string; error: string}> = []; @@ -594,9 +609,11 @@ describe('Debuglet', () => { // Resolve this. assert.strictEqual( undefined, - (debuglet.config.serviceContext as { - minorVersion: {}; - }).minorVersion + ( + debuglet.config.serviceContext as { + minorVersion: {}; + } + ).minorVersion ); }); @@ -972,7 +989,7 @@ describe('Debuglet', () => { ); const old = Debuglet.getSourceContextFromFile; Debuglet.getSourceContextFromFile = async () => { - return {a: (5 as {}) as string}; + return {a: 5 as {} as string}; }; const config = debugletConfig(); @@ -1007,7 +1024,7 @@ describe('Debuglet', () => { const old = Debuglet.getSourceContextFromFile; Debuglet.getSourceContextFromFile = async () => { - return {a: (5 as {}) as string}; + return {a: 5 as {} as string}; }; const config = debugletConfig({ @@ -1471,43 +1488,43 @@ describe('Debuglet', () => { // TODO: Determine if Debuglet.format() should allow a number[] // or if only string[] should be allowed. assert.deepStrictEqual( - Debuglet.format('hi', ([5] as {}) as string[]), + Debuglet.format('hi', [5] as {} as string[]), 'hi' ); assert.deepStrictEqual( - Debuglet.format('hi $0', ([5] as {}) as string[]), + Debuglet.format('hi $0', [5] as {} as string[]), 'hi 5' ); assert.deepStrictEqual( - Debuglet.format('hi $0 $1', ([5, 'there'] as {}) as string[]), + Debuglet.format('hi $0 $1', [5, 'there'] as {} as string[]), 'hi 5 there' ); assert.deepStrictEqual( - Debuglet.format('hi $0 $1', ([5] as {}) as string[]), + Debuglet.format('hi $0 $1', [5] as {} as string[]), 'hi 5 $1' ); assert.deepStrictEqual( - Debuglet.format('hi $0 $1 $0', ([5] as {}) as string[]), + Debuglet.format('hi $0 $1 $0', [5] as {} as string[]), 'hi 5 $1 5' ); assert.deepStrictEqual( - Debuglet.format('hi $$', ([5] as {}) as string[]), + Debuglet.format('hi $$', [5] as {} as string[]), 'hi $' ); assert.deepStrictEqual( - Debuglet.format('hi $$0', ([5] as {}) as string[]), + Debuglet.format('hi $$0', [5] as {} as string[]), 'hi $0' ); assert.deepStrictEqual( - Debuglet.format('hi $00', ([5] as {}) as string[]), + Debuglet.format('hi $00', [5] as {} as string[]), 'hi 50' ); assert.deepStrictEqual( - Debuglet.format('hi $0', (['$1', 5] as {}) as string[]), + Debuglet.format('hi $0', ['$1', 5] as {} as string[]), 'hi $1' ); assert.deepStrictEqual( - Debuglet.format('hi $11', ([ + Debuglet.format('hi $11', [ 0, 1, 2, @@ -1522,7 +1539,7 @@ describe('Debuglet', () => { 'b', 'c', 'd', - ] as {}) as string[]), + ] as {} as string[]), 'hi b' ); }); diff --git a/test/test-max-data-size.ts b/test/test-max-data-size.ts index 4379c3d7..2d105ba1 100644 --- a/test/test-max-data-size.ts +++ b/test/test-max-data-size.ts @@ -117,9 +117,9 @@ describe('maxDataSize', () => { ) => { return ( acc && - (((!elem!.status || + ((!elem!.status || elem!.status!.description.format !== - 'Max data size reached') as {}) as stackdriver.Variable) + 'Max data size reached') as {} as stackdriver.Variable) ); } ) diff --git a/test/test-options-credentials.ts b/test/test-options-credentials.ts index 2f551b40..e2d75d23 100644 --- a/test/test-options-credentials.ts +++ b/test/test-options-credentials.ts @@ -77,7 +77,7 @@ describe('test-options-credentials', () => { }); nocks.projectId('project-via-metadata'); // TODO: Determine how to remove this cast. - debuglet = new Debuglet(debug, (config as {}) as DebugAgentConfig); + debuglet = new Debuglet(debug, config as {} as DebugAgentConfig); debuglet.start(); }); @@ -104,7 +104,7 @@ describe('test-options-credentials', () => { }); nocks.projectId('project-via-metadata'); // TODO: Determine how to remove this cast. - debuglet = new Debuglet(debug, (config as {}) as DebugAgentConfig); + debuglet = new Debuglet(debug, config as {} as DebugAgentConfig); debuglet.start(); }); @@ -145,7 +145,7 @@ describe('test-options-credentials', () => { assert.notStrictEqual(options.credentials[field], fileCredentials[field]); }); // TODO: Determine how to remove this cast. - debuglet = new Debuglet(debug, (config as {}) as DebugAgentConfig); + debuglet = new Debuglet(debug, config as {} as DebugAgentConfig); debuglet.start(); }); }); diff --git a/test/test-v8debugapi.ts b/test/test-v8debugapi.ts index 47a82c4c..a27b7b02 100644 --- a/test/test-v8debugapi.ts +++ b/test/test-v8debugapi.ts @@ -35,6 +35,7 @@ import * as extend from 'extend'; import * as debugapi from '../src/agent/v8/debugapi'; import {defaultConfig} from '../src/agent/config'; import {StatusMessage} from '../src/client/stackdriver/status-message'; +import {InspectorDebugApi} from '../src/agent/v8/inspector-debugapi'; import * as scanner from '../src/agent/io/scanner'; import * as SourceMapper from '../src/agent/io/sourcemapper'; import * as path from 'path'; @@ -301,10 +302,10 @@ describe('v8debugapi', () => { it('should accept breakpoint with ids 0 as a valid breakpoint', done => { // TODO(dominickramer): Have this actually implement Breakpoint - const bp: stackdriver.Breakpoint = ({ + const bp: stackdriver.Breakpoint = { id: 0, location: breakpointInFoo.location, - } as {}) as stackdriver.Breakpoint; + } as {} as stackdriver.Breakpoint; api.set(bp, err1 => { assert.ifError(err1); api.clear(bp, err2 => { @@ -316,10 +317,10 @@ describe('v8debugapi', () => { it('should permit breakpoints on js files with non-standard extensions', done => { require('./fixtures/hello.jsz'); - const bp: stackdriver.Breakpoint = ({ + const bp: stackdriver.Breakpoint = { id: 0, location: {line: 1, path: path.join('fixtures', 'hello.jsz')}, - } as {}) as stackdriver.Breakpoint; + } as {} as stackdriver.Breakpoint; api.set(bp, err1 => { assert.ifError(err1); api.clear(bp, err2 => { @@ -332,10 +333,10 @@ describe('v8debugapi', () => { it('should set error for breakpoint in non-js files', done => { require('./fixtures/key-bad.json'); // TODO(dominickramer): Have this actually implement Breakpoint - const bp = ({ + const bp = { id: 0, location: {line: 1, path: path.join('fixtures', 'key-bad.json')}, - } as {}) as stackdriver.Breakpoint; + } as {} as stackdriver.Breakpoint; api.set(bp, err => { assert.ok(err, 'should return an error'); assert.ok(bp.status); @@ -349,10 +350,10 @@ describe('v8debugapi', () => { it('should disambiguate incorrect path if filename is unique', done => { require('./fixtures/foo.js'); // TODO(dominickramer): Have this actually implement Breakpoint - const bp: stackdriver.Breakpoint = ({ + const bp: stackdriver.Breakpoint = { id: 0, location: {line: 1, path: path.join(path.sep, 'test', 'foo.js')}, - } as {}) as stackdriver.Breakpoint; + } as {} as stackdriver.Breakpoint; api.set(bp, err1 => { assert.ifError(err1); api.clear(bp, err2 => { @@ -366,10 +367,10 @@ describe('v8debugapi', () => { require('./fixtures/foo.js'); // hello.js is not unique but a/hello.js is. // TODO(dominickramer): Have this actually implement Breakpoint - const bp: stackdriver.Breakpoint = ({ + const bp: stackdriver.Breakpoint = { id: 0, location: {line: 1, path: path.join(path.sep, 'Server', 'a', 'hello.js')}, - } as {}) as stackdriver.Breakpoint; + } as {} as stackdriver.Breakpoint; api.set(bp, err1 => { assert.ifError(err1); api.clear(bp, err2 => { @@ -382,21 +383,21 @@ describe('v8debugapi', () => { describe('invalid breakpoints', () => { // TODO(dominickramer): Have this actually be a list of Breakpoints const badBreakpoints: stackdriver.Breakpoint[] = [ - ({} as {}) as stackdriver.Breakpoint, - ({id: 'with no location'} as {}) as stackdriver.Breakpoint, - ({id: 'with bad location', location: {}} as {}) as stackdriver.Breakpoint, - ({ + {} as {} as stackdriver.Breakpoint, + {id: 'with no location'} as {} as stackdriver.Breakpoint, + {id: 'with bad location', location: {}} as {} as stackdriver.Breakpoint, + { id: 'with no path', location: {line: 4}, - } as {}) as stackdriver.Breakpoint, - ({ + } as {} as stackdriver.Breakpoint, + { id: 'with no line', location: {path: 'foo.js'}, - } as {}) as stackdriver.Breakpoint, - ({ + } as {} as stackdriver.Breakpoint, + { id: 'with incomplete path', location: {path: 'st-v8debugapi.js', line: 4}, - } as {}) as stackdriver.Breakpoint, + } as {} as stackdriver.Breakpoint, ]; badBreakpoints.forEach((bp: stackdriver.Breakpoint) => { @@ -415,10 +416,10 @@ describe('v8debugapi', () => { require('./fixtures/a/hello.js'); require('./fixtures/b/hello.js'); // TODO(dominickramer): Have this actually implement Breakpoint - const bp: stackdriver.Breakpoint = ({ + const bp: stackdriver.Breakpoint = { id: 'ambiguous', location: {line: 1, path: 'hello.js'}, - } as {}) as stackdriver.Breakpoint; + } as {} as stackdriver.Breakpoint; api.set(bp, err => { assert.ok(err); assert.ok(bp.status); @@ -460,10 +461,10 @@ describe('v8debugapi', () => { it('should reject breakpoint on non-existent line', done => { require('./fixtures/foo.js'); // TODO(dominickramer): Have this actually implement Breakpoint - const bp: stackdriver.Breakpoint = ({ + const bp: stackdriver.Breakpoint = { id: 'non-existent line', location: {path: path.join('fixtures', 'foo.js'), line: 500}, - } as {}) as stackdriver.Breakpoint; + } as {} as stackdriver.Breakpoint; api.set(bp, err => { assert.ok(err); assert.ok(bp.status); @@ -489,11 +490,11 @@ describe('v8debugapi', () => { it('should validate breakpoint with condition "' + expr + '"', done => { // make a clean copy of breakpointInFoo // TODO(dominickramer): Have this actually implement Breakpoint - const bp: stackdriver.Breakpoint = ({ + const bp: stackdriver.Breakpoint = { id: breakpointInFoo.id, location: breakpointInFoo.location, condition: expr, - } as {}) as stackdriver.Breakpoint; + } as {} as stackdriver.Breakpoint; api.set(bp, err1 => { test(err1); api.clear(bp, err2 => { @@ -595,18 +596,18 @@ describe('v8debugapi', () => { describe('path normalization', () => { // TODO(dominickramer): Have this actually be a list of Breakpoints const breakpoints = [ - ({ + { id: 'path0', location: { line: 5, path: path.join(path.sep, 'test', 'test-v8debugapi-code.js'), }, - } as {}) as stackdriver.Breakpoint, - ({ + } as {} as stackdriver.Breakpoint, + { id: 'path1', location: {line: 5, path: path.join('test', 'test-v8debugapi-code.js')}, - } as {}) as stackdriver.Breakpoint, - ({ + } as {} as stackdriver.Breakpoint, + { id: 'path2', location: { line: 5, @@ -618,32 +619,32 @@ describe('v8debugapi', () => { .concat('test-v8debugapi-code.js') .join(path.sep), }, - } as {}) as stackdriver.Breakpoint, - ({ + } as {} as stackdriver.Breakpoint, + { id: 'with . in path', location: { path: path.join('test', '.', 'test-v8debugapi-code.js'), line: 5, }, - } as {}) as stackdriver.Breakpoint, - ({ + } as {} as stackdriver.Breakpoint, + { id: 'with . in path', location: {path: path.join('.', 'test-v8debugapi-code.js'), line: 5}, - } as {}) as stackdriver.Breakpoint, - ({ + } as {} as stackdriver.Breakpoint, + { id: 'with .. in path', location: { path: path.join('test', '..', 'test-v8debugapi-code.js'), line: 5, }, - } as {}) as stackdriver.Breakpoint, - ({ + } as {} as stackdriver.Breakpoint, + { id: 'with .. in path', location: { path: path.join('..', 'test', 'test-v8debugapi-code.js'), line: 5, }, - } as {}) as stackdriver.Breakpoint, + } as {} as stackdriver.Breakpoint, ]; breakpoints.forEach((bp: stackdriver.Breakpoint) => { @@ -685,12 +686,12 @@ describe('v8debugapi', () => { it('should throttle correctly', done => { let completed = false; // TODO(dominickramer): Have this actually implement Breakpoint - const bp: stackdriver.Breakpoint = ({ + const bp: stackdriver.Breakpoint = { id: breakpointInFoo.id, location: breakpointInFoo.location, action: 'LOG', logMessageFormat: 'cat', - } as {}) as stackdriver.Breakpoint; + } as {} as stackdriver.Breakpoint; api.set(bp, err1 => { let transcript = ''; let runCount = 0; @@ -722,14 +723,84 @@ describe('v8debugapi', () => { }); }); + describe('InspectorDebugApi', () => { + let oldLPS: number; + let oldDS: number; + + before(() => { + oldLPS = config.log.maxLogsPerSecond; + oldDS = config.log.logDelaySeconds; + config.log.maxLogsPerSecond = config.resetV8DebuggerThreshold * 3; + config.log.logDelaySeconds = 1; + }); + + after(() => { + config.log.maxLogsPerSecond = oldLPS; + config.log.logDelaySeconds = oldDS; + assert(stateIsClean(api)); + }); + + it('should perform v8 breakpoints reset when meeting threshold', done => { + // The test is only eligible for the InspectorDebugApi test target. + if (!(api instanceof InspectorDebugApi)) { + done(); + return; + } + + const bp: stackdriver.Breakpoint = { + id: breakpointInFoo.id, + location: breakpointInFoo.location, + action: 'LOG', + logMessageFormat: 'cat', + } as {} as stackdriver.Breakpoint; + api.set(bp, err1 => { + let logpointEvaluatedTimes = 0; + + assert.ifError(err1); + api.log( + bp, + () => { + logpointEvaluatedTimes += 1; + }, + () => false + ); + + const inspectorDebugApi = api as InspectorDebugApi; + const v8BeforeReset = inspectorDebugApi.v8; + + // The loop should trigger the breakpoints reset. + for (let i = 0; i < config.resetV8DebuggerThreshold; i++) { + code.foo(1); + } + + // Expect the current v8 data is no longer the previous one. + assert.notStrictEqual(inspectorDebugApi.v8, v8BeforeReset); + + // Make sure the logpoint is still triggered correctly after the second reset. + for (let i = 0; i < config.resetV8DebuggerThreshold + 1; i++) { + code.foo(1); + } + assert.strictEqual( + logpointEvaluatedTimes, + config.resetV8DebuggerThreshold * 2 + 1 + ); + + api.clear(bp, err2 => { + assert.ifError(err2); + done(); + }); + }); + }); + }); + describe('set and wait', () => { it('should be possible to wait on a breakpoint', done => { // clone a clean breakpointInFoo // TODO(dominickramer): Have this actually implement Breakpoint - const bp: stackdriver.Breakpoint = ({ + const bp: stackdriver.Breakpoint = { id: breakpointInFoo.id, location: breakpointInFoo.location, - } as {}) as stackdriver.Breakpoint; + } as {} as stackdriver.Breakpoint; api.set(bp, err1 => { assert.ifError(err1); api.wait(bp, err2 => { @@ -746,10 +817,10 @@ describe('v8debugapi', () => { }); it('should resolve actual line number hit rather than originally set for js files', done => { - const bp: stackdriver.Breakpoint = ({ + const bp: stackdriver.Breakpoint = { id: 'fake-id-124', location: {path: 'build/test/test-v8debugapi-code.js', line: 4}, - } as {}) as stackdriver.Breakpoint; + } as {} as stackdriver.Breakpoint; api.set(bp, err1 => { assert.ifError(err1); api.wait(bp, err2 => { @@ -842,10 +913,10 @@ describe('v8debugapi', () => { }; // clone a clean breakpointInFoo // TODO(dominickramer): Have this actually implement Breakpoint - const bp: stackdriver.Breakpoint = ({ + const bp: stackdriver.Breakpoint = { id: breakpointInFoo.id, location: breakpointInFoo.location, - } as {}) as stackdriver.Breakpoint; + } as {} as stackdriver.Breakpoint; api.set(bp, err1 => { assert.ifError(err1); api.wait(bp, err2 => { @@ -870,12 +941,12 @@ describe('v8debugapi', () => { it('should be possible to wait on a logpoint without expressions', done => { // TODO(dominickramer): Have this actually implement Breakpoint - const bp: stackdriver.Breakpoint = ({ + const bp: stackdriver.Breakpoint = { id: breakpointInFoo.id, action: 'LOG', logMessageFormat: 'Hello World', location: breakpointInFoo.location, - } as {}) as stackdriver.Breakpoint; + } as {} as stackdriver.Breakpoint; api.set(bp, err1 => { assert.ifError(err1); api.wait(bp, err2 => { @@ -894,10 +965,10 @@ describe('v8debugapi', () => { it('should capture state', done => { // clone a clean breakpointInFoo // TODO(dominickramer): Have this actually implement Breakpoint - const bp: stackdriver.Breakpoint = ({ + const bp: stackdriver.Breakpoint = { id: breakpointInFoo.id, location: breakpointInFoo.location, - } as {}) as stackdriver.Breakpoint; + } as {} as stackdriver.Breakpoint; api.set(bp, err1 => { assert.ifError(err1); api.wait(bp, err2 => { @@ -926,10 +997,10 @@ describe('v8debugapi', () => { it('should resolve correct frame count', done => { // clone a clean breakpointInFoo // TODO(dominickramer): Have this actually implement Breakpoint - const bp: stackdriver.Breakpoint = ({ + const bp: stackdriver.Breakpoint = { id: breakpointInFoo.id, location: breakpointInFoo.location, - } as {}) as stackdriver.Breakpoint; + } as {} as stackdriver.Breakpoint; const oldCount = config.capture.maxExpandFrames; config.capture.maxExpandFrames = 0; api.set(bp, err1 => { @@ -980,10 +1051,10 @@ describe('v8debugapi', () => { it('should capture correct frame count', done => { // clone a clean breakpointInFoo // TODO(dominickramer): Have this actually implement Breakpoint - const bp: stackdriver.Breakpoint = ({ + const bp: stackdriver.Breakpoint = { id: breakpointInFoo.id, location: breakpointInFoo.location, - } as {}) as stackdriver.Breakpoint; + } as {} as stackdriver.Breakpoint; const oldMax = config.capture.maxFrames; config.capture.maxFrames = 1; api.set(bp, err1 => { @@ -1012,11 +1083,11 @@ describe('v8debugapi', () => { it('should capture state with watch expressions', done => { // clone a clean breakpointInFoo // TODO(dominickramer): Have this actually implement Breakpoint - const bp: stackdriver.Breakpoint = ({ + const bp: stackdriver.Breakpoint = { id: breakpointInFoo.id, location: breakpointInFoo.location, expressions: ['process'], - } as {}) as stackdriver.Breakpoint; + } as {} as stackdriver.Breakpoint; const oldMaxProps = config.capture.maxProperties; const oldMaxData = config.capture.maxDataSize; config.capture.maxProperties = 0; @@ -1067,14 +1138,14 @@ describe('v8debugapi', () => { it('should report error for native prop or getter', done => { // TODO(dominickramer): Have this actually implement Breakpoint - const bp: stackdriver.Breakpoint = ({ + const bp: stackdriver.Breakpoint = { id: 'fake-id-124', // TODO(dominickramer): This path can be lest strict when this file has // been // converted to Typescript. location: {path: 'build/test/test-v8debugapi-code.js', line: 10}, expressions: ['process.env', 'hasGetter'], - } as {}) as stackdriver.Breakpoint; + } as {} as stackdriver.Breakpoint; const oldMaxData = config.capture.maxDataSize; config.capture.maxDataSize = 20000; api.set(bp, err1 => { @@ -1118,14 +1189,14 @@ describe('v8debugapi', () => { it('should work with array length despite being native', done => { // TODO(dominickramer): Have this actually implement Breakpoint - const bp: stackdriver.Breakpoint = ({ + const bp: stackdriver.Breakpoint = { id: breakpointInFoo.id, // TODO(dominickramer): This path can be lest strict when this file has // been // converted to Typescript. location: {path: 'build/test/test-v8debugapi-code.js', line: 6}, expressions: ['A'], - } as {}) as stackdriver.Breakpoint; + } as {} as stackdriver.Breakpoint; api.set(bp, err1 => { assert.ifError(err1); api.wait(bp, err2 => { @@ -1157,13 +1228,13 @@ describe('v8debugapi', () => { it('should limit string length', done => { // TODO(dominickramer): Have this actually implement Breakpoint - const bp: stackdriver.Breakpoint = ({ + const bp: stackdriver.Breakpoint = { id: 'fake-id-124', // TODO(dominickramer): This path can be lest strict when this file has // been // converted to Typescript. location: {path: 'build/test/test-v8debugapi-code.js', line: 10}, - } as {}) as stackdriver.Breakpoint; + } as {} as stackdriver.Breakpoint; const oldMaxLength = config.capture.maxStringLength; const oldMaxData = config.capture.maxDataSize; config.capture.maxStringLength = 3; @@ -1202,13 +1273,13 @@ describe('v8debugapi', () => { it('should limit array length', done => { // TODO(dominickramer): Have this actually implement Breakpoint - const bp: stackdriver.Breakpoint = ({ + const bp: stackdriver.Breakpoint = { id: 'fake-id-124', // TODO(dominickramer): This path can be lest strict when this file has // been // converted to Typescript. location: {path: 'build/test/test-v8debugapi-code.js', line: 6}, - } as {}) as stackdriver.Breakpoint; + } as {} as stackdriver.Breakpoint; const oldMax = config.capture.maxProperties; config.capture.maxProperties = 1; api.set(bp, err1 => { @@ -1241,13 +1312,13 @@ describe('v8debugapi', () => { it('should limit object length', done => { // TODO(dominickramer): Have this actually implement Breakpoint - const bp: stackdriver.Breakpoint = ({ + const bp: stackdriver.Breakpoint = { id: 'fake-id-124', // TODO(dominickramer): This path can be lest strict when this file has // been // converted to Typescript. location: {path: 'build/test/test-v8debugapi-code.js', line: 6}, - } as {}) as stackdriver.Breakpoint; + } as {} as stackdriver.Breakpoint; const oldMax = config.capture.maxProperties; config.capture.maxProperties = 1; api.set(bp, err1 => { @@ -1280,14 +1351,14 @@ describe('v8debugapi', () => { it('should not limit the length of an evaluated string based on maxStringLength', done => { // TODO(dominickramer): Have this actually implement Breakpoint - const bp: stackdriver.Breakpoint = ({ + const bp: stackdriver.Breakpoint = { id: 'fake-id-124', // TODO(dominickramer): This path can be lest strict when this file // has been // converted to Typescript. location: {path: 'build/test/test-v8debugapi-code.js', line: 10}, expressions: ['hasGetter'], - } as {}) as stackdriver.Breakpoint; + } as {} as stackdriver.Breakpoint; const oldMaxLength = config.capture.maxStringLength; const oldMaxData = config.capture.maxDataSize; config.capture.maxStringLength = 3; @@ -1320,14 +1391,14 @@ describe('v8debugapi', () => { it('should not limit the length of an evaluated array based on maxProperties', done => { // TODO(dominickramer): Have this actually implement Breakpoint - const bp: stackdriver.Breakpoint = ({ + const bp: stackdriver.Breakpoint = { id: 'fake-id-124', // TODO(dominickramer): This path can be lest strict when this file // has been // converted to Typescript. location: {path: 'build/test/test-v8debugapi-code.js', line: 6}, expressions: ['A'], - } as {}) as stackdriver.Breakpoint; + } as {} as stackdriver.Breakpoint; const oldMaxProps = config.capture.maxProperties; const oldMaxData = config.capture.maxDataSize; config.capture.maxProperties = 1; @@ -1357,14 +1428,14 @@ describe('v8debugapi', () => { it('should not limit the length of an evaluated object based on maxProperties', done => { // TODO(dominickramer): Have this actually implement Breakpoint - const bp: stackdriver.Breakpoint = ({ + const bp: stackdriver.Breakpoint = { id: 'fake-id-124', // TODO(dominickramer): This path can be lest strict when this file // has been // converted to Typescript. location: {path: 'build/test/test-v8debugapi-code.js', line: 6}, expressions: ['B'], - } as {}) as stackdriver.Breakpoint; + } as {} as stackdriver.Breakpoint; const oldMaxProps = config.capture.maxProperties; const oldMaxData = config.capture.maxDataSize; config.capture.maxProperties = 1; @@ -1393,14 +1464,14 @@ describe('v8debugapi', () => { it('should display an error for an evaluated array beyond maxDataSize', done => { // TODO(dominickramer): Have this actually implement Breakpoint - const bp: stackdriver.Breakpoint = ({ + const bp: stackdriver.Breakpoint = { id: 'fake-id-124', // TODO(dominickramer): This path can be lest strict when this file // has been // converted to Typescript. location: {path: 'build/test/test-v8debugapi-code.js', line: 6}, expressions: ['A'], - } as {}) as stackdriver.Breakpoint; + } as {} as stackdriver.Breakpoint; const oldMaxProps = config.capture.maxProperties; const oldMaxData = config.capture.maxDataSize; config.capture.maxProperties = 5; @@ -1431,14 +1502,14 @@ describe('v8debugapi', () => { it('should display an error for an evaluated object beyond maxDataSize', done => { // TODO(dominickramer): Have this actually implement Breakpoint - const bp: stackdriver.Breakpoint = ({ + const bp: stackdriver.Breakpoint = { id: 'fake-id-124', // TODO(dominickramer): This path can be lest strict when this file // has been // converted to Typescript. location: {path: 'build/test/test-v8debugapi-code.js', line: 6}, expressions: ['B'], - } as {}) as stackdriver.Breakpoint; + } as {} as stackdriver.Breakpoint; const oldMaxProps = config.capture.maxProperties; const oldMaxData = config.capture.maxDataSize; config.capture.maxProperties = 5; @@ -1469,14 +1540,14 @@ describe('v8debugapi', () => { it('should set the correct status messages if maxDataSize is reached', done => { // TODO(dominickramer): Have this actually implement Breakpoint - const bp: stackdriver.Breakpoint = ({ + const bp: stackdriver.Breakpoint = { id: 'fake-id-124', // TODO(dominickramer): This path can be lest strict when this file // has been // converted to Typescript. location: {path: 'build/test/test-v8debugapi-code.js', line: 6}, expressions: ['A'], - } as {}) as stackdriver.Breakpoint; + } as {} as stackdriver.Breakpoint; const oldMaxProps = config.capture.maxProperties; const oldMaxData = config.capture.maxDataSize; config.capture.maxProperties = 1; @@ -1515,7 +1586,7 @@ describe('v8debugapi', () => { it('should capture without values for invalid watch expressions', done => { // clone a clean breakpointInFoo // TODO(dominickramer): Have this actually implement Breakpoint - const bp: stackdriver.Breakpoint = ({ + const bp: stackdriver.Breakpoint = { id: breakpointInFoo.id, location: breakpointInFoo.location, expressions: [ @@ -1525,7 +1596,7 @@ describe('v8debugapi', () => { 'i', 'process._not._def', ], - } as {}) as stackdriver.Breakpoint; + } as {} as stackdriver.Breakpoint; api.set(bp, err1 => { assert.ifError(err1); api.wait(bp, err2 => { @@ -1555,11 +1626,11 @@ describe('v8debugapi', () => { it('should be possible to set conditional breakpoints', done => { // clone a clean breakpointInFoo // TODO(dominickramer): Have this actually implement Breakpoint - const bp: stackdriver.Breakpoint = ({ + const bp: stackdriver.Breakpoint = { id: breakpointInFoo.id, location: breakpointInFoo.location, condition: 'n===5', - } as {}) as stackdriver.Breakpoint; + } as {} as stackdriver.Breakpoint; api.set(bp, err1 => { assert.ifError(err1); api.wait(bp, err2 => { @@ -1583,7 +1654,7 @@ describe('v8debugapi', () => { it('should be possible to set conditional breakpoints in coffeescript', done => { // TODO(dominickramer): Have this actually implement Breakpoint - const bp: stackdriver.Breakpoint = ({ + const bp: stackdriver.Breakpoint = { id: 'coffee-id-1729', // TODO(dominickramer): Determine if this path should contain 'build' location: { @@ -1598,7 +1669,7 @@ describe('v8debugapi', () => { line: 3, }, condition: 'if n == 3 then true else false', - } as {}) as stackdriver.Breakpoint; + } as {} as stackdriver.Breakpoint; // eslint-disable-next-line @typescript-eslint/no-var-requires const tt = require('./fixtures/coffee/transpile'); api.set(bp, err1 => { @@ -1625,7 +1696,7 @@ describe('v8debugapi', () => { it('should show error for invalid conditions in coffeescript', done => { // TODO(dominickramer): Have this actually implement Breakpoint - const bp: stackdriver.Breakpoint = ({ + const bp: stackdriver.Breakpoint = { id: 'coffee-id-1729', location: { path: path.join( @@ -1638,7 +1709,7 @@ describe('v8debugapi', () => { line: 3, }, condition: 'process=false', - } as {}) as stackdriver.Breakpoint; + } as {} as stackdriver.Breakpoint; api.set(bp, err => { assert(err); assert.strictEqual(err!.message, 'Error compiling condition.'); @@ -1648,7 +1719,7 @@ describe('v8debugapi', () => { it('should be possible to set conditional breakpoints with babel', done => { // TODO(dominickramer): Have this actually implement Breakpoint - const bp: stackdriver.Breakpoint = ({ + const bp: stackdriver.Breakpoint = { id: 'babel-id-1729', // TODO(dominickramer): Determine if this path should contain 'build' location: { @@ -1663,7 +1734,7 @@ describe('v8debugapi', () => { line: 2, }, condition: 'i + j === 3', - } as {}) as stackdriver.Breakpoint; + } as {} as stackdriver.Breakpoint; // eslint-disable-next-line @typescript-eslint/no-var-requires const tt = require('./fixtures/es6/transpile'); api.set(bp, err1 => { @@ -1690,7 +1761,7 @@ describe('v8debugapi', () => { it('should be possible to view watch expressions in coffeescript', done => { // TODO(dominickramer): Have this actually implement Breakpoint - const bp: stackdriver.Breakpoint = ({ + const bp: stackdriver.Breakpoint = { id: 'coffee-id-1729', // TODO(dominickramer): Determine if this path should contain 'build' location: { @@ -1705,7 +1776,7 @@ describe('v8debugapi', () => { line: 3, }, expressions: ['if n == 3 then Math.PI * n else n'], - } as {}) as stackdriver.Breakpoint; + } as {} as stackdriver.Breakpoint; // eslint-disable-next-line @typescript-eslint/no-var-requires const tt = require('./fixtures/coffee/transpile'); api.set(bp, err1 => { @@ -1736,7 +1807,7 @@ describe('v8debugapi', () => { it('should capture without values for invalid watch expressions in coffeescript', done => { // TODO(dominickramer): Have this actually implement Breakpoint - const bp: stackdriver.Breakpoint = ({ + const bp: stackdriver.Breakpoint = { id: 'coffee-id-1729', // TODO(dominickramer): Determine if this path should contain 'build' location: { @@ -1757,7 +1828,7 @@ describe('v8debugapi', () => { '((x) -> x x) n', 'return', ], - } as {}) as stackdriver.Breakpoint; + } as {} as stackdriver.Breakpoint; // eslint-disable-next-line @typescript-eslint/no-var-requires const tt = require('./fixtures/coffee/transpile'); api.set(bp, err => { @@ -1805,11 +1876,11 @@ describe('v8debugapi', () => { it('should remove listener when breakpoint is cleared before hitting', done => { // TODO(dominickramer): Have this actually implement Breakpoint - const bp: stackdriver.Breakpoint = ({ + const bp: stackdriver.Breakpoint = { id: breakpointInFoo.id, location: breakpointInFoo.location, condition: 'n===447', - } as {}) as stackdriver.Breakpoint; + } as {} as stackdriver.Breakpoint; api.set(bp, err1 => { assert.ifError(err1); api.wait(bp, () => { @@ -1830,15 +1901,15 @@ describe('v8debugapi', () => { it('should be possible to set multiple breakpoints at once', done => { // TODO(dominickramer): Have this actually implement Breakpoint - const bp1: stackdriver.Breakpoint = ({ + const bp1: stackdriver.Breakpoint = { id: 'bp1', location: {path: __filename, line: 5}, - } as {}) as stackdriver.Breakpoint; + } as {} as stackdriver.Breakpoint; // TODO(dominickramer): Have this actually implement Breakpoint - const bp2: stackdriver.Breakpoint = ({ + const bp2: stackdriver.Breakpoint = { id: 'bp2', location: {path: __filename, line: 6}, - } as {}) as stackdriver.Breakpoint; + } as {} as stackdriver.Breakpoint; api.set(bp1, err1 => { assert.ifError(err1); api.set(bp2, err2 => { @@ -1861,10 +1932,10 @@ describe('v8debugapi', () => { // eslint-disable-next-line @typescript-eslint/no-var-requires const foo = require('./fixtures/foo.js'); // TODO(dominickramer): Have this actually implement Breakpoint - const bp: stackdriver.Breakpoint = ({ + const bp: stackdriver.Breakpoint = { id: 'bp-line-1', location: {path: 'foo.js', line: 1, column: 45}, - } as {}) as stackdriver.Breakpoint; + } as {} as stackdriver.Breakpoint; api.set(bp, err1 => { assert.ifError(err1); api.wait(bp, err2 => { @@ -1899,10 +1970,10 @@ describe('v8debugapi', () => { // clone a clean breakpointInFoo // TODO(dominickramer): Have this actually implement Breakpoint - const bp: stackdriver.Breakpoint = ({ + const bp: stackdriver.Breakpoint = { id: breakpointInFoo.id, location: breakpointInFoo.location, - } as {}) as stackdriver.Breakpoint; + } as {} as stackdriver.Breakpoint; api.set(bp, err1 => { assert.ifError(err1); // TODO(dominickramer): Determine if the err parameter should be used. @@ -1919,13 +1990,13 @@ describe('v8debugapi', () => { }); it('should capture state in transpiled TS async functions', done => { - const bp: stackdriver.Breakpoint = ({ + const bp: stackdriver.Breakpoint = { id: 'async-id-1', location: { path: path.join('.', 'test', 'fixtures', 'ts', 'async.js'), line: 71, }, - } as {}) as stackdriver.Breakpoint; + } as {} as stackdriver.Breakpoint; // eslint-disable-next-line @typescript-eslint/no-var-requires const run = require('./fixtures/ts/async.js'); @@ -1970,15 +2041,8 @@ describe('v8debugapi.findScripts', () => { 'a', 'hello.js' )]: {hash: 'fake', lines: 5}, - [path.join( - 'my', - 'project', - 'root', - 'test', - 'fixtures', - 'a', - 'hello.js' - )]: {hash: 'fake', lines: 50}, + [path.join('my', 'project', 'root', 'test', 'fixtures', 'a', 'hello.js')]: + {hash: 'fake', lines: 50}, }; const scriptPath = path.join( 'my',