Skip to content

Commit ae05c61

Browse files
committed
fix: bad handling of first and last model samples
1 parent c79fd08 commit ae05c61

File tree

13 files changed

+1264
-461
lines changed

13 files changed

+1264
-461
lines changed

package-lock.json

+944-412
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+2-5
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
"test": "concurrently \"npm:test:*\"",
1818
"test:lint": "eslint \"packages/**/*.{ts,tsx}\"",
1919
"test:fmt": "prettier --list-different \"packages/**/*.{ts,tsx,css}\" \"!**/out/**\"",
20-
"test:core:unit": "cd packages/vscode-js-profile-core && mocha",
20+
"test:unit": "vitest",
2121
"vscode:prepublish": "npm run compile"
2222
},
2323
"prettier": {
@@ -28,15 +28,12 @@
2828
"tabWidth": 2
2929
},
3030
"devDependencies": {
31-
"@types/chai": "^4.2.11",
3231
"@types/glob": "^7.1.1",
33-
"@types/mocha": "^7.0.1",
3432
"@types/node": "^12.11.7",
3533
"@types/vscode": "^1.61.0",
3634
"@typescript-eslint/eslint-plugin": "^2.18.0",
3735
"@typescript-eslint/parser": "^2.18.0",
3836
"autoprefixer": "^9.7.4",
39-
"chai": "^4.2.0",
4037
"chokidar-cli": "^2.1.0",
4138
"concurrently": "^5.1.0",
4239
"cpy-cli": "^3.1.0",
@@ -50,7 +47,6 @@
5047
"glob": "^7.1.6",
5148
"html-webpack-plugin": "^5.5.0",
5249
"lerna": "^3.20.2",
53-
"mocha": "^7.0.1",
5450
"os-browserify": "^0.3.0",
5551
"path-browserify": "^1.0.1",
5652
"postcss-loader": "^3.0.0",
@@ -64,6 +60,7 @@
6460
"svg-inline-loader": "^0.8.2",
6561
"ts-loader": "^6.2.1",
6662
"typescript": "^3.7.5",
63+
"vitest": "^0.12.6",
6764
"vscode-debugprotocol": "^1.39.0",
6865
"vscode-test": "^1.3.0",
6966
"webpack": "^5.70.0",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
// Vitest Snapshot v1
2+
3+
exports[`model > models #0 1`] = `
4+
{
5+
"duration": 1100000,
6+
"locations": [
7+
{
8+
"aggregateTime": 900000,
9+
"callFrame": {
10+
"columnNumber": 0,
11+
"functionName": "Native",
12+
"lineNumber": 0,
13+
"scriptId": "",
14+
"url": "",
15+
},
16+
"category": 1,
17+
"id": 0,
18+
"selfTime": 900000,
19+
"src": {
20+
"columnNumber": 0,
21+
"lineNumber": 0,
22+
"source": {
23+
"name": "",
24+
"path": "",
25+
"sourceReference": 0,
26+
},
27+
},
28+
"ticks": 0,
29+
},
30+
{
31+
"aggregateTime": 900000,
32+
"callFrame": {
33+
"columnNumber": 0,
34+
"functionName": "func1",
35+
"lineNumber": 3,
36+
"scriptId": "468",
37+
"url": "test.js",
38+
},
39+
"category": 1,
40+
"id": 1,
41+
"selfTime": 0,
42+
"src": {
43+
"columnNumber": 0,
44+
"lineNumber": 3,
45+
"relativePath": "test.js",
46+
"source": {
47+
"name": "test.js",
48+
"path": "test.js",
49+
"sourceReference": 0,
50+
},
51+
},
52+
"ticks": 0,
53+
},
54+
{
55+
"aggregateTime": 900000,
56+
"callFrame": {
57+
"columnNumber": 0,
58+
"functionName": "<anonymous>",
59+
"lineNumber": 8,
60+
"scriptId": "468",
61+
"url": "test.js",
62+
},
63+
"category": 1,
64+
"id": 2,
65+
"selfTime": 0,
66+
"src": {
67+
"columnNumber": 0,
68+
"lineNumber": 8,
69+
"relativePath": "test.js",
70+
"source": {
71+
"name": "test.js",
72+
"path": "test.js",
73+
"sourceReference": 0,
74+
},
75+
},
76+
"ticks": 0,
77+
},
78+
],
79+
"nodes": [
80+
{
81+
"aggregateTime": 900000,
82+
"children": [],
83+
"id": 0,
84+
"locationId": 0,
85+
"parent": 1,
86+
"selfTime": 900000,
87+
},
88+
{
89+
"aggregateTime": 900000,
90+
"children": [
91+
0,
92+
],
93+
"id": 1,
94+
"locationId": 1,
95+
"parent": 2,
96+
"selfTime": 0,
97+
},
98+
{
99+
"aggregateTime": 900000,
100+
"children": [
101+
1,
102+
],
103+
"id": 2,
104+
"locationId": 2,
105+
"selfTime": 0,
106+
},
107+
],
108+
"rootPath": ".",
109+
"samples": [
110+
0,
111+
0,
112+
0,
113+
],
114+
"timeDeltas": [
115+
200000,
116+
400000,
117+
500000,
118+
0,
119+
],
120+
}
121+
`;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/*---------------------------------------------------------
2+
* Copyright (C) Microsoft Corporation. All rights reserved.
3+
*--------------------------------------------------------*/
4+
5+
import { describe, expect, it } from 'vitest';
6+
import { buildModel } from './model';
7+
8+
describe('model', () => {
9+
for (let i = 0; i < profiles.length; i++) {
10+
it(`models #${i}`, () => {
11+
expect(buildModel(profiles[i])).toMatchSnapshot();
12+
});
13+
}
14+
});
15+
16+
const profiles = [
17+
{
18+
nodes: [
19+
{
20+
id: 0,
21+
hitCount: 3,
22+
callFrame: {
23+
functionName: 'Native',
24+
scriptId: '',
25+
url: '',
26+
lineNumber: 0,
27+
columnNumber: 0,
28+
},
29+
locationId: 0,
30+
},
31+
{
32+
id: 1,
33+
hitCount: 1,
34+
callFrame: {
35+
functionName: 'func1',
36+
scriptId: '468',
37+
url: 'test.js',
38+
lineNumber: 3,
39+
columnNumber: 0,
40+
},
41+
children: [0],
42+
locationId: 1,
43+
},
44+
{
45+
id: 2,
46+
hitCount: 1,
47+
callFrame: {
48+
functionName: '<anonymous>',
49+
scriptId: '468',
50+
url: 'test.js',
51+
lineNumber: 8,
52+
columnNumber: 0,
53+
},
54+
children: [1],
55+
locationId: 2,
56+
},
57+
],
58+
samples: [0, 0, 0],
59+
timeDeltas: [200000, 400000, 500000],
60+
startTime: 1652739599999000,
61+
endTime: 1652739601099000,
62+
$vscode: {
63+
rootPath: '.',
64+
locations: [
65+
{
66+
callFrame: {
67+
functionName: 'Native',
68+
scriptId: '',
69+
url: '',
70+
lineNumber: 0,
71+
columnNumber: 0,
72+
},
73+
locations: [
74+
{
75+
lineNumber: 0,
76+
columnNumber: 0,
77+
source: { name: '', path: '', sourceReference: 0 },
78+
},
79+
],
80+
},
81+
{
82+
callFrame: {
83+
functionName: 'func1',
84+
scriptId: '468',
85+
url: 'test.js',
86+
lineNumber: 3,
87+
columnNumber: 0,
88+
},
89+
locations: [
90+
{
91+
lineNumber: 3,
92+
columnNumber: 0,
93+
source: {
94+
name: 'test.js',
95+
path: 'test.js',
96+
sourceReference: 0,
97+
},
98+
},
99+
],
100+
},
101+
{
102+
callFrame: {
103+
functionName: '<anonymous>',
104+
scriptId: '468',
105+
url: 'test.js',
106+
lineNumber: 8,
107+
columnNumber: 0,
108+
},
109+
locations: [
110+
{
111+
lineNumber: 8,
112+
columnNumber: 0,
113+
source: {
114+
name: 'test.js',
115+
path: 'test.js',
116+
sourceReference: 0,
117+
},
118+
},
119+
],
120+
},
121+
],
122+
},
123+
},
124+
];

packages/vscode-js-profile-core/src/cpu/model.ts

+14-3
Original file line numberDiff line numberDiff line change
@@ -217,10 +217,21 @@ export const buildModel = (profile: ICpuProfileRaw): IProfileModel => {
217217

218218
// 2. The profile samples are the 'bottom-most' node, the currently running
219219
// code. Sum of these in the self time.
220-
for (let i = 1; i < timeDeltas.length; i++) {
221-
nodes[mapId(samples[i])].selfTime += timeDeltas[i - 1];
220+
const duration = profile.endTime - profile.startTime;
221+
let lastNodeTime = duration - timeDeltas[0];
222+
for (let i = 0; i < timeDeltas.length - 1; i++) {
223+
const d = timeDeltas[i + 1];
224+
nodes[mapId(samples[i])].selfTime += d;
225+
lastNodeTime -= d;
222226
}
223227

228+
// Add in an extra time delta for the last sample. `timeDeltas[0]` is the
229+
// time before the first sample, and the time of the last sample is only
230+
// derived (approximately) by the missing time in the sum of deltas. Save
231+
// some work by calculating it here.
232+
nodes[mapId(samples[timeDeltas.length - 1])].selfTime += lastNodeTime;
233+
timeDeltas.push(lastNodeTime);
234+
224235
// 3. Add the aggregate times for all node children and locations
225236
for (let i = 0; i < nodes.length; i++) {
226237
const node = nodes[i];
@@ -235,6 +246,6 @@ export const buildModel = (profile: ICpuProfileRaw): IProfileModel => {
235246
samples: samples.map(mapId),
236247
timeDeltas,
237248
rootPath: profile.$vscode?.rootPath,
238-
duration: profile.endTime - profile.startTime,
249+
duration,
239250
};
240251
};

packages/vscode-js-profile-core/src/open-location.ts

+14-5
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,23 @@
22
* Copyright (C) Microsoft Corporation. All rights reserved.
33
*--------------------------------------------------------*/
44

5-
import { DebugProtocol as Dap } from 'vscode-debugprotocol';
6-
import * as vscode from 'vscode';
7-
import { properRelative, exists } from './path';
8-
import { resolve } from 'path';
9-
import { ISourceLocation } from './location-mapping';
105
import { Protocol as Cdp } from 'devtools-protocol';
116
import { tmpdir } from 'os';
7+
import { resolve } from 'path';
8+
import * as vscode from 'vscode';
9+
import { DebugProtocol as Dap } from 'vscode-debugprotocol';
1210
import { DownloadFileProvider } from './download-file-provider';
11+
import { ISourceLocation } from './location-mapping';
12+
import { properRelative } from './path';
13+
14+
const exists = async (file: string) => {
15+
try {
16+
await vscode.workspace.fs.stat(vscode.Uri.file(file));
17+
return true;
18+
} catch {
19+
return false;
20+
}
21+
};
1322

1423
/**
1524
* Gets the best location for display among the given set of candidates

packages/vscode-js-profile-core/src/path.ts

-10
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,6 @@
33
*--------------------------------------------------------*/
44

55
import * as path from 'path';
6-
import * as vscode from 'vscode';
7-
8-
export const exists = async (file: string) => {
9-
try {
10-
await vscode.workspace.fs.stat(vscode.Uri.file(file));
11-
return true;
12-
} catch {
13-
return false;
14-
}
15-
};
166

177
/**
188
* Resolves path segments properly based on whether they appear to be c:/ -style or / style.

packages/vscode-js-profile-core/src/ql/index.ts

+2-18
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,9 @@
33
*--------------------------------------------------------*/
44

55
import { compile, lex } from './parser';
6+
import { Property } from './types';
67

7-
export const enum PropertyType {
8-
String,
9-
Number,
10-
}
11-
12-
export interface IPropertyToPrimitiveType {
13-
[PropertyType.Number]: number;
14-
[PropertyType.String]: string;
15-
}
16-
17-
export interface IBasePropertyDefinition<TNode, TProp extends PropertyType> {
18-
type: TProp;
19-
accessor: (node: TNode) => IPropertyToPrimitiveType[TProp];
20-
}
21-
22-
export type StringPropertyDefinition<T> = IBasePropertyDefinition<T, PropertyType.String>;
23-
export type NumberPropertyDefinition<T> = IBasePropertyDefinition<T, PropertyType.Number>;
24-
export type Property<T> = StringPropertyDefinition<T> | NumberPropertyDefinition<T>;
8+
export * from './types';
259

2610
/**
2711
* Data source that provides a stream of items, and includes the list of

0 commit comments

Comments
 (0)