Refactor the tracing namespace organization
Also changes the `tracingEnabled.Mode.*` enum to a string union.
This commit is contained in:
parent
b7922147d3
commit
4fc9c8446d
|
@ -5,299 +5,291 @@ namespace ts { // eslint-disable-line one-namespace-per-file
|
|||
// should be used as tracing?.___
|
||||
export let tracing: typeof tracingEnabled | undefined;
|
||||
// enable the above using startTracing()
|
||||
}
|
||||
|
||||
// `tracingEnabled` should never be used directly, only through the above
|
||||
/* @internal */
|
||||
namespace ts.tracingEnabled { // eslint-disable-line one-namespace-per-file
|
||||
export const enum Mode {
|
||||
Project,
|
||||
Build,
|
||||
Server,
|
||||
}
|
||||
// `tracingEnabled` should never be used directly, only through the above
|
||||
namespace tracingEnabled { // eslint-disable-line one-namespace-per-file
|
||||
type Mode = "project" | "build" | "server";
|
||||
|
||||
let fs: typeof import("fs");
|
||||
let fs: typeof import("fs");
|
||||
|
||||
let traceCount = 0;
|
||||
let traceFd = 0;
|
||||
let traceCount = 0;
|
||||
let traceFd = 0;
|
||||
|
||||
let mode: Mode;
|
||||
let mode: Mode;
|
||||
|
||||
let legendPath: string | undefined;
|
||||
const legend: TraceRecord[] = [];
|
||||
let legendPath: string | undefined;
|
||||
const legend: TraceRecord[] = [];
|
||||
|
||||
// The actual constraint is that JSON.stringify be able to serialize it without throwing.
|
||||
interface Args {
|
||||
[key: string]: string | number | boolean | null | undefined | Args | readonly (string | number | boolean | null | undefined | Args)[];
|
||||
};
|
||||
|
||||
/** Starts tracing for the given project. */
|
||||
export function startTracing(tracingMode: Mode, traceDir: string, configFilePath?: string) {
|
||||
Debug.assert(!tracing, "Tracing already started");
|
||||
|
||||
if (fs === undefined) {
|
||||
try {
|
||||
fs = require("fs");
|
||||
}
|
||||
catch (e) {
|
||||
throw new Error(`tracing requires having fs\n(original error: ${e.message || e})`);
|
||||
}
|
||||
}
|
||||
|
||||
mode = tracingMode;
|
||||
|
||||
if (legendPath === undefined) {
|
||||
legendPath = combinePaths(traceDir, "legend.json");
|
||||
}
|
||||
|
||||
// Note that writing will fail later on if it exists and is not a directory
|
||||
if (!fs.existsSync(traceDir)) {
|
||||
fs.mkdirSync(traceDir, { recursive: true });
|
||||
}
|
||||
|
||||
const countPart =
|
||||
mode === Mode.Build ? `.${process.pid}-${++traceCount}`
|
||||
: mode === Mode.Server ? `.${process.pid}`
|
||||
: ``;
|
||||
const tracePath = combinePaths(traceDir, `trace${countPart}.json`);
|
||||
const typesPath = combinePaths(traceDir, `types${countPart}.json`);
|
||||
|
||||
legend.push({
|
||||
configFilePath,
|
||||
tracePath,
|
||||
typesPath,
|
||||
});
|
||||
|
||||
traceFd = fs.openSync(tracePath, "w");
|
||||
tracing = tracingEnabled; // only when traceFd is properly set
|
||||
|
||||
// Start with a prefix that contains some metadata that the devtools profiler expects (also avoids a warning on import)
|
||||
const meta = { cat: "__metadata", ph: "M", ts: 1000 * timestamp(), pid: 1, tid: 1 };
|
||||
fs.writeSync(traceFd,
|
||||
"[\n"
|
||||
+ [{ name: "process_name", args: { name: "tsc" }, ...meta },
|
||||
{ name: "thread_name", args: { name: "Main" }, ...meta },
|
||||
{ name: "TracingStartedInBrowser", ...meta, cat: "disabled-by-default-devtools.timeline" }]
|
||||
.map(v => JSON.stringify(v)).join(",\n"));
|
||||
}
|
||||
|
||||
/** Stops tracing for the in-progress project and dumps the type catalog. */
|
||||
export function stopTracing(typeCatalog?: readonly Type[]) {
|
||||
Debug.assert(tracing, "Tracing is not in progress");
|
||||
Debug.assert(!!typeCatalog === (mode !== Mode.Server)); // Have a type catalog iff not in server mode
|
||||
|
||||
fs.writeSync(traceFd, `\n]\n`);
|
||||
fs.closeSync(traceFd);
|
||||
tracing = undefined;
|
||||
|
||||
if (typeCatalog) {
|
||||
dumpTypes(typeCatalog);
|
||||
}
|
||||
else {
|
||||
// We pre-computed this path for convenience, but clear it
|
||||
// now that the file won't be created.
|
||||
legend[legend.length - 1].typesPath = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export const enum Phase {
|
||||
Parse = "parse",
|
||||
Program = "program",
|
||||
Bind = "bind",
|
||||
Check = "check", // Before we get into checking types (e.g. checkSourceFile)
|
||||
CheckTypes = "checkTypes",
|
||||
Emit = "emit",
|
||||
Session = "session",
|
||||
}
|
||||
|
||||
export function instant(phase: Phase, name: string, args?: Args) {
|
||||
writeEvent("I", phase, name, args, `"s":"g"`);
|
||||
}
|
||||
|
||||
const eventStack: { phase: Phase, name: string, args?: Args, time: number, separateBeginAndEnd: boolean }[] = [];
|
||||
|
||||
/**
|
||||
* @param separateBeginAndEnd - used for special cases where we need the trace point even if the event
|
||||
* never terminates (typically for reducing a scenario too big to trace to one that can be completed).
|
||||
* In the future we might implement an exit handler to dump unfinished events which would deprecate
|
||||
* these operations.
|
||||
*/
|
||||
export function push(phase: Phase, name: string, args?: Args, separateBeginAndEnd = false) {
|
||||
if (separateBeginAndEnd) {
|
||||
writeEvent("B", phase, name, args);
|
||||
}
|
||||
eventStack.push({ phase, name, args, time: 1000 * timestamp(), separateBeginAndEnd });
|
||||
}
|
||||
export function pop() {
|
||||
Debug.assert(eventStack.length > 0);
|
||||
writeStackEvent(eventStack.length - 1, 1000 * timestamp());
|
||||
eventStack.length--;
|
||||
}
|
||||
export function popAll() {
|
||||
const endTime = 1000 * timestamp();
|
||||
for (let i = eventStack.length - 1; i >= 0; i--) {
|
||||
writeStackEvent(i, endTime);
|
||||
}
|
||||
eventStack.length = 0;
|
||||
}
|
||||
// sample every 10ms
|
||||
const sampleInterval = 1000 * 10;
|
||||
function writeStackEvent(index: number, endTime: number) {
|
||||
const { phase, name, args, time, separateBeginAndEnd } = eventStack[index];
|
||||
if (separateBeginAndEnd) {
|
||||
writeEvent("E", phase, name, args, /*extras*/ undefined, endTime);
|
||||
}
|
||||
// test if [time,endTime) straddles a sampling point
|
||||
else if (sampleInterval - (time % sampleInterval) <= endTime - time) {
|
||||
writeEvent("X", phase, name, args, `"dur":${endTime - time}`, time);
|
||||
}
|
||||
}
|
||||
|
||||
function writeEvent(eventType: string, phase: Phase, name: string, args: Args | undefined, extras?: string,
|
||||
time: number = 1000 * timestamp()) {
|
||||
|
||||
// In server mode, there's no easy way to dump type information, so we drop events that would require it.
|
||||
if (mode === Mode.Server && phase === Phase.CheckTypes) return;
|
||||
|
||||
performance.mark("beginTracing");
|
||||
fs.writeSync(traceFd, `,\n{"pid":1,"tid":1,"ph":"${eventType}","cat":"${phase}","ts":${time},"name":"${name}"`);
|
||||
if (extras) fs.writeSync(traceFd, `,${extras}`);
|
||||
if (args) fs.writeSync(traceFd, `,"args":${JSON.stringify(args)}`);
|
||||
fs.writeSync(traceFd, `}`);
|
||||
performance.mark("endTracing");
|
||||
performance.measure("Tracing", "beginTracing", "endTracing");
|
||||
}
|
||||
|
||||
function indexFromOne(lc: LineAndCharacter): LineAndCharacter {
|
||||
return {
|
||||
line: lc.line + 1,
|
||||
character: lc.character + 1,
|
||||
// The actual constraint is that JSON.stringify be able to serialize it without throwing.
|
||||
interface Args {
|
||||
[key: string]: string | number | boolean | null | undefined | Args | readonly (string | number | boolean | null | undefined | Args)[];
|
||||
};
|
||||
}
|
||||
|
||||
function dumpTypes(types: readonly Type[]) {
|
||||
performance.mark("beginDumpTypes");
|
||||
/** Starts tracing for the given project. */
|
||||
export function startTracing(tracingMode: Mode, traceDir: string, configFilePath?: string) {
|
||||
Debug.assert(!tracing, "Tracing already started");
|
||||
|
||||
const typesPath = legend[legend.length - 1].typesPath!;
|
||||
const typesFd = fs.openSync(typesPath, "w");
|
||||
|
||||
const recursionIdentityMap = new Map<object, number>();
|
||||
|
||||
// Cleverness: no line break here so that the type ID will match the line number
|
||||
fs.writeSync(typesFd, "[");
|
||||
|
||||
const numTypes = types.length;
|
||||
for (let i = 0; i < numTypes; i++) {
|
||||
const type = types[i];
|
||||
const objectFlags = (type as any).objectFlags;
|
||||
const symbol = type.aliasSymbol ?? type.symbol;
|
||||
const firstDeclaration = symbol?.declarations?.[0];
|
||||
const firstFile = firstDeclaration && getSourceFileOfNode(firstDeclaration);
|
||||
|
||||
// It's slow to compute the display text, so skip it unless it's really valuable (or cheap)
|
||||
let display: string | undefined;
|
||||
if ((objectFlags & ObjectFlags.Anonymous) | (type.flags & TypeFlags.Literal)) {
|
||||
if (fs === undefined) {
|
||||
try {
|
||||
display = type.checker?.typeToString(type);
|
||||
fs = require("fs");
|
||||
}
|
||||
catch {
|
||||
display = undefined;
|
||||
catch (e) {
|
||||
throw new Error(`tracing requires having fs\n(original error: ${e.message || e})`);
|
||||
}
|
||||
}
|
||||
|
||||
let indexedAccessProperties: object = {};
|
||||
if (type.flags & TypeFlags.IndexedAccess) {
|
||||
const indexedAccessType = type as IndexedAccessType;
|
||||
indexedAccessProperties = {
|
||||
indexedAccessObjectType: indexedAccessType.objectType?.id,
|
||||
indexedAccessIndexType: indexedAccessType.indexType?.id,
|
||||
};
|
||||
mode = tracingMode;
|
||||
|
||||
if (legendPath === undefined) {
|
||||
legendPath = combinePaths(traceDir, "legend.json");
|
||||
}
|
||||
|
||||
let referenceProperties: object = {};
|
||||
if (objectFlags & ObjectFlags.Reference) {
|
||||
const referenceType = type as TypeReference;
|
||||
referenceProperties = {
|
||||
instantiatedType: referenceType.target?.id,
|
||||
typeArguments: referenceType.resolvedTypeArguments?.map(t => t.id),
|
||||
};
|
||||
// Note that writing will fail later on if it exists and is not a directory
|
||||
if (!fs.existsSync(traceDir)) {
|
||||
fs.mkdirSync(traceDir, { recursive: true });
|
||||
}
|
||||
|
||||
let conditionalProperties: object = {};
|
||||
if (type.flags & TypeFlags.Conditional) {
|
||||
const conditionalType = type as ConditionalType;
|
||||
conditionalProperties = {
|
||||
conditionalCheckType: conditionalType.checkType?.id,
|
||||
conditionalExtendsType: conditionalType.extendsType?.id,
|
||||
conditionalTrueType: conditionalType.resolvedTrueType?.id ?? -1,
|
||||
conditionalFalseType: conditionalType.resolvedFalseType?.id ?? -1,
|
||||
};
|
||||
}
|
||||
const countPart =
|
||||
mode === "build" ? `.${process.pid}-${++traceCount}`
|
||||
: mode === "server" ? `.${process.pid}`
|
||||
: ``;
|
||||
const tracePath = combinePaths(traceDir, `trace${countPart}.json`);
|
||||
const typesPath = combinePaths(traceDir, `types${countPart}.json`);
|
||||
|
||||
// We can't print out an arbitrary object, so just assign each one a unique number.
|
||||
// Don't call it an "id" so people don't treat it as a type id.
|
||||
let recursionToken: number | undefined;
|
||||
const recursionIdentity = type.checker.getRecursionIdentity(type);
|
||||
if (recursionIdentity) {
|
||||
recursionToken = recursionIdentityMap.get(recursionIdentity);
|
||||
if (!recursionToken) {
|
||||
recursionToken = recursionIdentityMap.size;
|
||||
recursionIdentityMap.set(recursionIdentity, recursionToken);
|
||||
}
|
||||
}
|
||||
legend.push({
|
||||
configFilePath,
|
||||
tracePath,
|
||||
typesPath,
|
||||
});
|
||||
|
||||
const descriptor = {
|
||||
id: type.id,
|
||||
intrinsicName: (type as any).intrinsicName,
|
||||
symbolName: symbol?.escapedName && unescapeLeadingUnderscores(symbol.escapedName),
|
||||
recursionId: recursionToken,
|
||||
unionTypes: (type.flags & TypeFlags.Union) ? (type as UnionType).types?.map(t => t.id) : undefined,
|
||||
intersectionTypes: (type.flags & TypeFlags.Intersection) ? (type as IntersectionType).types.map(t => t.id) : undefined,
|
||||
aliasTypeArguments: type.aliasTypeArguments?.map(t => t.id),
|
||||
keyofType: (type.flags & TypeFlags.Index) ? (type as IndexType).type?.id : undefined,
|
||||
...indexedAccessProperties,
|
||||
...referenceProperties,
|
||||
...conditionalProperties,
|
||||
firstDeclaration: firstDeclaration && {
|
||||
path: firstFile.path,
|
||||
start: indexFromOne(getLineAndCharacterOfPosition(firstFile, firstDeclaration.pos)),
|
||||
end: indexFromOne(getLineAndCharacterOfPosition(getSourceFileOfNode(firstDeclaration), firstDeclaration.end)),
|
||||
},
|
||||
flags: Debug.formatTypeFlags(type.flags).split("|"),
|
||||
display,
|
||||
traceFd = fs.openSync(tracePath, "w");
|
||||
tracing = tracingEnabled; // only when traceFd is properly set
|
||||
|
||||
// Start with a prefix that contains some metadata that the devtools profiler expects (also avoids a warning on import)
|
||||
const meta = { cat: "__metadata", ph: "M", ts: 1000 * timestamp(), pid: 1, tid: 1 };
|
||||
fs.writeSync(traceFd,
|
||||
"[\n"
|
||||
+ [{ name: "process_name", args: { name: "tsc" }, ...meta },
|
||||
{ name: "thread_name", args: { name: "Main" }, ...meta },
|
||||
{ name: "TracingStartedInBrowser", ...meta, cat: "disabled-by-default-devtools.timeline" }]
|
||||
.map(v => JSON.stringify(v)).join(",\n"));
|
||||
}
|
||||
|
||||
/** Stops tracing for the in-progress project and dumps the type catalog. */
|
||||
export function stopTracing(typeCatalog?: readonly Type[]) {
|
||||
Debug.assert(tracing, "Tracing is not in progress");
|
||||
Debug.assert(!!typeCatalog === (mode !== "server")); // Have a type catalog iff not in server mode
|
||||
|
||||
fs.writeSync(traceFd, `\n]\n`);
|
||||
fs.closeSync(traceFd);
|
||||
tracing = undefined;
|
||||
|
||||
if (typeCatalog) {
|
||||
dumpTypes(typeCatalog);
|
||||
}
|
||||
else {
|
||||
// We pre-computed this path for convenience, but clear it
|
||||
// now that the file won't be created.
|
||||
legend[legend.length - 1].typesPath = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export const enum Phase {
|
||||
Parse = "parse",
|
||||
Program = "program",
|
||||
Bind = "bind",
|
||||
Check = "check", // Before we get into checking types (e.g. checkSourceFile)
|
||||
CheckTypes = "checkTypes",
|
||||
Emit = "emit",
|
||||
Session = "session",
|
||||
}
|
||||
|
||||
export function instant(phase: Phase, name: string, args?: Args) {
|
||||
writeEvent("I", phase, name, args, `"s":"g"`);
|
||||
}
|
||||
|
||||
const eventStack: { phase: Phase, name: string, args?: Args, time: number, separateBeginAndEnd: boolean }[] = [];
|
||||
|
||||
/**
|
||||
* @param separateBeginAndEnd - used for special cases where we need the trace point even if the event
|
||||
* never terminates (typically for reducing a scenario too big to trace to one that can be completed).
|
||||
* In the future we might implement an exit handler to dump unfinished events which would deprecate
|
||||
* these operations.
|
||||
*/
|
||||
export function push(phase: Phase, name: string, args?: Args, separateBeginAndEnd = false) {
|
||||
if (separateBeginAndEnd) {
|
||||
writeEvent("B", phase, name, args);
|
||||
}
|
||||
eventStack.push({ phase, name, args, time: 1000 * timestamp(), separateBeginAndEnd });
|
||||
}
|
||||
export function pop() {
|
||||
Debug.assert(eventStack.length > 0);
|
||||
writeStackEvent(eventStack.length - 1, 1000 * timestamp());
|
||||
eventStack.length--;
|
||||
}
|
||||
export function popAll() {
|
||||
const endTime = 1000 * timestamp();
|
||||
for (let i = eventStack.length - 1; i >= 0; i--) {
|
||||
writeStackEvent(i, endTime);
|
||||
}
|
||||
eventStack.length = 0;
|
||||
}
|
||||
// sample every 10ms
|
||||
const sampleInterval = 1000 * 10;
|
||||
function writeStackEvent(index: number, endTime: number) {
|
||||
const { phase, name, args, time, separateBeginAndEnd } = eventStack[index];
|
||||
if (separateBeginAndEnd) {
|
||||
writeEvent("E", phase, name, args, /*extras*/ undefined, endTime);
|
||||
}
|
||||
// test if [time,endTime) straddles a sampling point
|
||||
else if (sampleInterval - (time % sampleInterval) <= endTime - time) {
|
||||
writeEvent("X", phase, name, args, `"dur":${endTime - time}`, time);
|
||||
}
|
||||
}
|
||||
|
||||
function writeEvent(eventType: string, phase: Phase, name: string, args: Args | undefined, extras?: string,
|
||||
time: number = 1000 * timestamp()) {
|
||||
|
||||
// In server mode, there's no easy way to dump type information, so we drop events that would require it.
|
||||
if (mode === "server" && phase === Phase.CheckTypes) return;
|
||||
|
||||
performance.mark("beginTracing");
|
||||
fs.writeSync(traceFd, `,\n{"pid":1,"tid":1,"ph":"${eventType}","cat":"${phase}","ts":${time},"name":"${name}"`);
|
||||
if (extras) fs.writeSync(traceFd, `,${extras}`);
|
||||
if (args) fs.writeSync(traceFd, `,"args":${JSON.stringify(args)}`);
|
||||
fs.writeSync(traceFd, `}`);
|
||||
performance.mark("endTracing");
|
||||
performance.measure("Tracing", "beginTracing", "endTracing");
|
||||
}
|
||||
|
||||
function indexFromOne(lc: LineAndCharacter): LineAndCharacter {
|
||||
return {
|
||||
line: lc.line + 1,
|
||||
character: lc.character + 1,
|
||||
};
|
||||
}
|
||||
|
||||
fs.writeSync(typesFd, JSON.stringify(descriptor));
|
||||
if (i < numTypes - 1) {
|
||||
fs.writeSync(typesFd, ",\n");
|
||||
function dumpTypes(types: readonly Type[]) {
|
||||
performance.mark("beginDumpTypes");
|
||||
|
||||
const typesPath = legend[legend.length - 1].typesPath!;
|
||||
const typesFd = fs.openSync(typesPath, "w");
|
||||
|
||||
const recursionIdentityMap = new Map<object, number>();
|
||||
|
||||
// Cleverness: no line break here so that the type ID will match the line number
|
||||
fs.writeSync(typesFd, "[");
|
||||
|
||||
const numTypes = types.length;
|
||||
for (let i = 0; i < numTypes; i++) {
|
||||
const type = types[i];
|
||||
const objectFlags = (type as any).objectFlags;
|
||||
const symbol = type.aliasSymbol ?? type.symbol;
|
||||
const firstDeclaration = symbol?.declarations?.[0];
|
||||
const firstFile = firstDeclaration && getSourceFileOfNode(firstDeclaration);
|
||||
|
||||
// It's slow to compute the display text, so skip it unless it's really valuable (or cheap)
|
||||
let display: string | undefined;
|
||||
if ((objectFlags & ObjectFlags.Anonymous) | (type.flags & TypeFlags.Literal)) {
|
||||
try {
|
||||
display = type.checker?.typeToString(type);
|
||||
}
|
||||
catch {
|
||||
display = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
let indexedAccessProperties: object = {};
|
||||
if (type.flags & TypeFlags.IndexedAccess) {
|
||||
const indexedAccessType = type as IndexedAccessType;
|
||||
indexedAccessProperties = {
|
||||
indexedAccessObjectType: indexedAccessType.objectType?.id,
|
||||
indexedAccessIndexType: indexedAccessType.indexType?.id,
|
||||
};
|
||||
}
|
||||
|
||||
let referenceProperties: object = {};
|
||||
if (objectFlags & ObjectFlags.Reference) {
|
||||
const referenceType = type as TypeReference;
|
||||
referenceProperties = {
|
||||
instantiatedType: referenceType.target?.id,
|
||||
typeArguments: referenceType.resolvedTypeArguments?.map(t => t.id),
|
||||
};
|
||||
}
|
||||
|
||||
let conditionalProperties: object = {};
|
||||
if (type.flags & TypeFlags.Conditional) {
|
||||
const conditionalType = type as ConditionalType;
|
||||
conditionalProperties = {
|
||||
conditionalCheckType: conditionalType.checkType?.id,
|
||||
conditionalExtendsType: conditionalType.extendsType?.id,
|
||||
conditionalTrueType: conditionalType.resolvedTrueType?.id ?? -1,
|
||||
conditionalFalseType: conditionalType.resolvedFalseType?.id ?? -1,
|
||||
};
|
||||
}
|
||||
|
||||
// We can't print out an arbitrary object, so just assign each one a unique number.
|
||||
// Don't call it an "id" so people don't treat it as a type id.
|
||||
let recursionToken: number | undefined;
|
||||
const recursionIdentity = type.checker.getRecursionIdentity(type);
|
||||
if (recursionIdentity) {
|
||||
recursionToken = recursionIdentityMap.get(recursionIdentity);
|
||||
if (!recursionToken) {
|
||||
recursionToken = recursionIdentityMap.size;
|
||||
recursionIdentityMap.set(recursionIdentity, recursionToken);
|
||||
}
|
||||
}
|
||||
|
||||
const descriptor = {
|
||||
id: type.id,
|
||||
intrinsicName: (type as any).intrinsicName,
|
||||
symbolName: symbol?.escapedName && unescapeLeadingUnderscores(symbol.escapedName),
|
||||
recursionId: recursionToken,
|
||||
unionTypes: (type.flags & TypeFlags.Union) ? (type as UnionType).types?.map(t => t.id) : undefined,
|
||||
intersectionTypes: (type.flags & TypeFlags.Intersection) ? (type as IntersectionType).types.map(t => t.id) : undefined,
|
||||
aliasTypeArguments: type.aliasTypeArguments?.map(t => t.id),
|
||||
keyofType: (type.flags & TypeFlags.Index) ? (type as IndexType).type?.id : undefined,
|
||||
...indexedAccessProperties,
|
||||
...referenceProperties,
|
||||
...conditionalProperties,
|
||||
firstDeclaration: firstDeclaration && {
|
||||
path: firstFile.path,
|
||||
start: indexFromOne(getLineAndCharacterOfPosition(firstFile, firstDeclaration.pos)),
|
||||
end: indexFromOne(getLineAndCharacterOfPosition(getSourceFileOfNode(firstDeclaration), firstDeclaration.end)),
|
||||
},
|
||||
flags: Debug.formatTypeFlags(type.flags).split("|"),
|
||||
display,
|
||||
};
|
||||
|
||||
fs.writeSync(typesFd, JSON.stringify(descriptor));
|
||||
if (i < numTypes - 1) {
|
||||
fs.writeSync(typesFd, ",\n");
|
||||
}
|
||||
}
|
||||
|
||||
fs.writeSync(typesFd, "]\n");
|
||||
|
||||
fs.closeSync(typesFd);
|
||||
|
||||
performance.mark("endDumpTypes");
|
||||
performance.measure("Dump types", "beginDumpTypes", "endDumpTypes");
|
||||
}
|
||||
|
||||
fs.writeSync(typesFd, "]\n");
|
||||
export function dumpLegend() {
|
||||
if (!legendPath) {
|
||||
return;
|
||||
}
|
||||
|
||||
fs.closeSync(typesFd);
|
||||
|
||||
performance.mark("endDumpTypes");
|
||||
performance.measure("Dump types", "beginDumpTypes", "endDumpTypes");
|
||||
}
|
||||
|
||||
export function dumpLegend() {
|
||||
if (!legendPath) {
|
||||
return;
|
||||
fs.writeFileSync(legendPath, JSON.stringify(legend));
|
||||
}
|
||||
|
||||
fs.writeFileSync(legendPath, JSON.stringify(legend));
|
||||
interface TraceRecord {
|
||||
configFilePath?: string;
|
||||
tracePath: string;
|
||||
typesPath?: string;
|
||||
}
|
||||
}
|
||||
|
||||
interface TraceRecord {
|
||||
configFilePath?: string;
|
||||
tracePath: string;
|
||||
typesPath?: string;
|
||||
}
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
namespace ts { // eslint-disable-line one-namespace-per-file
|
||||
// define after tracingEnabled is initialized
|
||||
export const startTracing = tracingEnabled.startTracing;
|
||||
}
|
||||
|
|
|
@ -666,7 +666,7 @@ namespace ts {
|
|||
}
|
||||
|
||||
if (canTrace(system, compilerOptions)) {
|
||||
startTracing(isBuildMode ? tracingEnabled.Mode.Build : tracingEnabled.Mode.Project,
|
||||
startTracing(isBuildMode ? "build" : "project",
|
||||
compilerOptions.generateTrace!, compilerOptions.configFilePath);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -861,7 +861,7 @@ namespace ts.server {
|
|||
? stripQuotes(commandLineTraceDir)
|
||||
: process.env.TSS_TRACE;
|
||||
if (traceDir) {
|
||||
startTracing(tracingEnabled.Mode.Server, traceDir);
|
||||
startTracing("server", traceDir);
|
||||
}
|
||||
|
||||
const ioSession = new IOSession();
|
||||
|
|
Loading…
Reference in a new issue