Skip to content

Ignore dot-to-dot conversion #206

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 23 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,17 +53,17 @@ To install go-callvis, run:

```sh
# Latest release
go install github.com/ofabry/go-callvis@latest
go install github.com/OscarBohlin/go-callvis@latest

# Development version
go install github.com/ofabry/go-callvis@master
go install github.com/OscarBohlin/go-callvis@master
```

Alternatively, clone the repository and compile the source code:

```sh
# Clone repository
git clone https://github.com/ofabry/go-callvis.git
git clone https://github.com/OscarBohlin/go-callvis.git
cd go-callvis

# Compile and install
Expand Down Expand Up @@ -129,7 +129,7 @@ Usage of go-callvis:
-tests
Include test code.
-algo string
Use specific algorithm for package analyzer: static, cha or rta (default "static")
Use specific algorithm for package analyzer: static, cha, rta or vta (default "static")
-version
Show version and exit.
```
Expand All @@ -142,31 +142,31 @@ Here you can find descriptions for various types of output.

### Packages / Types

|Represents | Style|
|----------: | :-------------|
|`focused` | **blue** color|
|`stdlib` | **green** color|
|`other` | **yellow** color|
| Represents | Style |
| ---------: | :--------------- |
| `focused` | **blue** color |
| `stdlib` | **green** color |
| `other` | **yellow** color |

### Functions / Methods

|Represents | Style|
|-----------: | :--------------|
|`exported` | **bold** border|
|`unexported` | **normal** border|
|`anonymous` | **dotted** border|
| Represents | Style |
| -----------: | :---------------- |
| `exported` | **bold** border |
| `unexported` | **normal** border |
| `anonymous` | **dotted** border |

### Calls

|Represents | Style|
|-----------: | :-------------|
|`internal` | **black** color|
|`external` | **brown** color|
|`static` | **solid** line|
|`dynamic` | **dashed** line|
|`regular` | **simple** arrow|
|`concurrent` | arrow with **circle**|
|`deferred` | arrow with **diamond**|
| Represents | Style |
| -----------: | :--------------------- |
| `internal` | **black** color |
| `external` | **brown** color |
| `static` | **solid** line |
| `dynamic` | **dashed** line |
| `regular` | **simple** arrow |
| `concurrent` | arrow with **circle** |
| `deferred` | arrow with **diamond** |

## Examples

Expand Down
22 changes: 21 additions & 1 deletion analysis.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"golang.org/x/tools/go/callgraph"
"golang.org/x/tools/go/callgraph/cha"
"golang.org/x/tools/go/callgraph/rta"
"golang.org/x/tools/go/callgraph/vta"
"golang.org/x/tools/go/callgraph/static"

"golang.org/x/tools/go/packages"
Expand All @@ -29,6 +30,7 @@ const (
CallGraphTypeStatic CallGraphType = "static"
CallGraphTypeCha CallGraphType = "cha"
CallGraphTypeRta CallGraphType = "rta"
CallGraphTypeVta CallGraphType = "vta"
)

// ==[ type def/func: analysis ]===============================================
Expand Down Expand Up @@ -87,6 +89,7 @@ type analysis struct {
pkgs []*ssa.Package
mainPkg *ssa.Package
callgraph *callgraph.Graph
imports []*packages.Package
}

var Analysis *analysis
Expand Down Expand Up @@ -117,6 +120,14 @@ func (a *analysis) DoAnalysis(
return fmt.Errorf("packages contain errors")
}

if *importFlag != "" {
for _, p := range initial {
for _, i := range p.Imports {
a.imports = append(a.imports, i)
}
}
}

logf("loaded %d initial packages, building program", len(initial))

// Create and build SSA-form program representation.
Expand All @@ -134,6 +145,8 @@ func (a *analysis) DoAnalysis(
graph = static.CallGraph(prog)
case CallGraphTypeCha:
graph = cha.CallGraph(prog)
case CallGraphTypeVta:
fallthrough
case CallGraphTypeRta:
mains, err := mainPackages(prog.AllPackages())
if err != nil {
Expand All @@ -152,8 +165,15 @@ func (a *analysis) DoAnalysis(
for _, init := range inits {
roots = append(roots, init)
}

graph = rta.Analyze(roots, true).CallGraph

if algo == CallGraphTypeVta {
funcs := make(map[*ssa.Function]bool)
for fun := range graph.Nodes {
funcs[fun] = true
}
graph = vta.CallGraph(funcs, graph)
}
default:
return fmt.Errorf("invalid call graph type: %s", a.opts.algo)
}
Expand Down
2 changes: 1 addition & 1 deletion examples/main/main.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package main

import (
"github.com/ofabry/go-callvis/examples/main/mypkg"
"github.com/OscarBohlin/go-callvis/examples/main/mypkg"
)

func main() {
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module github.com/ofabry/go-callvis
module github.com/OscarBohlin/go-callvis

go 1.22.0

Expand Down
61 changes: 59 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
package main

import (
"encoding/csv"
"flag"
"fmt"
"go/build"
Expand All @@ -14,6 +15,8 @@ import (

"github.com/pkg/browser"
"golang.org/x/tools/go/buildutil"
"golang.org/x/tools/go/callgraph"
"golang.org/x/tools/go/ssa"
)

const Usage = `go-callvis: visualize call graph of a Go program.
Expand Down Expand Up @@ -42,9 +45,10 @@ var (
skipBrowser = flag.Bool("skipbrowser", false, "Skip opening browser.")
outputFile = flag.String("file", "", "output filename - omit to use server mode")
outputFormat = flag.String("format", "svg", "output file format [svg | png | jpg | ...]")
importFlag = flag.String("exportImport", "", "Writes the imports found in provided name or import path to specified file.")
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")
callgraphAlgo = flag.String("algo", string(CallGraphTypeStatic), fmt.Sprintf("The algorithm used to construct the call graph. Possible values inlcude: %q, %q, %q",
CallGraphTypeStatic, CallGraphTypeCha, CallGraphTypeRta))
callgraphAlgo = flag.String("algo", string(CallGraphTypeStatic), fmt.Sprintf("The algorithm used to construct the call graph. Possible values inlcude: %q, %q, %q, %q",
CallGraphTypeStatic, CallGraphTypeCha, CallGraphTypeRta, CallGraphTypeVta))

debugFlag = flag.Bool("debug", false, "Enable verbose log.")
versionFlag = flag.Bool("version", false, "Show version and exit.")
Expand Down Expand Up @@ -110,12 +114,61 @@ func outputDot(fname string, outputFormat string) {

log.Printf("converting dot to %s\n", outputFormat)

if outputFormat == "csv" {
dotToCsv(fmt.Sprintf("%v.csv", fname))
return
}

_, err = dotToImage(fname, outputFormat, output)
if err != nil {
log.Fatalf("%v\n", err)
}
}

func writeCsv(writer *csv.Writer, nodesMap map[*ssa.Function]*callgraph.Node) {
for fun, node := range nodesMap {
if node.Out == nil {
continue
}

line := []string{fun.String()}

for _, edge := range node.Out {
line = append(line, edge.Callee.Func.String())
}
err := writer.Write(line)
if err != nil {
log.Fatalf("error writing csv: %v\n", err)
}
}
}

//Creates a adjacency list in csv format for all nodes and edges
func dotToCsv(fname string) (error) {
f, err := os.Create(fname)
if err != nil {
log.Fatalf("could not create file %v: %v\n", fname, err)
}

writer := csv.NewWriter(f)
writeCsv(writer, Analysis.callgraph.Nodes)
writer.Flush()
f.Close()
return nil
}

func outputImports(fname string) {
output := ""
for _, p := range Analysis.imports {
output += fmt.Sprintf("%v\n", p.String())
}
bytes := []byte(output)
err := os.WriteFile(fname, bytes, 0755)
if err != nil {
log.Fatalf("%v\n", err)
}
}

//noinspection GoUnhandledErrorResult
func main() {
flag.Parse()
Expand Down Expand Up @@ -144,6 +197,10 @@ func main() {
log.Fatal(err)
}

if *importFlag != "" {
outputImports(*importFlag)
}

http.HandleFunc("/", handler)

if *outputFile == "" {
Expand Down
Loading