diff --git a/analysis.go b/analysis.go index 86ad102..0298aa6 100644 --- a/analysis.go +++ b/analysis.go @@ -87,6 +87,7 @@ type analysis struct { pkgs []*ssa.Package mainPkg *ssa.Package callgraph *callgraph.Graph + initial []*packages.Package } var Analysis *analysis @@ -101,7 +102,7 @@ func (a *analysis) DoAnalysis( defer logf("analysis done") cfg := &packages.Config{ - Mode: packages.LoadAllSyntax, + Mode: packages.LoadAllSyntax | packages.NeedModule, Tests: tests, Dir: dir, BuildFlags: getBuildFlags(), @@ -164,6 +165,7 @@ func (a *analysis) DoAnalysis( a.pkgs = pkgs a.mainPkg = mainPkg a.callgraph = graph + a.initial = initial return nil } @@ -235,6 +237,8 @@ func (a *analysis) OverrideByHTTP(r *http.Request) { } if std := r.FormValue("std"); std != "" { a.opts.nostd = false + } else if nostd := r.FormValue("nostd"); nostd != "" { + a.opts.nostd = true } if inter := r.FormValue("nointer"); inter != "" { a.opts.nointer = true @@ -299,6 +303,7 @@ func (a *analysis) Render() ([]byte, error) { } dot, err := printOutput( + a.initial, a.prog, a.mainPkg, a.callgraph, diff --git a/dot.go b/dot.go index 87145d7..0e3863b 100644 --- a/dot.go +++ b/dot.go @@ -167,18 +167,18 @@ func runDotToImageCallSystemGraphviz(outfname string, format string, dot []byte) dotSystemBinary = dot } - var img string + var path string if outfname == "" { - img = filepath.Join(os.TempDir(), fmt.Sprintf("go-callvis_export.%s", format)) + path = filepath.Join(os.TempDir(), fmt.Sprintf("go-callvis_export.%s", format)) } else { - img = fmt.Sprintf("%s.%s", outfname, format) + path = fmt.Sprintf("%s.%s", outfname, format) } - cmd := exec.Command(dotSystemBinary, fmt.Sprintf("-T%s", format), "-o", img) + cmd := exec.Command(dotSystemBinary, fmt.Sprintf("-T%s", format), "-o", path) cmd.Stdin = bytes.NewReader(dot) var stderr bytes.Buffer cmd.Stderr = &stderr if err := cmd.Run(); err != nil { return "", fmt.Errorf("command '%v': %v\n%v", cmd, err, stderr.String()) } - return img, nil + return path, nil } diff --git a/dot_cgo.go b/graphviz_cgo.go similarity index 70% rename from dot_cgo.go rename to graphviz_cgo.go index 4821bdc..baf7887 100644 --- a/dot_cgo.go +++ b/graphviz_cgo.go @@ -1,5 +1,4 @@ //go:build cgo -// +build cgo package main @@ -26,14 +25,14 @@ func runDotToImage(outfname string, format string, dot []byte) (string, error) { log.Printf("error closing graphviz: %v", err) } }() - var img string + var path string if outfname == "" { - img = filepath.Join(os.TempDir(), fmt.Sprintf("go-callvis_export.%s", format)) + path = filepath.Join(os.TempDir(), fmt.Sprintf("go-callvis_export.%s", format)) } else { - img = fmt.Sprintf("%s.%s", outfname, format) + path = fmt.Sprintf("%s.%s", outfname, format) } - if err := g.RenderFilename(graph, graphviz.Format(format), img); err != nil { + if err := g.RenderFilename(graph, graphviz.Format(format), path); err != nil { return "", err } - return img, nil + return path, nil } diff --git a/dot_nocgo.go b/graphviz_nocgo.go similarity index 92% rename from dot_nocgo.go rename to graphviz_nocgo.go index bfde541..0585331 100644 --- a/dot_nocgo.go +++ b/graphviz_nocgo.go @@ -1,5 +1,4 @@ //go:build !cgo -// +build !cgo package main diff --git a/handler.go b/handler.go index 948920c..002cd9b 100644 --- a/handler.go +++ b/handler.go @@ -5,6 +5,7 @@ import ( "log" "net/http" "strings" + "time" ) func handler(w http.ResponseWriter, r *http.Request) { @@ -13,10 +14,18 @@ func handler(w http.ResponseWriter, r *http.Request) { return } + logf("----------------------") - logf(" => handling request: %v", r.URL) + logf(" => handling request: %v %v", r.Method, r.URL) logf("----------------------") + t0 := time.Now() + defer func() { + logf("----------------------") + logf(" -> request done (took %.3fs): %v", time.Since(t0).Round(time.Millisecond).Seconds(), r.URL) + logf("----------------------") + }() + // set up cmdline default for analysis Analysis.OptsSetup() @@ -49,13 +58,16 @@ func handler(w http.ResponseWriter, r *http.Request) { } log.Printf("converting dot to %s\n", *outputFormat) - + t := time.Now() + img, err = dotToImage("", *outputFormat, output) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } + log.Printf("conversion done (took %.3fs)", time.Since(t).Round(time.Millisecond).Seconds()) + err = Analysis.CacheImg(img) if err != nil { http.Error(w, "cache img error: "+err.Error(), http.StatusBadRequest) diff --git a/output.go b/output.go index 579a874..8e2bedc 100644 --- a/output.go +++ b/output.go @@ -9,6 +9,7 @@ import ( "strings" "golang.org/x/tools/go/callgraph" + "golang.org/x/tools/go/packages" "golang.org/x/tools/go/ssa" ) @@ -31,18 +32,27 @@ func isStdPkgPath(path string) bool { return true } -func printOutput( - prog *ssa.Program, - mainPkg *ssa.Package, - cg *callgraph.Graph, - focusPkg *types.Package, - limitPaths, - ignorePaths, - includePaths []string, - groupBy []string, - nostd, - nointer bool, -) ([]byte, error) { +func printOutput(initial []*packages.Package, prog *ssa.Program, mainPkg *ssa.Package, cg *callgraph.Graph, focusPkg *types.Package, limitPaths, ignorePaths, includePaths, groupBy []string, nostd, nointer bool, ) ([]byte, error) { + var initialPkg *packages.Package + for _, p := range initial { + if p.PkgPath == mainPkg.Pkg.Path() { + initialPkg = p + break + } + } + if initialPkg != nil { + logf("initial package: %+v, module: %+v", initialPkg, initialPkg.Module) + } else { + logf("initial package: NOT FOUND") + } + + var modulePath string + if initialPkg != nil && initialPkg.Module != nil { + modulePath = initialPkg.Module.Path + } else { + split := strings.SplitN(mainPkg.Pkg.Path(), "/", 3) + modulePath = strings.Join(split[:2], "/") + } logf("printing output for: %v", focusPkg) logf("src dirs: %+v, default build context: %+v", build.Default.SrcDirs(), build.Default) @@ -264,9 +274,11 @@ func printOutput( if isFocused { attrs["fillcolor"] = "lightblue" } else if isStdPkg { - attrs["fillcolor"] = "#adedad" + attrs["fillcolor"] = "#bfffbf" + } else if strings.HasPrefix(pkgPath, modulePath) { + attrs["fillcolor"] = "#FFF9CA" } else { - attrs["fillcolor"] = "moccasin" + attrs["fillcolor"] = "#fccfcf" } // include pkg name @@ -303,7 +315,7 @@ func printOutput( "fontsize": "16", "label": label, "style": "filled", - "fillcolor": "lightyellow", + "fillcolor": "mistyrose", "URL": fmt.Sprintf("/?f=%s", key), "fontname": "Tahoma bold", "tooltip": fmt.Sprintf("package: %s", key), @@ -312,6 +324,8 @@ func printOutput( } if isStdPkg { c.Clusters[key].Attrs["fillcolor"] = "#E0FFE1" + } else if strings.HasPrefix(node.Func.Pkg.Pkg.Path(), modulePath) { + c.Clusters[key].Attrs["fillcolor"] = "lightyellow" } } c = c.Clusters[key] @@ -332,7 +346,7 @@ func printOutput( "label": label, "labelloc": "b", "style": "rounded,filled", - "fillcolor": "wheat2", + "fillcolor": "#DCAFAF", "tooltip": fmt.Sprintf("type: %s", key), }, } @@ -340,6 +354,8 @@ func printOutput( c.Clusters[key].Attrs["fillcolor"] = "lightsteelblue" } else if isStdPkg { c.Clusters[key].Attrs["fillcolor"] = "#c2e3c2" + } else if strings.HasPrefix(node.Func.Pkg.Pkg.Path(), modulePath) { + c.Clusters[key].Attrs["fillcolor"] = "wheat2" } } c = c.Clusters[key]