blob: 91e2f242439a32393c88fcbc46640184bcc29a23 [file]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Siso is a Ninja-compatible build system optimized for remote execution.
package main
import (
"context"
"flag"
"fmt"
"net/http"
"os"
"os/signal"
"runtime"
"runtime/pprof"
"runtime/trace"
"syscall"
log "github.com/golang/glog"
"github.com/google/subcommands"
"go.chromium.org/build/siso/auth/cred"
"go.chromium.org/build/siso/hashfs/osfs"
"go.chromium.org/build/siso/subcmd/auth"
"go.chromium.org/build/siso/subcmd/collector"
"go.chromium.org/build/siso/subcmd/fetch"
"go.chromium.org/build/siso/subcmd/fscmd"
"go.chromium.org/build/siso/subcmd/isolate"
"go.chromium.org/build/siso/subcmd/metricscmd"
"go.chromium.org/build/siso/subcmd/ninja"
"go.chromium.org/build/siso/subcmd/ninjafrontend"
"go.chromium.org/build/siso/subcmd/proxy"
"go.chromium.org/build/siso/subcmd/ps"
"go.chromium.org/build/siso/subcmd/query"
"go.chromium.org/build/siso/subcmd/recall"
"go.chromium.org/build/siso/subcmd/report"
"go.chromium.org/build/siso/subcmd/sandbox"
"go.chromium.org/build/siso/subcmd/scandeps"
"go.chromium.org/build/siso/subcmd/version"
"go.chromium.org/build/siso/subcmd/webui"
"go.chromium.org/build/siso/ui"
_ "net/http/pprof" // import to let pprof register its HTTP handlers
)
var (
pprofAddr string
cpuprofile string
memprofile string
mutexprofile string
blockprofile string
blockprofRate int
mutexprofFrac int
traceFile string
)
const versionID = "v1.4.31"
const versionStr = "siso " + versionID
func main() {
// Wraps sisoMain() because os.Exit() doesn't wait defers.
os.Exit(sisoMain())
}
func sisoMain() int {
flag.CommandLine.Usage = func() {
w := flag.CommandLine.Output()
fmt.Fprint(w, `
Usage: siso [flags] [command] [arguments]
e.g.
$ siso ninja -C out/Default
`)
fmt.Fprintf(w, "important flags of %s:\n", os.Args[0])
f := flag.Lookup("credential_helper")
fmt.Fprintf(w, ` -credential_helper path
%s
(default %q)
`, f.Usage, f.DefValue)
f = flag.Lookup("version")
fmt.Fprintf(w, ` -version
%s
`, f.Usage)
fmt.Fprintf(flag.CommandLine.Output(), `
Use "siso help" to display commands.
Use "siso help [command]" for more information about a command.
Use "siso flags" to display all flags.
`)
}
flag.StringVar(&pprofAddr, "pprof_addr", "", `listen address for "go tool pprof". e.g. "localhost:6060"`)
flag.StringVar(&cpuprofile, "cpuprofile", "", "write cpu profile to this file")
flag.StringVar(&memprofile, "memprofile", "", "write memory profile to this file")
flag.StringVar(&mutexprofile, "mutexprofile", "", "write mutex profile to this file")
flag.StringVar(&blockprofile, "blockprofile", "", "write block profile to this file")
flag.IntVar(&blockprofRate, "blockprof_rate", 0, "block profile rate")
flag.IntVar(&mutexprofFrac, "mutexprof_frac", 0, "mutex profile fraction")
flag.StringVar(&traceFile, "trace", "", `go trace output for "go tool trace"`)
var credHelper string
if h, ok := os.LookupEnv("SISO_CREDENTIAL_HELPER"); ok {
credHelper = h
} else {
credHelper = cred.DefaultCredentialHelper()
}
flag.StringVar(&credHelper, "credential_helper", credHelper, `path to a credential helper.
see https://github.com/EngFlow/credential-helper-spec/blob/main/spec.md
"luci-auth" uses luci-auth.
"gcloud" uses gcloud.
"google-application-default" or "" uses Google Application Default Credentials.
"mTLS" disables per RPC credentials.
environment variable SISO_CREDENTIAL_HELPER sets default value.`)
var printVersion bool
flag.BoolVar(&printVersion, "version", false, "print version")
flag.Parse()
ctx := context.Background()
// Flush the log on exit to not lose any messages.
defer log.Flush()
// Print a stack trace when a panic occurs.
defer func() {
if r := recover(); r != nil {
const size = 64 << 10
buf := make([]byte, size)
buf = buf[:runtime.Stack(buf, false)]
log.Fatalf("panic: %v\n%s", r, buf)
}
}()
if printVersion {
return int(version.Cmd(versionStr).Execute(ctx, flag.CommandLine))
}
if blockprofile != "" && blockprofRate == 0 {
blockprofRate = 1
}
if mutexprofile != "" && mutexprofFrac == 0 {
mutexprofFrac = 1
}
if blockprofRate > 0 {
runtime.SetBlockProfileRate(blockprofRate)
}
if mutexprofFrac > 0 {
runtime.SetMutexProfileFraction(mutexprofFrac)
}
// Start an HTTP server that can be used to profile Siso during runtime.
if pprofAddr != "" {
// https://pkg.go.dev/net/http/pprof
fmt.Fprintf(os.Stderr, "pprof is enabled, listening at http://%s/debug/pprof/\n", pprofAddr)
go func() {
log.Infof("pprof http listener: %v", http.ListenAndServe(pprofAddr, nil))
}()
defer func() {
fmt.Fprintf(os.Stderr, "pprof is still listening at http://%s/debug/pprof/\n", pprofAddr)
fmt.Fprintln(os.Stderr, "Press Ctrl-C to terminate the process")
sigch := make(chan os.Signal, 1)
signal.Notify(sigch, os.Interrupt, syscall.SIGTERM)
<-sigch
}()
}
// Save a CPU profile to disk on exit.
if cpuprofile != "" {
f, err := os.Create(cpuprofile)
if err != nil {
log.Fatalf("failed to create cpuprofile file: %v", err)
}
err = pprof.StartCPUProfile(f)
if err != nil {
log.Errorf("failed to start CPU profiler: %v", err)
}
defer pprof.StopCPUProfile()
}
// Save a heap profile to disk on exit.
if memprofile != "" {
f, err := os.Create(memprofile)
if err != nil {
log.Fatalf("failed to create memprofile file: %v", err)
}
defer func() {
err := pprof.WriteHeapProfile(f)
if err != nil {
log.Errorf("failed to write heap profile: %v", err)
}
}()
}
// Save a mutex profile to disk on exit.
if mutexprofile != "" {
f, err := os.Create(mutexprofile)
if err != nil {
log.Fatalf("failed to create mutexprofile file: %v", err)
}
defer func() {
if err := pprof.Lookup("mutex").WriteTo(f, 0); err != nil {
log.Errorf("failed to write mutex profile: %v", err)
}
if err := f.Close(); err != nil {
log.Errorf("failed to close mutexprofile file: %v", err)
}
}()
}
// Save a block profile to disk on exit.
if blockprofile != "" {
f, err := os.Create(blockprofile)
if err != nil {
log.Fatalf("failed to create blockprofile file: %v", err)
}
defer func() {
if err := pprof.Lookup("block").WriteTo(f, 0); err != nil {
log.Errorf("failed to write block profile: %v", err)
}
if err := f.Close(); err != nil {
log.Errorf("failed to close blockprofile file: %v", err)
}
}()
}
// Save a go trace to disk during execution.
if traceFile != "" {
fmt.Fprintf(os.Stderr, "enable go trace in %q\n", traceFile)
f, err := os.Create(traceFile)
if err != nil {
log.Fatalf("Failed to create go trace output file: %v", err)
}
defer func() {
fmt.Fprintf(os.Stderr, "go trace: go tool trace %s\n", traceFile)
cerr := f.Close()
if cerr != nil {
log.Fatalf("Failed to close go trace output file: %v", cerr)
}
}()
if err := trace.Start(f); err != nil {
log.Fatalf("Failed to start go trace: %v", err)
}
defer trace.Stop()
}
// Initialize the UI and ensure we restore the state of the terminal upon exit.
ui.Init()
defer ui.Restore()
authOpts := cred.AuthOpts(credHelper)
subcommands.Register(ninja.Cmd(authOpts, versionID), "")
subcommands.Register(collector.Cmd(authOpts, versionID), "")
subcommands.Register(recall.Cmd(authOpts), "reapi")
subcommands.Register(fetch.Cmd(authOpts), "reapi")
subcommands.Register(isolate.Cmd(authOpts), "reapi")
subcommands.Register(proxy.Cmd(authOpts), "reapi")
subcommands.Register(fscmd.Cmd(authOpts), "investigation")
subcommands.Register(metricscmd.Cmd(), "investigation")
subcommands.Register(ps.Cmd(), "investigation")
subcommands.Register(query.Cmd(), "investigation")
subcommands.Register(report.Cmd(), "investigation")
subcommands.Register(webui.Cmd(versionID), "investigation")
subcommands.Register(auth.CheckCmd(authOpts), "auth")
subcommands.Register(auth.LoginCmd(authOpts), "auth")
subcommands.Register(auth.LogoutCmd(authOpts), "auth")
subcommands.Register(ninjafrontend.Cmd(), "debugging")
subcommands.Register(sandbox.Cmd(), "debugging")
subcommands.Register(scandeps.Cmd(), "debugging")
subcommands.Register(osfs.HelperCmd(), "internal-helper")
subcommands.Register(subcommands.FlagsCommand(), "command-help")
subcommands.Register(subcommands.HelpCommand(), "command-help")
subcommands.Register(version.Cmd(versionStr), "command-help")
return int(subcommands.Execute(ctx))
}