Go-Callvis Viewer

Home|gocallvis/main.go
1// go-callvis: a tool to help visualize the call graph of a Go program.
2//
3package main
4
5import (
6    "flag"
7    "fmt"
8    "go/build"
9    "io/ioutil"
10    "log"
11    "net"
12    "net/http"
13    "net/url"
14    "os"
15    "time"
16
17    "github.com/pkg/browser"
18    "golang.org/x/tools/go/buildutil"
19)
20
21const Usage = `go-callvis: visualize call graph of a Go program.
22
23Usage:
24
25  go-callvis [flags] package
26
27  Package should be main package, otherwise -tests flag must be used.
28
29Flags:
30
31`
32
33var (
34    focusFlag     = flag.String("focus""main""Focus specific package using name or import path.")
35    groupFlag     = flag.String("group""pkg""Grouping functions by packages and/or types [pkg, type] (separated by comma)")
36    limitFlag     = flag.String("limit""""Limit package paths to given prefixes (separated by comma)")
37    ignoreFlag    = flag.String("ignore""""Ignore package paths containing given prefixes (separated by comma)")
38    includeFlag   = flag.String("include""""Include package paths with given prefixes (separated by comma)")
39    nostdFlag     = flag.Bool("nostd"false"Omit calls to/from packages in standard library.")
40    nointerFlag   = flag.Bool("nointer"false"Omit calls to unexported functions.")
41    testFlag      = flag.Bool("tests"false"Include test code.")
42    graphvizFlag  = flag.Bool("graphviz"false"Use Graphviz's dot program to render images.")
43    httpFlag      = flag.String("http"":7878""HTTP service address.")
44    skipBrowser   = flag.Bool("skipbrowser"false"Skip opening browser.")
45    outputFile    = flag.String("file""""output filename - omit to use server mode")
46    outputFormat  = flag.String("format""svg""output file format [svg | png | jpg | ...]")
47    cacheDir      = flag.String("cacheDir""""Enable caching to avoid unnecessary re-rendering, you can force rendering by adding 'refresh=true' to the URL query or emptying the cache directory")
48    callgraphAlgo = flag.String("algo""pointer"fmt.Sprintf("The algorithm used to construct the call graph. Possible values inlcude: %q, %q, %q, %q",
49        "static""cha""rta""pointer"))
50
51    debugFlag   = flag.Bool("debug"false"Enable verbose log.")
52    versionFlag = flag.Bool("version"false"Show version and exit.")
53)
54
55func init() {
56    flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags"buildutil.TagsFlagDoc)
57    // Graphviz options
58    flag.UintVar(&minlen"minlen"2"Minimum edge length (for wider output).")
59    flag.Float64Var(&nodesep"nodesep"0.35"Minimum space between two adjacent nodes in the same rank (for taller output).")
60    flag.StringVar(&nodeshape"nodeshape""box""graph node shape (see graphvis manpage for valid values)")
61    flag.StringVar(&nodestyle"nodestyle""filled,rounded""graph node style (see graphvis manpage for valid values)")
62    flag.StringVar(&rankdir"rankdir""LR""Direction of graph layout [LR | RL | TB | BT]")
63}
64
65func logf(f stringa ...interface{}) {
66    if *debugFlag {
67        log.Printf(fa...)
68    }
69}
70
71func parseHTTPAddr(addr stringstring {
72    hostport_ := net.SplitHostPort(addr)
73    if host == "" {
74        host = "localhost"
75    }
76    if port == "" {
77        port = "80"
78    }
79    u := url.URL{
80        Scheme"http",
81        Host:   fmt.Sprintf("%s:%s"hostport),
82    }
83    return u.String()
84}
85
86func openBrowser(url string) {
87    time.Sleep(time.Millisecond * 100)
88    if err := browser.OpenURL(url); err != nil {
89        log.Printf("OpenURL error: %v"err)
90    }
91}
92
93func outputDot(fname stringoutputFormat string) {
94    // get cmdline default for analysis
95    Analysis.OptsSetup()
96
97    if e := Analysis.ProcessListArgs(); e != nil {
98        log.Fatalf("%v\n"e)
99    }
100
101    outputerr := Analysis.Render()
102    if err != nil {
103        log.Fatalf("%v\n"err)
104    }
105
106    log.Println("writing dot output..")
107
108    writeErr := ioutil.WriteFile(fmt.Sprintf("%s.gv"fname), output0755)
109    if writeErr != nil {
110        log.Fatalf("%v\n"writeErr)
111    }
112
113    log.Printf("converting dot to %s..\n"outputFormat)
114
115    _err = dotToImage(fnameoutputFormatoutput)
116    if err != nil {
117        log.Fatalf("%v\n"err)
118    }
119}
120
121//noinspection GoUnhandledErrorResult
122func main() {
123    flag.Parse()
124
125    if *versionFlag {
126        fmt.Fprintln(os.StderrVersion())
127        os.Exit(0)
128    }
129    if *debugFlag {
130        log.SetFlags(log.Lmicroseconds)
131    }
132
133    if flag.NArg() != 1 {
134        fmt.Fprint(os.StderrUsage)
135        flag.PrintDefaults()
136        os.Exit(2)
137    }
138
139    args := flag.Args()
140    tests := *testFlag
141    httpAddr := *httpFlag
142    urlAddr := parseHTTPAddr(httpAddr)
143
144    Analysis = new(analysis)
145    if err := Analysis.DoAnalysis(CallGraphType(*callgraphAlgo), ""testsargs); err != nil {
146        log.Fatal(err)
147    }
148
149    http.HandleFunc("/"handler)
150
151    if *outputFile == "" {
152        *outputFile = "output"
153        if !*skipBrowser {
154            go openBrowser(urlAddr)
155        }
156
157        log.Printf("http serving at %s"urlAddr)
158
159        if err := http.ListenAndServe(httpAddrnil); err != nil {
160            log.Fatal(err)
161        }
162    } else {
163        outputDot(*outputFile, *outputFormat)
164    }
165}
166
MembersX
flag
time
logf
parseHTTPAddr.port
buildutil
Usage
outputDot.writeErr
main.httpAddr
net
browser
init
parseHTTPAddr._
outputDot.e
openBrowser.url
openBrowser.err
main
main.err
main.BlockStmt.err
ioutil
logf.f
logf.a
parseHTTPAddr
outputDot.outputFormat
main.args
parseHTTPAddr.u
outputDot.fname
outputDot.err
url
parseHTTPAddr.addr
openBrowser
outputDot
outputDot.output
main.tests
parseHTTPAddr.host
main.urlAddr
Members
X