diff options
| author | 2023-04-28 01:36:44 +0800 | |
|---|---|---|
| committer | 2023-04-28 01:36:44 +0800 | |
| commit | dd84b9d64fb98746a230cd24233ff50a562c39c9 (patch) | |
| tree | b583261ef00b3afe72ec4d6dacb31e57779a6faf /cli/internal/cmd | |
| parent | 0b46fcd72ac34382387b2bcf9095233efbcc52f4 (diff) | |
| download | HydroRoll-dd84b9d64fb98746a230cd24233ff50a562c39c9.tar.gz HydroRoll-dd84b9d64fb98746a230cd24233ff50a562c39c9.zip | |
Diffstat (limited to 'cli/internal/cmd')
| -rw-r--r-- | cli/internal/cmd/root.go | 157 |
1 files changed, 157 insertions, 0 deletions
diff --git a/cli/internal/cmd/root.go b/cli/internal/cmd/root.go new file mode 100644 index 0000000..d8d0e33 --- /dev/null +++ b/cli/internal/cmd/root.go @@ -0,0 +1,157 @@ +// Package cmd holds the root cobra command for turbo +package cmd + +import ( + "context" + "fmt" + "os" + "runtime/pprof" + "runtime/trace" + + "github.com/pkg/errors" + "github.com/vercel/turbo/cli/internal/cmdutil" + "github.com/vercel/turbo/cli/internal/daemon" + "github.com/vercel/turbo/cli/internal/process" + "github.com/vercel/turbo/cli/internal/prune" + "github.com/vercel/turbo/cli/internal/run" + "github.com/vercel/turbo/cli/internal/signals" + "github.com/vercel/turbo/cli/internal/turbostate" + "github.com/vercel/turbo/cli/internal/util" +) + +func initializeOutputFiles(helper *cmdutil.Helper, parsedArgs *turbostate.ParsedArgsFromRust) error { + if parsedArgs.Trace != "" { + cleanup, err := createTraceFile(parsedArgs.Trace) + if err != nil { + return fmt.Errorf("failed to create trace file: %v", err) + } + helper.RegisterCleanup(cleanup) + } + if parsedArgs.Heap != "" { + cleanup, err := createHeapFile(parsedArgs.Heap) + if err != nil { + return fmt.Errorf("failed to create heap file: %v", err) + } + helper.RegisterCleanup(cleanup) + } + if parsedArgs.CPUProfile != "" { + cleanup, err := createCpuprofileFile(parsedArgs.CPUProfile) + if err != nil { + return fmt.Errorf("failed to create CPU profile file: %v", err) + } + helper.RegisterCleanup(cleanup) + } + + return nil +} + +// RunWithArgs runs turbo with the ParsedArgsFromRust that is passed from the Rust side. +func RunWithArgs(args *turbostate.ParsedArgsFromRust, turboVersion string) int { + util.InitPrintf() + // TODO: replace this with a context + signalWatcher := signals.NewWatcher() + helper := cmdutil.NewHelper(turboVersion, args) + ctx := context.Background() + + err := initializeOutputFiles(helper, args) + if err != nil { + fmt.Printf("%v", err) + return 1 + } + defer helper.Cleanup(args) + + doneCh := make(chan struct{}) + var execErr error + go func() { + command := args.Command + if command.Daemon != nil { + execErr = daemon.ExecuteDaemon(ctx, helper, signalWatcher, args) + } else if command.Prune != nil { + execErr = prune.ExecutePrune(helper, args) + } else if command.Run != nil { + execErr = run.ExecuteRun(ctx, helper, signalWatcher, args) + } else { + execErr = fmt.Errorf("unknown command: %v", command) + } + + close(doneCh) + }() + + // Wait for either our command to finish, in which case we need to clean up, + // or to receive a signal, in which case the signal handler above does the cleanup + select { + case <-doneCh: + // We finished whatever task we were running + signalWatcher.Close() + exitErr := &process.ChildExit{} + if errors.As(execErr, &exitErr) { + return exitErr.ExitCode + } else if execErr != nil { + fmt.Printf("Turbo error: %v\n", execErr) + return 1 + } + return 0 + case <-signalWatcher.Done(): + // We caught a signal, which already called the close handlers + return 1 + } +} + +type profileCleanup func() error + +// Close implements io.Close for profileCleanup +func (pc profileCleanup) Close() error { + return pc() +} + +// To view a CPU trace, use "go tool trace [file]". Note that the trace +// viewer doesn't work under Windows Subsystem for Linux for some reason. +func createTraceFile(traceFile string) (profileCleanup, error) { + f, err := os.Create(traceFile) + if err != nil { + return nil, errors.Wrapf(err, "failed to create trace file: %v", traceFile) + } + if err := trace.Start(f); err != nil { + return nil, errors.Wrap(err, "failed to start tracing") + } + return func() error { + trace.Stop() + return f.Close() + }, nil +} + +// To view a heap trace, use "go tool pprof [file]" and type "top". You can +// also drop it into https://speedscope.app and use the "left heavy" or +// "sandwich" view modes. +func createHeapFile(heapFile string) (profileCleanup, error) { + f, err := os.Create(heapFile) + if err != nil { + return nil, errors.Wrapf(err, "failed to create heap file: %v", heapFile) + } + return func() error { + if err := pprof.WriteHeapProfile(f); err != nil { + // we don't care if we fail to close the file we just failed to write to + _ = f.Close() + return errors.Wrapf(err, "failed to write heap file: %v", heapFile) + } + return f.Close() + }, nil +} + +// To view a CPU profile, drop the file into https://speedscope.app. +// Note: Running the CPU profiler doesn't work under Windows subsystem for +// Linux. The profiler has to be built for native Windows and run using the +// command prompt instead. +func createCpuprofileFile(cpuprofileFile string) (profileCleanup, error) { + f, err := os.Create(cpuprofileFile) + if err != nil { + return nil, errors.Wrapf(err, "failed to create cpuprofile file: %v", cpuprofileFile) + } + if err := pprof.StartCPUProfile(f); err != nil { + return nil, errors.Wrap(err, "failed to start CPU profiling") + } + return func() error { + pprof.StopCPUProfile() + return f.Close() + }, nil +} |
