@@ -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
0 commit comments