cmd/govulncheck: add -cpuprofile flag
Most of the time is spent in callGraph:
5s in CHA, 8s in VTA(1), 5s in VTA(2) when analyzing k8s.
Change-Id: If428095c68a8a5d47e9b709e02161d5f22952807
Reviewed-on: https://go-review.googlesource.com/c/vuln/+/457955
Reviewed-by: Tim King <taking@google.com>
Run-TryBot: Alan Donovan <adonovan@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
diff --git a/cmd/govulncheck/main.go b/cmd/govulncheck/main.go
index 96c4a2b..2eefce3 100644
--- a/cmd/govulncheck/main.go
+++ b/cmd/govulncheck/main.go
@@ -10,6 +10,8 @@
"fmt"
"os"
"path/filepath"
+ "runtime/pprof"
+
"strings"
"golang.org/x/tools/go/buildutil"
@@ -24,7 +26,9 @@
jsonFlag = flag.Bool("json", false, "output JSON")
verboseFlag = flag.Bool("v", false, "print a full call stack for each vulnerability")
testFlag = flag.Bool("test", false, "analyze test files. Only valid for source code.")
- tagsFlag buildutil.TagsFlag
+ cpuprofile = flag.String("cpuprofile", "", "write CPU profile to file")
+
+ tagsFlag buildutil.TagsFlag
// testmode flags. See main_testmode.go.
dirFlag string
@@ -58,6 +62,16 @@
os.Exit(1)
}
+ // Profiling support.
+ if *cpuprofile != "" {
+ f, err := os.Create(*cpuprofile)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+ pprof.StartCPUProfile(f)
+ }
+
patterns := flag.Args()
sourceAnalysis := true
@@ -66,14 +80,18 @@
}
validateFlags(sourceAnalysis)
- if err := doGovulncheck(patterns, sourceAnalysis); err != nil {
- die(fmt.Sprintf("govulncheck: %v", err))
+ err := doGovulncheck(patterns, sourceAnalysis)
+ pprof.StopCPUProfile()
+ if err != nil {
+ if code, ok := err.(exitCode); ok {
+ os.Exit(int(code))
+ }
+ die("govulncheck: %v", err)
}
}
-// doGovulncheck performs main govulncheck functionality and exits the
-// program upon success with an appropriate exit status. Otherwise,
-// returns an error.
+// doGovulncheck performs the main govulncheck functionality and
+// returns an error, possibly an exitCode.
func doGovulncheck(patterns []string, sourceAnalysis bool) error {
ctx := context.Background()
dir := filepath.FromSlash(dirFlag)
@@ -127,11 +145,11 @@
if *jsonFlag {
// Following golang.org/x/tools/go/analysis/singlechecker,
- // return 0 exit code in -json mode.
+ // -json mode is always a success.
if err := printJSON(res); err != nil {
return err
}
- os.Exit(0)
+ return nil // success
}
printText(res, *verboseFlag, sourceAnalysis)
@@ -144,16 +162,21 @@
if sourceAnalysis {
for _, v := range res.Vulns {
if v.IsCalled() {
- os.Exit(3)
+ return exitCode(3)
}
}
} else if len(res.Vulns) > 0 {
- os.Exit(3)
+ return exitCode(3)
}
- os.Exit(0)
return nil
}
+// exitCode is an error returned by doGovulncheck to indicate
+// that the the program should silently exit with the specified code.
+type exitCode int
+
+func (code exitCode) Error() string { return fmt.Sprintf("exit code %d", code) }
+
func validateFlags(source bool) {
if !source {
if *testFlag {
diff --git a/cmd/govulncheck/main_command_118_test.go b/cmd/govulncheck/main_command_118_test.go
index 9a0516c..385380b 100644
--- a/cmd/govulncheck/main_command_118_test.go
+++ b/cmd/govulncheck/main_command_118_test.go
@@ -36,7 +36,10 @@
if err != nil {
t.Fatal(err)
}
- ts.DisableLogging = false
+
+ // Comment this out to log all command output (very verbose).
+ ts.DisableLogging = true
+
// Define a command that runs govulncheck with our local DB. We can't use
// cmdtest.Program for this because it doesn't let us set the environment,
// and that is the only way to tell govulncheck about an alternative vuln
diff --git a/cmd/govulncheck/testdata/usage.ct b/cmd/govulncheck/testdata/usage.ct
index 72afbcc..92c8610 100644
--- a/cmd/govulncheck/testdata/usage.ct
+++ b/cmd/govulncheck/testdata/usage.ct
@@ -3,6 +3,8 @@
govulncheck [flags] package...
govulncheck [flags] binary
+ -cpuprofile string
+ write CPU profile to file
-dir string
directory to use for loading source files
-json
@@ -22,6 +24,8 @@
govulncheck [flags] package...
govulncheck [flags] binary
+ -cpuprofile string
+ write CPU profile to file
-dir string
directory to use for loading source files
-json