11/*@internal */
22/** Tracing events for the compiler. */
33namespace ts . tracing {
4- type WriteFn = ( data : string ) => void ;
4+ let fs : typeof import ( "fs" ) | false | undefined ;
55
6- let write : WriteFn | undefined ;
6+ let traceCount = 0 ;
7+ let traceFd : number | undefined ;
78
8- /** Enables (and resets) tracing events for the compiler. */
9- export function startTracing ( w : WriteFn ) {
10- write = w ;
11- write ( `[\n` ) ;
9+ let legendPath : string | undefined ;
10+ const legend : TraceRecord [ ] = [ ] ;
11+
12+ /** Starts tracing for the given project (unless the `fs` module is unavailable). */
13+ export function startTracing ( configFilePath : string | undefined , traceDir : string , isBuildMode : boolean ) {
14+ Debug . assert ( ! traceFd , "Tracing already started" ) ;
15+
16+ if ( fs === undefined ) {
17+ try {
18+ fs = require ( "fs" ) ;
19+ }
20+ catch {
21+ fs = false ;
22+ }
23+ }
24+
25+ if ( ! fs ) {
26+ return ;
27+ }
28+
29+ if ( legendPath === undefined ) {
30+ legendPath = combinePaths ( traceDir , "legend.json" ) ;
31+ }
32+
33+ // Note that writing will fail later on if it exists and is not a directory
34+ if ( ! fs . existsSync ( traceDir ) ) {
35+ fs . mkdirSync ( traceDir , { recursive : true } ) ;
36+ }
37+
38+ const countPart = isBuildMode ? `.${ ++ traceCount } ` : `` ;
39+ const tracePath = combinePaths ( traceDir , `trace${ countPart } .json` ) ;
40+ const typesPath = combinePaths ( traceDir , `types${ countPart } .json` ) ;
41+
42+ legend . push ( {
43+ configFilePath,
44+ tracePath,
45+ typesPath,
46+ } ) ;
47+
48+ traceFd = fs . openSync ( tracePath , "w" ) ;
49+ fs . writeSync ( traceFd , `[\n` ) ;
1250 }
1351
14- /** Disables tracing events for the compiler. */
15- export function stopTracing ( ) {
52+ /** Stops tracing for the in-progress project and dumps the type catalog (unless the `fs` module is unavailable). */
53+ export function stopTracing ( typeCatalog : readonly Type [ ] ) {
54+ if ( ! traceFd ) {
55+ Debug . assert ( ! fs , "Tracing is not in progress" ) ;
56+ return ;
57+ }
58+
59+ Debug . assert ( fs ) ;
60+
1661 // This both indicates that the trace is untruncated and conveniently
1762 // ensures that the last array element won't have a trailing comma.
18- write ?.( `{"pid":1,"tid":1,"ph":"i","ts":${ 1000 * timestamp ( ) } ,"name":"done","s":"g"}\n` ) ;
19- write ?.( `]\n` ) ;
20- write = undefined ;
63+ fs . writeSync ( traceFd , `{"pid":1,"tid":1,"ph":"i","ts":${ 1000 * timestamp ( ) } ,"name":"done","s":"g"}\n` ) ;
64+ fs . writeSync ( traceFd , `]\n` ) ;
65+
66+ fs . closeSync ( traceFd ) ;
67+ traceFd = undefined ;
68+
69+ if ( typeCatalog ) {
70+ dumpTypes ( typeCatalog ) ;
71+ }
72+ else {
73+ // We pre-computed this path for convenience, but clear it
74+ // now that the file won't be created.
75+ legend [ legend . length - 1 ] . typesPath = undefined ;
76+ }
2177 }
2278
2379 export function isTracing ( ) {
24- return ! ! write ;
80+ return ! ! traceFd ;
2581 }
2682
2783 export const enum Phase {
@@ -33,15 +89,25 @@ namespace ts.tracing {
3389 }
3490
3591 export function begin ( phase : Phase , name : string , args : object ) {
92+ if ( ! traceFd ) {
93+ return ;
94+ }
95+ Debug . assert ( fs ) ;
96+
3697 performance . mark ( "beginTracing" ) ;
37- write ?. ( `{"pid":1,"tid":1,"ph":"B","cat":"${ phase } ","ts":${ 1000 * timestamp ( ) } ,"name":"${ name } ","args":{ "ts": ${ JSON . stringify ( args ) } }},\n` ) ;
98+ fs . writeSync ( traceFd , `{"pid":1,"tid":1,"ph":"B","cat":"${ phase } ","ts":${ 1000 * timestamp ( ) } ,"name":"${ name } ","args":{ "ts": ${ JSON . stringify ( args ) } }},\n` ) ;
3899 performance . mark ( "endTracing" ) ;
39100 performance . measure ( "Tracing" , "beginTracing" , "endTracing" ) ;
40101 }
41102
42103 export function end ( ) {
104+ if ( ! traceFd ) {
105+ return ;
106+ }
107+ Debug . assert ( fs ) ;
108+
43109 performance . mark ( "beginTracing" ) ;
44- write ?. ( `{"pid":1,"tid":1,"ph":"E","ts":${ 1000 * timestamp ( ) } },\n` ) ;
110+ fs . writeSync ( traceFd , `{"pid":1,"tid":1,"ph":"E","ts":${ 1000 * timestamp ( ) } },\n` ) ;
45111 performance . mark ( "endTracing" ) ;
46112 performance . measure ( "Tracing" , "beginTracing" , "endTracing" ) ;
47113 }
@@ -53,13 +119,18 @@ namespace ts.tracing {
53119 } ;
54120 }
55121
56- export function dumpTypes ( types : readonly Type [ ] , write : WriteFn ) {
122+ function dumpTypes ( types : readonly Type [ ] ) {
123+ Debug . assert ( fs ) ;
124+
57125 performance . mark ( "beginDumpTypes" ) ;
58126
59- const numTypes = types . length ;
127+ const typesPath = legend [ legend . length - 1 ] . typesPath ! ;
128+ const typesFd = fs . openSync ( typesPath , "w" ) ;
129+
130+ // Cleverness: no line break here so that the type ID will match the line number
131+ fs . writeSync ( typesFd , "[" ) ;
60132
61- // Cleverness: no line break hear so that the type ID will match the line number
62- write ( "[" ) ;
133+ const numTypes = types . length ;
63134 for ( let i = 0 ; i < numTypes ; i ++ ) {
64135 const type = types [ i ] ;
65136 const objectFlags = ( type as any ) . objectFlags ;
@@ -127,14 +198,32 @@ namespace ts.tracing {
127198 display,
128199 } ;
129200
130- write ( JSON . stringify ( descriptor ) ) ;
201+ fs . writeSync ( typesFd , JSON . stringify ( descriptor ) ) ;
131202 if ( i < numTypes - 1 ) {
132- write ( ",\n" ) ;
203+ fs . writeSync ( typesFd , ",\n" ) ;
133204 }
134205 }
135- write ( "]\n" ) ;
206+
207+ fs . writeSync ( typesFd , "]\n" ) ;
208+
209+ fs . closeSync ( typesFd ) ;
136210
137211 performance . mark ( "endDumpTypes" ) ;
138212 performance . measure ( "Dump types" , "beginDumpTypes" , "endDumpTypes" ) ;
139213 }
214+
215+ export function dumpLegend ( ) {
216+ if ( ! legendPath ) {
217+ return ;
218+ }
219+ Debug . assert ( fs ) ;
220+
221+ fs . writeFileSync ( legendPath , JSON . stringify ( legend ) ) ;
222+ }
223+
224+ interface TraceRecord {
225+ configFilePath ?: string ;
226+ tracePath : string ;
227+ typesPath ?: string ;
228+ }
140229}
0 commit comments