diff --git a/tsc/internal/execute/build/orchestrator.go b/tsc/internal/execute/build/orchestrator.go index 664e47844616e..6a1db66d7fddd 100644 --- a/tsc/internal/execute/build/orchestrator.go +++ b/tsc/internal/execute/build/orchestrator.go @@ -60,7 +60,7 @@ func (b *OrchestratorResult) reportWithFilesToDelete(o *Orchestrator, reportFile return } b.Statistics.SetTotalTime(o.opts.Sys.SinceStart()) - b.Statistics.Report(o.opts.Sys.Writer(), o.opts.Testing) + b.Statistics.Report(o.opts.Sys.Writer(), o.opts.Testing, o.opts.Command.CompilerOptions.ExtendedDiagnostics.IsTrue()) } type Orchestrator struct { diff --git a/tsc/internal/execute/tsc/emit.go b/tsc/internal/execute/tsc/emit.go index 4f30b1617ada1..44fa09179f810 100644 --- a/tsc/internal/execute/tsc/emit.go +++ b/tsc/internal/execute/tsc/emit.go @@ -60,7 +60,7 @@ func EmitAndReportStatistics(input EmitInput) (CompileAndEmitResult, *Statistics runtime.ReadMemStats(&memStats) statistics = statisticsFromProgram(input, &memStats) - statistics.Report(input.Writer, input.Testing) + statistics.Report(input.Writer, input.Testing, input.Config.CompilerOptions().ExtendedDiagnostics.IsTrue()) } if result.EmitResult.EmitSkipped && len(result.Diagnostics) > 0 { diff --git a/tsc/internal/execute/tsc/statistics.go b/tsc/internal/execute/tsc/statistics.go index ce2ac6d5d4bdc..f2b843ba88235 100644 --- a/tsc/internal/execute/tsc/statistics.go +++ b/tsc/internal/execute/tsc/statistics.go @@ -10,6 +10,7 @@ import ( "time" "github.com/microsoft/TypeScript/tsc/internal/compiler" + "github.com/microsoft/TypeScript/tsc/internal/osutil" ) type tableRow struct { @@ -83,7 +84,7 @@ func statisticsFromProgram(input EmitInput, memStats *runtime.MemStats) *Statist } } -func (s *Statistics) Report(w io.Writer, testing CommandLineTesting) { +func (s *Statistics) Report(w io.Writer, testing CommandLineTesting, extendedDiagnostics bool) { if testing != nil { testing.OnStatisticsStart(w) defer testing.OnStatisticsEnd(w) @@ -126,6 +127,11 @@ func (s *Statistics) Report(w io.Writer, testing CommandLineTesting) { } s.addContentMapperStatistics(&table, prefix) table.add(prefix+"Total time", s.compileTimes.totalTime) + if extendedDiagnostics { + if cpuTime, err := osutil.ProcessCPUTime(); err == nil { + table.add("CPU time", cpuTime) + } + } table.print(w) } diff --git a/tsc/internal/osutil/cputime_other.go b/tsc/internal/osutil/cputime_other.go new file mode 100644 index 0000000000000..3b09c61fc3d67 --- /dev/null +++ b/tsc/internal/osutil/cputime_other.go @@ -0,0 +1,12 @@ +//go:build !unix && !windows + +package osutil + +import ( + "errors" + "time" +) + +func processCPUTime() (time.Duration, error) { + return 0, errors.ErrUnsupported +} diff --git a/tsc/internal/osutil/cputime_unix.go b/tsc/internal/osutil/cputime_unix.go new file mode 100644 index 0000000000000..ec9d3eb57bb16 --- /dev/null +++ b/tsc/internal/osutil/cputime_unix.go @@ -0,0 +1,17 @@ +//go:build unix + +package osutil + +import ( + "time" + + "golang.org/x/sys/unix" +) + +func processCPUTime() (time.Duration, error) { + var usage unix.Rusage + if err := unix.Getrusage(unix.RUSAGE_SELF, &usage); err != nil { + return 0, err + } + return time.Duration(usage.Utime.Nano()) + time.Duration(usage.Stime.Nano()), nil +} diff --git a/tsc/internal/osutil/cputime_windows.go b/tsc/internal/osutil/cputime_windows.go new file mode 100644 index 0000000000000..b25d84f897b8d --- /dev/null +++ b/tsc/internal/osutil/cputime_windows.go @@ -0,0 +1,17 @@ +package osutil + +import ( + "time" + + "golang.org/x/sys/windows" +) + +func processCPUTime() (time.Duration, error) { + var creation, exit, kernel, user windows.Filetime + if err := windows.GetProcessTimes(windows.CurrentProcess(), &creation, &exit, &kernel, &user); err != nil { + return 0, err + } + kernelTicks := uint64(kernel.HighDateTime)<<32 | uint64(kernel.LowDateTime) + userTicks := uint64(user.HighDateTime)<<32 | uint64(user.LowDateTime) + return time.Duration(kernelTicks+userTicks) * 100 * time.Nanosecond, nil +} diff --git a/tsc/internal/osutil/osutil.go b/tsc/internal/osutil/osutil.go index ed6bc22edd697..16d5d49972be9 100644 --- a/tsc/internal/osutil/osutil.go +++ b/tsc/internal/osutil/osutil.go @@ -1,5 +1,7 @@ package osutil +import "time" + // Args returns the command-line arguments with platform-specific launcher details removed. func Args() []string { return args() @@ -9,3 +11,9 @@ func Args() []string { func Executable() (string, error) { return executable() } + +// ProcessCPUTime returns cumulative user and system CPU time across all threads +// of the current process, excluding child processes. +func ProcessCPUTime() (time.Duration, error) { + return processCPUTime() +}