Skip to content

Commit 206d0ec

Browse files
eanders-msthsparks
andauthored
perf milestone and measurement updates for minecraft (#10309)
* wip * telemetry updates for minecraft * stub for tests * Update docfiles/pxtweb/cookieCompliance.ts Co-authored-by: Thomas Sparks <69657545+thsparks@users.noreply.github.com> --------- Co-authored-by: Thomas Sparks <69657545+thsparks@users.noreply.github.com>
1 parent 052f934 commit 206d0ec

File tree

21 files changed

+263
-72
lines changed

21 files changed

+263
-72
lines changed

.vscode/launch.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,28 @@
4343
"sourceMaps": false,
4444
"outFiles": []
4545
},
46+
{
47+
"name": "pxt serve (minecraft)",
48+
"type": "node",
49+
"request": "launch",
50+
"program": "${workspaceRoot}/built/pxt.js",
51+
"stopOnEntry": false,
52+
"args": [
53+
"serve",
54+
"--noauth"
55+
],
56+
"cwd": "${workspaceRoot}/../pxt-minecraft",
57+
"runtimeExecutable": null,
58+
"runtimeArgs": [
59+
"--nolazy"
60+
],
61+
"env": {
62+
"NODE_ENV": "development"
63+
},
64+
"console": "integratedTerminal",
65+
"sourceMaps": false,
66+
"outFiles": []
67+
},
4668
{
4769
"name": "pxt serve (microbit)",
4870
"type": "node",

docfiles/pxtweb/cookieCompliance.ts

Lines changed: 58 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -38,20 +38,50 @@ namespace pxt {
3838
let eventLogger: TelemetryQueue<string, Map<string>, Map<number>>;
3939
let exceptionLogger: TelemetryQueue<any, string, Map<string>>;
4040

41+
type EventListener<T = any> = (payload: T) => void;
42+
type EventSource<T> = {
43+
subscribe(listener: (payload: T) => void): () => void;
44+
emit(payload: T): void;
45+
};
46+
47+
function createEventSource<T = any>(): EventSource<T> {
48+
const listeners: EventListener<T>[] = [];
49+
50+
return {
51+
subscribe(listener: EventListener<T>): () => void {
52+
listeners.push(listener);
53+
// Return an unsubscribe function
54+
return () => {
55+
const index = listeners.indexOf(listener);
56+
if (index !== -1) {
57+
listeners.splice(index, 1);
58+
}
59+
};
60+
},
61+
emit(payload: T): void {
62+
listeners.forEach(listener => listener(payload));
63+
}
64+
};
65+
}
66+
4167
// performance measuring, added here because this is amongst the first (typescript) code ever executed
4268
export namespace perf {
4369
let enabled: boolean;
4470

71+
export const onMilestone = createEventSource<{ milestone: string, time: number, params?: Map<string> }>();
72+
export const onMeasurement = createEventSource<{ name: string, start: number, duration: number, params?: Map<string> }>();
73+
4574
export let startTimeMs: number;
4675
export let stats: {
47-
// name, start, duration
48-
durations: [string, number, number][],
76+
// name, start, duration, params
77+
durations: [string, number, number, Map<string>?][],
4978
// name, event
50-
milestones: [string, number][]
79+
milestones: [string, number, Map<string>?][]
5180
} = {
5281
durations: [],
5382
milestones: []
5483
}
84+
export function isEnabled() { return enabled; }
5585
export let perfReportLogged = false
5686
export function splitMs(): number {
5787
return Math.round(performance.now() - startTimeMs)
@@ -75,8 +105,10 @@ namespace pxt {
75105
return prettyStr(splitMs())
76106
}
77107

78-
export function recordMilestone(msg: string, time: number = splitMs()) {
79-
stats.milestones.push([msg, time])
108+
export function recordMilestone(msg: string, params?: Map<string>) {
109+
const time = splitMs()
110+
stats.milestones.push([msg, time, params])
111+
onMilestone.emit({ milestone: msg, time, params });
80112
}
81113
export function init() {
82114
enabled = performance && !!performance.mark && !!performance.measure;
@@ -89,7 +121,7 @@ namespace pxt {
89121
export function measureStart(name: string) {
90122
if (enabled) performance.mark(`${name} start`)
91123
}
92-
export function measureEnd(name: string) {
124+
export function measureEnd(name: string, params?: Map<string>) {
93125
if (enabled && performance.getEntriesByName(`${name} start`).length) {
94126
performance.mark(`${name} end`)
95127
performance.measure(`${name} elapsed`, `${name} start`, `${name} end`)
@@ -98,7 +130,8 @@ namespace pxt {
98130
let measure = e[0]
99131
let durMs = measure.duration
100132
if (durMs > 10) {
101-
stats.durations.push([name, measure.startTime, durMs])
133+
stats.durations.push([name, measure.startTime, durMs, params])
134+
onMeasurement.emit({ name, start: measure.startTime, duration: durMs, params });
102135
}
103136
}
104137
performance.clearMarks(`${name} start`)
@@ -108,34 +141,44 @@ namespace pxt {
108141
}
109142
export function report(filter: string = null) {
110143
perfReportLogged = true;
111-
112144
if (enabled) {
113-
const milestones: {[index: string]: number} = {};
114-
const durations: {[index: string]: number} = {};
145+
const milestones: { [index: string]: number } = {};
146+
const durations: { [index: string]: number } = {};
115147

116-
let report = `performance report:\n`
117-
for (let [msg, time] of stats.milestones) {
148+
let report = `Performance Report:\n`
149+
report += `\n`
150+
report += `\tMilestones:\n`
151+
for (let [msg, time, params] of stats.milestones) {
118152
if (!filter || msg.indexOf(filter) >= 0) {
119153
let pretty = prettyStr(time)
120-
report += `\t\t${msg} @ ${pretty}\n`
154+
report += `\t\t${msg} @ ${pretty}`
155+
for (let k of Object.keys(params || {})) {
156+
report += `\n\t\t\t${k}: ${params[k]}`
157+
}
158+
report += `\n`
121159
milestones[msg] = time;
122160
}
123161
}
124162

125163
report += `\n`
126-
for (let [msg, start, duration] of stats.durations) {
164+
report += `\tMeasurements:\n`
165+
for (let [msg, start, duration, params] of stats.durations) {
127166
let filterIncl = filter && msg.indexOf(filter) >= 0
128167
if ((duration > 50 && !filter) || filterIncl) {
129168
let pretty = prettyStr(duration)
130169
report += `\t\t${msg} took ~ ${pretty}`
131170
if (duration > 1000) {
132171
report += ` (${prettyStr(start)} - ${prettyStr(start + duration)})`
172+
for (let k of Object.keys(params || {})) {
173+
report += `\n\t\t\t${k}: ${params[k]}`
174+
}
133175
}
134176
report += `\n`
135177
}
136178
durations[msg] = duration;
137179
}
138180
console.log(report)
181+
enabled = false; // stop collecting milestones and measurements after report
139182
return { milestones, durations };
140183
}
141184
return undefined;
@@ -210,7 +253,7 @@ namespace pxt {
210253

211254
// App Insights automatically sends a page view event on setup, but we send our own later with additional properties.
212255
// This stops the automatic event from firing, so we don't end up with duplicate page view events.
213-
if(envelope.baseType == "PageviewData" && !envelope.baseData.properties) {
256+
if (envelope.baseType == "PageviewData" && !envelope.baseData.properties) {
214257
return false;
215258
}
216259

gulpfile.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -782,6 +782,8 @@ exports.pxtrunner = gulp.series(
782782
pxtembed,
783783
);
784784

785+
exports.pxtweb = pxtweb;
786+
exports.pxtlib = pxtlib;
785787
exports.skillmapTest = testSkillmap;
786788
exports.updatestrings = updatestrings;
787789
exports.lint = lint

localtypings/pxteditor.d.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1145,7 +1145,10 @@ declare namespace pxt.editor {
11451145
blocklyPatch?: (pkgTargetVersion: string, dom: Element) => void;
11461146
webUsbPairDialogAsync?: (pairAsync: () => Promise<boolean>, confirmAsync: (options: any) => Promise<number>) => Promise<number>;
11471147
mkPacketIOWrapper?: (io: pxt.packetio.PacketIO) => pxt.packetio.PacketIOWrapper;
1148-
1148+
onPostHostMessage?: (msg: pxt.editor.EditorMessageRequest) => void;
1149+
onPerfMilestone?: (payload: { milestone: string, time: number, params?: Map<string> }) => void;
1150+
onPerfMeasurement?: (payload: { name: string, start: number, duration: number, params?: Map<string> }) => void;
1151+
11491152
// Used with the @tutorialCompleted macro. See docs/writing-docs/tutorials.md for more info
11501153
onTutorialCompleted?: () => void;
11511154
onMarkdownActivityLoad?: (path: string, title?: string, editorProjectName?: string) => Promise<void>;

pxteditor/editorcontroller.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -335,8 +335,17 @@ export function bindEditorMessages(getEditorAsync: () => Promise<IProjectView>)
335335
/**
336336
* Sends analytics messages upstream to container if any
337337
*/
338+
let controllerAnalyticsEnabled = false;
338339
export function enableControllerAnalytics() {
339-
if (!pxt.appTarget.appTheme.allowParentController || !pxt.BrowserUtils.isIFrame()) return;
340+
if (controllerAnalyticsEnabled) return;
341+
342+
const hasOnPostHostMessage = !!pxt.commands.onPostHostMessage;
343+
const hasAllowParentController = pxt.appTarget.appTheme.allowParentController;
344+
const isInsideIFrame = pxt.BrowserUtils.isIFrame();
345+
346+
if (!(hasOnPostHostMessage || (hasAllowParentController && isInsideIFrame))) {
347+
return;
348+
}
340349

341350
const te = pxt.tickEvent;
342351
pxt.tickEvent = function (id: string, data?: pxt.Map<string | number>): void {
@@ -379,6 +388,8 @@ export function enableControllerAnalytics() {
379388
data
380389
})
381390
}
391+
392+
controllerAnalyticsEnabled = true;
382393
}
383394

384395
function sendResponse(request: pxt.editor.EditorMessage, resp: any, success: boolean, error: any) {
@@ -424,6 +435,16 @@ export function postHostMessageAsync(msg: pxt.editor.EditorMessageRequest): Prom
424435
window.parent.postMessage(env, "*");
425436
}
426437

438+
// Post to editor extension if it wants to be notified of these messages.
439+
// Note this is a one-way notification. Responses are not supported.
440+
if (pxt.commands.onPostHostMessage) {
441+
try {
442+
pxt.commands.onPostHostMessage(msg);
443+
} catch (err) {
444+
pxt.reportException(err);
445+
}
446+
}
447+
427448
if (!msg.response)
428449
resolve(undefined)
429450
})

pxtlib/analytics.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ namespace pxt.analytics {
5353

5454
if (!data) pxt.aiTrackEvent(id);
5555
else {
56-
const props: Map<string> = { ...defaultProps } || {};
57-
const measures: Map<number> = { ...defaultMeasures } || {};
56+
const props: Map<string> = { ...defaultProps };
57+
const measures: Map<number> = { ...defaultMeasures };
5858
Object.keys(data).forEach(k => {
5959
if (typeof data[k] == "string") props[k] = <string>data[k];
6060
else if (typeof data[k] == "number") measures[k] = <number>data[k];

pxtlib/browserutils.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1182,14 +1182,14 @@ namespace pxt.BrowserUtils {
11821182
}
11831183

11841184
setAsync(filename: string, snippets: Map<Map<number>>, code: string[], highlights: Map<Map<number>>, codeValidationMap: Map<Map<string[]>>, branch?: string): Promise<void> {
1185-
pxt.perf.measureStart("tutorial info db setAsync")
1185+
pxt.perf.measureStart(Measurements.TutorialInfoDbSetAsync)
11861186
const key = getTutorialInfoKey(filename, branch);
11871187
const hash = getTutorialCodeHash(code);
11881188
return this.setWithHashAsync(filename, snippets, hash, highlights, codeValidationMap);
11891189
}
11901190

11911191
setWithHashAsync(filename: string, snippets: Map<Map<number>>, hash: string, highlights: Map<Map<number>>, codeValidationMap: Map<Map<string[]>>, branch?: string): Promise<void> {
1192-
pxt.perf.measureStart("tutorial info db setAsync")
1192+
pxt.perf.measureStart(Measurements.TutorialInfoDbSetAsync)
11931193
const key = getTutorialInfoKey(filename, branch);
11941194
const blocks: Map<number> = {};
11951195
Object.keys(snippets).forEach(hash => {
@@ -1210,7 +1210,7 @@ namespace pxt.BrowserUtils {
12101210

12111211
return this.db.setAsync(TutorialInfoIndexedDb.TABLE, entry)
12121212
.then(() => {
1213-
pxt.perf.measureEnd("tutorial info db setAsync")
1213+
pxt.perf.measureEnd(Measurements.TutorialInfoDbSetAsync)
12141214
})
12151215
}
12161216

pxtlib/cmds.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ namespace pxt.commands {
3838
export let electronDeployAsync: (r: ts.pxtc.CompileResult) => Promise<void> = undefined; // A pointer to the Electron deploy function, so that targets can access it in their extension.ts
3939
export let webUsbPairDialogAsync: (pairAsync: () => Promise<boolean>, confirmAsync: (options: any) => Promise<WebUSBPairResult>, implicitlyCalled?: boolean) => Promise<WebUSBPairResult> = undefined;
4040
export let onTutorialCompleted: () => void = undefined;
41+
export let onPostHostMessage: (msg: any /*pxt.editor.EditorMessageRequest*/) => void;
42+
export let onPerfMilestone: (payload: { milestone: string, time: number, params?: Map<string> }) => void = undefined;
43+
export let onPerfMeasurement: (payload: { name: string, start: number, duration: number, params?: Map<string> }) => void = undefined;
4144
export let workspaceLoadedAsync: () => Promise<void> = undefined;
4245
export let onMarkdownActivityLoad: (path: string, title?: string, editorProjectName?: string) => Promise<void> = undefined;
4346
}

pxtlib/constants.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace Measurements {
2+
export const TutorialInfoDbSetAsync = "tutorial info db setAsync";
3+
export const ReloadAppTargetVariant = "reloadAppTargetVariant";
4+
export const Sha256Buffer = "sha256buffer";
5+
export const WebworkerRecvHandler = "webworker recvHandler";
6+
export const NetworkRequest = "network.request";
7+
}

pxtlib/main.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,26 @@
77
/// <reference path="tickEvent.ts"/>
88

99
namespace pxt.perf {
10+
export type EventSource<T> = {
11+
subscribe(listener: (payload: T) => void): () => void;
12+
//emit(payload: T): void; // not used externally
13+
};
14+
1015
// These functions are defined in docfiles/pxtweb/cookieCompliance.ts
16+
export declare function isEnabled(): boolean;
1117
export declare let perfReportLogged: boolean;
1218
export declare function report(): { milestones: {[index: string]: number}, durations: {[index: string]: number} } | undefined;
13-
export declare function recordMilestone(msg: string, time?: number): void;
19+
export declare function recordMilestone(msg: string, params?: Map<string>): void;
1420
export declare function measureStart(name: string): void;
15-
export declare function measureEnd(name: string): void;
21+
export declare function measureEnd(name: string, params?: Map<string>): void;
22+
export declare const onMilestone: EventSource<{ milestone: string, time: number, params?: Map<string> }>;
23+
export declare const onMeasurement: EventSource<{ name: string, start: number, duration: number, params?: Map<string> }>;
1624
}
1725
(function () {
1826
// Sometimes these aren't initialized, for example in tests. We only care about them
1927
// doing anything in the browser.
28+
if (!pxt.perf.isEnabled)
29+
pxt.perf.isEnabled = () => false
2030
if (!pxt.perf.report)
2131
pxt.perf.report = () => undefined
2232
if (!pxt.perf.recordMilestone)
@@ -258,7 +268,7 @@ namespace pxt {
258268
}
259269

260270
export function reloadAppTargetVariant(temporary = false) {
261-
pxt.perf.measureStart("reloadAppTargetVariant")
271+
pxt.perf.measureStart(Measurements.ReloadAppTargetVariant)
262272
const curr = temporary ? "" : JSON.stringify(appTarget);
263273
appTarget = U.cloneTargetBundle(savedAppTarget)
264274
if (appTargetVariant) {
@@ -272,7 +282,7 @@ namespace pxt {
272282
// check if apptarget changed
273283
if (!temporary && onAppTargetChanged && curr != JSON.stringify(appTarget))
274284
onAppTargetChanged();
275-
pxt.perf.measureEnd("reloadAppTargetVariant")
285+
pxt.perf.measureEnd(Measurements.ReloadAppTargetVariant)
276286
}
277287

278288
// this is set by compileServiceVariant in pxt.json

0 commit comments

Comments
 (0)