Skip to content

Commit 32c3172

Browse files
committed
Migrates from stack trace processing to DebugLocation
1 parent abdf85c commit 32c3172

21 files changed

+254
-189
lines changed
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
export type DebugLocation = DebugLocationImpl | undefined;
7+
8+
export namespace DebugLocation {
9+
let enabled = false;
10+
11+
export function enable(): void {
12+
enabled = true;
13+
}
14+
15+
export function ofCaller(): DebugLocation {
16+
if (!enabled) {
17+
return undefined;
18+
}
19+
const Err = Error as any as { stackTraceLimit: number }; // For the monaco editor checks, which don't have the nodejs types.
20+
21+
const l = Err.stackTraceLimit;
22+
Err.stackTraceLimit = 3;
23+
const stack = new Error().stack!;
24+
Err.stackTraceLimit = l;
25+
26+
return DebugLocationImpl.fromStack(stack, 2);
27+
}
28+
}
29+
30+
class DebugLocationImpl implements ILocation {
31+
public static fromStack(stack: string, parentIdx: number): DebugLocationImpl | undefined {
32+
const lines = stack.split('\n');
33+
const location = parseLine(lines[parentIdx + 1]);
34+
if (location) {
35+
return new DebugLocationImpl(
36+
location.fileName,
37+
location.line,
38+
location.column,
39+
location.id
40+
);
41+
} else {
42+
return undefined;
43+
}
44+
}
45+
46+
constructor(
47+
public readonly fileName: string,
48+
public readonly line: number,
49+
public readonly column: number,
50+
public readonly id: string,
51+
) {
52+
}
53+
}
54+
55+
56+
export interface ILocation {
57+
fileName: string;
58+
line: number;
59+
column: number;
60+
id: string;
61+
}
62+
63+
function parseLine(stackLine: string): ILocation | undefined {
64+
const match = stackLine.match(/\((.*):(\d+):(\d+)\)/);
65+
if (match) {
66+
return {
67+
fileName: match[1],
68+
line: parseInt(match[2]),
69+
column: parseInt(match[3]),
70+
id: stackLine,
71+
};
72+
}
73+
74+
const match2 = stackLine.match(/at ([^\(\)]*):(\d+):(\d+)/);
75+
76+
if (match2) {
77+
return {
78+
fileName: match2[1],
79+
line: parseInt(match2[2]),
80+
column: parseInt(match2[3]),
81+
id: stackLine,
82+
};
83+
}
84+
85+
return undefined;
86+
}

src/vs/base/common/observableInternal/experimental/reducer.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { subtransaction } from '../transaction.js';
99
import { IChangeTracker } from '../changeTracker.js';
1010
import { DebugNameData, DebugOwner } from '../debugName.js';
1111
import { DerivedWithSetter, IDerivedReader } from '../observables/derivedImpl.js';
12+
import { DebugLocation } from '../debugLocation.js';
1213

1314
export interface IReducerOptions<T, TChangeSummary = void, TOutChange = void> {
1415
/**
@@ -73,7 +74,8 @@ export function observableReducerSettable<T, TInChanges, TOutChange = void>(owne
7374
prevValue = value;
7475
d.setValue(value, tx, change);
7576
});
76-
}
77+
},
78+
DebugLocation.ofCaller()
7779
);
7880

7981
return d;

src/vs/base/common/observableInternal/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ export { observableValue } from './observables/observableValue.js';
3434

3535
export { ObservableSet } from './set.js';
3636
export { ObservableMap } from './map.js';
37+
export { DebugLocation } from './debugLocation.js';
3738

3839
import { addLogger, setLogObservableFn } from './logging/logging.js';
3940
import { ConsoleObservableLogger, logObservableToConsole } from './logging/consoleObservableLogger.js';

src/vs/base/common/observableInternal/logging/debugger/devToolsLogger.ts

Lines changed: 17 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,15 @@ import { IChangeInformation, IObservableLogger } from '../logging.js';
99
import { formatValue } from '../consoleObservableLogger.js';
1010
import { ObsDebuggerApi, IObsDeclaration, ObsInstanceId, ObsStateUpdate, ITransactionState, ObserverInstanceState } from './debuggerApi.js';
1111
import { registerDebugChannel } from './debuggerRpc.js';
12-
import { deepAssign, deepAssignDeleteNulls, getFirstStackFrameOutsideOf, ILocation, Throttler } from './utils.js';
12+
import { deepAssign, deepAssignDeleteNulls, Throttler } from './utils.js';
1313
import { isDefined } from '../../../types.js';
1414
import { FromEventObservable } from '../../observables/observableFromEvent.js';
1515
import { BugIndicatingError, onUnexpectedError } from '../../../errors.js';
1616
import { IObservable, IObserver } from '../../base.js';
1717
import { BaseObservable } from '../../observables/baseObservable.js';
1818
import { Derived, DerivedState } from '../../observables/derivedImpl.js';
1919
import { ObservableValue } from '../../observables/observableValue.js';
20+
import { DebugLocation } from '../../debugLocation.js';
2021

2122
interface IInstanceInfo {
2223
declarationId: number;
@@ -272,7 +273,9 @@ export class DevToolsLogger implements IObservableLogger {
272273
return undefined;
273274
}
274275

275-
private constructor() { }
276+
private constructor() {
277+
DebugLocation.enable();
278+
}
276279

277280
private _pendingChanges: ObsStateUpdate | null = null;
278281
private readonly _changeThrottler = new Throttler();
@@ -298,54 +301,29 @@ export class DevToolsLogger implements IObservableLogger {
298301
}
299302
};
300303

301-
private _getDeclarationId(type: IObsDeclaration['type']): number {
302-
303-
let shallow = true;
304-
let loc!: ILocation;
305-
306-
const Err = Error as any as { stackTraceLimit: number }; // For the monaco editor checks, which don't have the nodejs types.
307-
308-
while (true) {
309-
const l = Err.stackTraceLimit;
310-
Err.stackTraceLimit = shallow ? 6 : 20;
311-
const stack = new Error().stack!;
312-
Err.stackTraceLimit = l;
313-
314-
let result = getFirstStackFrameOutsideOf(stack, /[/\\]observableInternal[/\\]|\.observe|[/\\]util(s)?\./);
315-
316-
if (!shallow && !result) {
317-
result = getFirstStackFrameOutsideOf(stack, /[/\\]observableInternal[/\\]|\.observe/)!;
318-
}
319-
if (result) {
320-
loc = result;
321-
break;
322-
}
323-
if (!shallow) {
324-
console.error('Could not find location for declaration', new Error().stack);
325-
loc = { fileName: 'unknown', line: 0, column: 0, id: 'unknown' };
326-
break;
327-
}
328-
shallow = false;
304+
private _getDeclarationId(type: IObsDeclaration['type'], location: DebugLocation): number {
305+
if (!location) {
306+
return -1;
329307
}
330308

331-
let decInfo = this._declarations.get(loc.id);
309+
let decInfo = this._declarations.get(location.id);
332310
if (decInfo === undefined) {
333311
decInfo = {
334312
id: this._declarationId++,
335313
type,
336-
url: loc.fileName,
337-
line: loc.line,
338-
column: loc.column,
314+
url: location.fileName,
315+
line: location.line,
316+
column: location.column,
339317
};
340-
this._declarations.set(loc.id, decInfo);
318+
this._declarations.set(location.id, decInfo);
341319

342320
this._handleChange({ decls: { [decInfo.id]: decInfo } });
343321
}
344322
return decInfo.id;
345323
}
346324

347-
handleObservableCreated(observable: IObservable<any>): void {
348-
const declarationId = this._getDeclarationId('observable/value');
325+
handleObservableCreated(observable: IObservable<any>, location: DebugLocation): void {
326+
const declarationId = this._getDeclarationId('observable/value', location);
349327

350328
const info: IObservableInfo = {
351329
declarationId,
@@ -405,8 +383,8 @@ export class DevToolsLogger implements IObservableLogger {
405383
}
406384
}
407385

408-
handleAutorunCreated(autorun: AutorunObserver): void {
409-
const declarationId = this._getDeclarationId('autorun');
386+
handleAutorunCreated(autorun: AutorunObserver, location: DebugLocation): void {
387+
const declarationId = this._getDeclarationId('autorun', location);
410388
const info: IAutorunInfo = {
411389
declarationId,
412390
instanceId: this._instanceId++,

src/vs/base/common/observableInternal/logging/debugger/utils.ts

Lines changed: 0 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -5,64 +5,6 @@
55

66
import { IDisposable } from '../../../lifecycle.js';
77

8-
export function getFirstStackFrameOutsideOf(stack: string, pattern?: RegExp): ILocation | undefined {
9-
const lines = stack.split('\n');
10-
11-
for (let i = 1; i < lines.length; i++) {
12-
const line = lines[i];
13-
14-
if (pattern && pattern.test(line)) {
15-
continue;
16-
}
17-
18-
const showFramesUpMatch = line.match(/\$show(\d+)FramesUp/);
19-
if (showFramesUpMatch) {
20-
const n = parseInt(showFramesUpMatch[1], 10);
21-
i += (n - 1);
22-
continue;
23-
}
24-
25-
const result = parseLine(line);
26-
if (result) {
27-
return result;
28-
}
29-
}
30-
31-
return undefined;
32-
}
33-
34-
export interface ILocation {
35-
fileName: string;
36-
line: number;
37-
column: number;
38-
id: string;
39-
}
40-
41-
function parseLine(stackLine: string): ILocation | undefined {
42-
const match = stackLine.match(/\((.*):(\d+):(\d+)\)/);
43-
if (match) {
44-
return {
45-
fileName: match[1],
46-
line: parseInt(match[2]),
47-
column: parseInt(match[3]),
48-
id: stackLine,
49-
};
50-
}
51-
52-
const match2 = stackLine.match(/at ([^\(\)]*):(\d+):(\d+)/);
53-
54-
if (match2) {
55-
return {
56-
fileName: match2[1],
57-
line: parseInt(match2[2]),
58-
column: parseInt(match2[3]),
59-
id: stackLine,
60-
};
61-
}
62-
63-
return undefined;
64-
}
65-
668
export class Debouncer implements IDisposable {
679
private _timeout: Timeout | undefined = undefined;
6810

src/vs/base/common/observableInternal/logging/logging.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { AutorunObserver } from '../reactions/autorunImpl.js';
77
import { IObservable } from '../base.js';
88
import { TransactionImpl } from '../transaction.js';
99
import type { Derived } from '../observables/derivedImpl.js';
10+
import { DebugLocation } from '../debugLocation.js';
1011

1112
let globalObservableLogger: IObservableLogger | undefined;
1213

@@ -44,12 +45,12 @@ export interface IChangeInformation {
4445
}
4546

4647
export interface IObservableLogger {
47-
handleObservableCreated(observable: IObservable<any>): void;
48+
handleObservableCreated(observable: IObservable<any>, location: DebugLocation): void;
4849
handleOnListenerCountChanged(observable: IObservable<any>, newCount: number): void;
4950

5051
handleObservableUpdated(observable: IObservable<any>, info: IChangeInformation): void;
5152

52-
handleAutorunCreated(autorun: AutorunObserver): void;
53+
handleAutorunCreated(autorun: AutorunObserver, location: DebugLocation): void;
5354
handleAutorunDisposed(autorun: AutorunObserver): void;
5455
handleAutorunDependencyChanged(autorun: AutorunObserver, observable: IObservable<any>, change: unknown): void;
5556
handleAutorunStarted(autorun: AutorunObserver): void;
@@ -67,9 +68,9 @@ class ComposedLogger implements IObservableLogger {
6768
public readonly loggers: IObservableLogger[],
6869
) { }
6970

70-
handleObservableCreated(observable: IObservable<any>): void {
71+
handleObservableCreated(observable: IObservable<any>, location: DebugLocation): void {
7172
for (const logger of this.loggers) {
72-
logger.handleObservableCreated(observable);
73+
logger.handleObservableCreated(observable, location);
7374
}
7475
}
7576
handleOnListenerCountChanged(observable: IObservable<any>, newCount: number): void {
@@ -82,9 +83,9 @@ class ComposedLogger implements IObservableLogger {
8283
logger.handleObservableUpdated(observable, info);
8384
}
8485
}
85-
handleAutorunCreated(autorun: AutorunObserver): void {
86+
handleAutorunCreated(autorun: AutorunObserver, location: DebugLocation): void {
8687
for (const logger of this.loggers) {
87-
logger.handleAutorunCreated(autorun);
88+
logger.handleAutorunCreated(autorun, location);
8889
}
8990
}
9091
handleAutorunDisposed(autorun: AutorunObserver): void {

src/vs/base/common/observableInternal/observables/baseObservable.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import { IObservableWithChange, IObserver, IReader, IObservable } from '../base.js';
77
import { DisposableStore } from '../commonFacade/deps.js';
8+
import { DebugLocation } from '../debugLocation.js';
89
import { DebugOwner, getFunctionName } from '../debugName.js';
910
import { getLogger, logObservable } from '../logging/logging.js';
1011
import type { keepObserved, recomputeInitiallyAndOnChange } from '../utils/utils.js';
@@ -124,9 +125,9 @@ export abstract class ConvenientObservable<T, TChange> implements IObservableWit
124125
export abstract class BaseObservable<T, TChange = void> extends ConvenientObservable<T, TChange> {
125126
protected readonly _observers = new Set<IObserver>();
126127

127-
constructor() {
128+
constructor(debugLocation: DebugLocation) {
128129
super();
129-
getLogger()?.handleObservableCreated(this);
130+
getLogger()?.handleObservableCreated(this, debugLocation);
130131
}
131132

132133
public addObserver(observer: IObserver): void {
@@ -157,7 +158,7 @@ export abstract class BaseObservable<T, TChange = void> extends ConvenientObserv
157158
const hadLogger = !!getLogger();
158159
logObservable(this);
159160
if (!hadLogger) {
160-
getLogger()?.handleObservableCreated(this);
161+
getLogger()?.handleObservableCreated(this, DebugLocation.ofCaller());
161162
}
162163
return this;
163164
}

0 commit comments

Comments
 (0)