Print Go call tree - function

Given a file like this:
package main
func A() {}
func B() {
A()
}
func C() {
A()
}
func D() {
B()
}
func E() {
B()
}
func F() {
C()
}
func G() {
C()
}
func main() {
D()
E()
F()
G()
}
I would like to print a call tree of the program, something like this:
main
D
B
A
E
B
A
F
C
A
G
C
A
I found the callgraph program [1], but it does not create a tree:
PS C:\prog> callgraph .
prog.A --static-4:5--> prog.C
prog.A --static-5:5--> prog.D
prog.main --static-19:5--> prog.A
prog.B --static-9:5--> prog.E
prog.B --static-10:5--> prog.F
prog.main --static-20:5--> prog.B
Is some method available to do this?
https://github.com/golang/tools/blob/master/cmd/callgraph

So I did find a package that seems to handle printing a tree from a graph on the
command line [1]. However I thought about it some more, and a printed tree
might not be the best solution to my issue. What I want to do, is return an
error from one of my functions. However to do that, I need to propagate the
error all the way up to main. As this can be several layers, I thought it
would be best if I start from main, and work my way down to the desired
function. That way, I can the work in stages if need be. The issue is, how do I
get an ordered list of these functions? I found a solution with tsort [2]:
PS C:\> callgraph -format digraph . | coreutils tsort
"init/test.main"
"init/test.D"
"init/test.E"
"init/test.F"
"init/test.G"
"init/test.B"
"init/test.C"
"init/test.A"
but I may not always want the entire call graph. Next I thought about just adding
a panic:
func A() {
panic(1)
}
but this will not give you all branches, only the first path to the target
function:
main.A(...)
C:/test.go:4
main.B(...)
C:/test.go:8
main.D(...)
C:/test.go:16
main.main()
C:/test.go:32 +0x45
Finally I wrote my own sort function, that takes arbitrary destination as input,
and prints all paths in order from main to the target function:
package main
func tsort(graph map[string][]string, end string) []string {
var (
b = make(map[string]bool)
l []string
s = []string{end}
)
for len(s) > 0 {
n := s[len(s) - 1]
b[n] = true
for _, m := range graph[n] {
if ! b[m] {
s = append(s, m)
}
}
if s[len(s) - 1] == n {
s = s[:len(s) - 1]
l = append(l, n)
}
}
return l
}
Example:
package main
import (
"bytes"
"fmt"
"os/exec"
)
func main() {
b := new(bytes.Buffer)
c := exec.Command("callgraph", "-format", "digraph", ".")
c.Stdout = b
c.Run()
m := make(map[string][]string)
for {
var parent, child string
_, e := fmt.Fscanln(b, &parent, &child)
if e != nil { break }
m[child] = append(m[child], parent)
}
for n, s := range tsort(m, `"init/test.A"`) {
fmt.Print(n+1, ". ", s, "\n")
}
}
Result:
1. "init/test.main"
2. "init/test.G"
3. "init/test.F"
4. "init/test.C"
5. "init/test.D"
6. "init/test.E"
7. "init/test.B"
8. "init/test.A"
https://github.com/soniakeys/graph/blob/master/treevis/treevis.go
https://github.com/uutils/coreutils

Related

Recursive Function involving *html.Node to print all links in a HTML Doc

I am trying to print all links in a HTML Doc using a function which accepts *html.Node as an argument. I am new to both Golang and the *html.Node datatype, I have never worked with them before.
func visit(links []string, n *html.Node) []string {
if n == nil {
return links
}
if n.Type == html.ElementNode && n.Data == "a" {
for _, a := range n.Attr {
if a.Key == "href" {
links = append(links, a.Val)
}
}
}
if i == 0 {
i++
return visit(links, n.FirstChild)
}
return visit(links, n.NextSibling)
}
The aim of the if block which checks whether i==0 is to ensure that return visit(links, n.FirstChild) only runs once (the first time) and return visit(links, n.NextSibling) runs on the following iterations. However, links never gets appended, and always returns an empty slice. I do not understand the error in my code.
The code works perfectly fine when using a for loop, but breaks when I try to use recursion.
for c := n.FirstChild; c != nil; c = c.NextSibling {
links = visit(links, c)
}
Your code doesn't work as in it takes the first child of the document, which is html element and then it takes its sibling element which is nil thus causing the function to end with empty slice of links.
To explain in detail:
Here's an example code,
package main
import (
"fmt"
"log"
"strings"
"golang.org/x/net/html"
)
var i int = 0
func visit(links []string, n *html.Node) []string {
if n == nil {
return links
}
if n.Type == html.ElementNode && n.Data == "a" {
for _, a := range n.Attr {
if a.Key == "href" {
links = append(links, a.Val)
}
}
}
if i == 0 {
i++
return visit(links, n.FirstChild)
}
return visit(links, n.NextSibling)
}
func main() {
s := `<p>Links:</p><ul><li>Foo<li>BarBaz</ul>`
doc, err := html.Parse(strings.NewReader(s))
if err != nil {
log.Fatal(err)
}
links := visit([]string{}, doc)
fmt.Println(links)
}
1st call to visit,
Arguments:
links = []
n = DocumentNode
In the first call, i=0, so it makes a recursive call to visit with the first child of the document node.
2nd call to visit,
Arguments:
links = []
n = ElementNode (n.Data = "html")
In the 2nd call, n is the html element node. Now a 3rd call to visit is made with the next sibling of the html element node.
And this is where the problem lies.
There is no sibling for html element node, so n will be nil.
3rd call to visit,
Arguments:
links = []
n = nil
So, now all the function 3 function calls that were called recursively will return and the flow of execution will go back to main and hence the slice links will remain empty.
Hope you understood.
The proper way to code this functionality is by the loop you shared in your question, like this,
package main
import (
"fmt"
"log"
"strings"
"golang.org/x/net/html"
)
func visit(links []string, n *html.Node) []string {
if n.Type == html.ElementNode && n.Data == "a" {
for _, a := range n.Attr {
if a.Key == "href" {
links = append(links, a.Val)
}
}
}
for c := n.FirstChild; c != nil; c = c.NextSibling {
links = visit(links, c)
}
return links
}
func main() {
s := `<p>Links:</p><ul><li>Foo<li>BarBaz</ul>`
doc, err := html.Parse(strings.NewReader(s))
if err != nil {
log.Fatal(err)
}
links := visit([]string{}, doc)
fmt.Println(links)
}
Here, the loop helps to recursively find the links by checking each and every HTML element's children. If one of the HTML elements have no siblings, then it would simply move on to its parent's next sibling element and check
I'm struggling with the same problem for a while. According to the title the purpose of the question is how to replace for loop:
for c := n.FirstChild; c != nil; c = c.NextSibling {
links = visit(links, c)
}
with recursive function. This for loop is a standard way to traverse the tree document included in x/html go package. Despite that #Ramaraja answer was accepted it doesn't contain a proper solution.
Here, the loop helps to recursively find the links by checking each and every HTML element's children. If one of the HTML elements have no siblings, then it would simply move on to its parent's next sibling element and check
Based on this answer I tried this one:
if c := n.FirstChild; c != nil {
links = visit(links, c)
} else if c.NextSibling != nil {
links = visit(links, c.NextSibling)
}
The program triggers panic procedure. I'm not sure whether it is possible to avoid for loop at all.

How to add multiple values returned by a function directly

I have the following code.
package main
import "fmt"
func main() {
a := 0
b := 0
a, b += getValues()
fmt.Println(a, b)
}
func getValues() (a int, b int) {
a = 0
b = 5
return
}
I want to directly add the multiple values returned by a function. I just want to if there is a provision like this in Go.
When I run the above code, I get the following error.
syntax error: unexpected +=, expecting := or = or comma
You can use a helper method which takes a variadic number of parameters and just returns the slice created from the params
func aggregator(res ...interface{}) []interface{}{
return res
}
If you want to escape the extra type assertion you can set the type you want to work with, in your case int, for the input and output parameters of the helper function. But here is an example using interface{}:
func main() {
fmt.Printf("%d, %d", aggregator(f())[0].(int), aggregator(f())[1].(int))
}
func aggregator(res ...interface{}) []interface{}{
return res
}
func f () (int, int) {
return 1,2
}
Go Playground.

How to define a function which returns a map

I am attempting to define a function which returns an initialized map:
package main
import "fmt"
import "os"
func defaults() map {
m := make(map[string]string)
m["start"] = "1"
m["from"] = "encrypted"
m["to"] = "loaded"
return m
}
func main() {
args := os.Args[1:]
fmt.Println(args)
runvals := defaults()
fmt.Println(runvals)
}
Errors I'm getting:
Line 6 col 21 error| expected '[', found '{'
Line 7 col 5 error| expected ']', found ':='
Line 11 col 3 error| expected declaration, found 'return'
Can someone help me get the syntax right? Or am I trying to do something that Go doesn't do?
You need to declare the whole type including key and value types.
func defaults() map[string]string {
…
}
func defaults() map[string] string {
m := make(map[string]string)
m["start"] = "1"
...
return m
}
The problem with your defaults function is the return type map has no types.

Golang Function Call in Map

I am a newbie at GO Programming. Here is scenario :-
There exists a JSON file that looks like this :-
{
"template": "linuxbase1",
"checkname": ["check_disk"],
"checkmethod": ["check_disk"]
}
I am Unmarshalling this data into a structure :-
package func1
import (
"io/ioutil"
"os"
"encoding/json"
"fmt"
)
type pluginfunc func() string
type Plugindata struct {
Template string `json:"template"`
Checkname []string `json:"checkname"`
Checkmethod []pluginfunc `json:"checkmethod"`
}
var (
Templatepath = "json_sample1.json"
Templateitems Plugindata
)
func Gettemplatedata() {
tdata, err := ioutil.ReadFile(Templatepath)
if err != nil {
fmt.Printf("Unable to read file %s. Error - %v\n",Templatepath, err.Error())
os.Exit(3)
}
json.Unmarshal(tdata, &Templateitems)
}
The "check_disk" function is here :-
package func1
func check_disk() string {
return "Called check_disk"
}
This is the program with main() :-
package main
import (
"fmt"
"checksexpt/func1"
)
func main() {
func1.Gettemplatedata()
fmt.Printf("Templateitems in Main() => %v\n",func1.Templateitems)
for index,funcname := range func1.Templateitems.Checkmethod {
fmt.Printf("%d = %s\n",index,funcname())
}
}
As expected, when I run main(); I see the error :-
Templateitems in Main() => {linuxbase1 [check_cpu check_disk] [<nil> <nil>]}
panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xb code=0x1 addr=0x0 pc=0x40115e]
goroutine 1 [running]:
panic(0x50e980, 0xc82000a100)
/opt/go/src/runtime/panic.go:481 +0x3e6
So, I am trying to grab a string from the JSON file and treat it as a function call. That obviously fails ! But, the primary constraint here is that the function names have to be picked from the JSON file. How can I do this ? I know that I can create a static map as follows :-
type checkfunc func() string
var (
Templateitems = map[string]map[string]checkfunc {
"linuxbase1": {
"check_disk": check_disk,
},
}
)
So, A call like - Templateitems["linuxbase1"]["check_disk"]() would work just fine. But, I dont want to create any such static map as the elements in that map needs to keep growing. Any ideas on this?
There is no direct way to parse a function directly from a JSON value. Also, you cannot use string values to refer to variables. So a string check_cpu would not be able to refer to the function with the same name directly.
What you can do instead is parse the json string as is and have a global map for functions. That way, you can call your functions like so:
var funcMap = map[string]pluginfunc{
"check_disk": check_disk,
"check_cpu": check_cpu
}
In your main loop:
for index, funcname := range func1.Templateitems.Checkmethod {
fmt.Printf("%d = %s\n", index, funcMap[funcname]())
}
If however, you really need to put the value in your structure, you can try implementing UnmarshalJSON from the json.Unmarshaler interface. A simple example would be:
type pf map[string]pluginfunc
type Plugindata struct {
Template string `json:"template"`
Checkname []string `json:"checkname"`
Checkmethod pf `json:"checkmethod"`
}
func (p *pf) UnmarshalJSON(data []byte) error {
d := []string{}
if err := json.Unmarshal(data, &d); err != nil {
return err
}
*p = make(pf)
for _, s := range d {
(*p)[s] = funcMap[s]
}
return nil
}
var funcMap = pf{
"check_disk": check_disk,
"check_cpu": check_cpu
}
func main() {
json.Unmarshal(tdata, &Templateitems)
for k, f := range Templateitems.Checkmethod {
fmt.Printf("%s -- %s\n", k, f())
}
}
Working code
Note that this way is not as readable or simple as the first method and it still relies on a function map.
You can read more about json.Unmarshaler here.

Go: json encoding struct with pointers is slower than with copies?

I have the following test code:
package main
import (
"fmt"
"testing"
"encoding/json"
)
type Coll1 struct {
A string
B string
C string
}
type Coll2 struct {
A *string
B *string
C *string
}
var as = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
var bs = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
var cs = "ccccccccccccccccccccccccccccccccc"
func testBM1(b *testing.B) {
for i := 0; i<b.N; i++ {
json.Marshal(Coll1{as,bs,cs})
}
}
func testBM2(b *testing.B) {
for i := 0; i<b.N; i++ {
json.Marshal(Coll2{&as,&bs,&cs})
}
}
func main() {
fmt.Println(testing.Benchmark(testBM1))
fmt.Println(testing.Benchmark(testBM2))
}
I would expect the second case to run faster since it is using pointers and therefore doesn't have to copy the strings, but in fact it runs at about 4250 ns/op where the first runs near 2800 ns/op. Can anyone shed any light on why this might be?
Edit: Darshan Computing suggested that this may hold true for embedded structs even. A simple test confirms this:
package main
import (
"fmt"
"testing"
"encoding/json"
)
type Coll1 struct {
A,B,C string
}
type Coll1Outer struct {
A,B,C Coll1
}
type Coll2Outer struct {
A,B,C *Coll2
}
type Coll2 struct {
A,B,C *string
}
var as = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
var bs = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
var cs = "ccccccccccccccccccccccccccccccccc"
func testBM1(b *testing.B) {
for i := 0; i<b.N; i++ {
c := Coll1Outer{ Coll1{as,bs,cs},
Coll1{as,bs,cs},
Coll1{as,bs,cs} }
json.Marshal(c)
}
}
func testBM2(b *testing.B) {
for i := 0; i<b.N; i++ {
c := Coll2Outer{ &Coll2{&as,&bs,&cs},
&Coll2{&as,&bs,&cs},
&Coll2{&as,&bs,&cs} }
json.Marshal(c)
}
}
func main() {
fmt.Println(testing.Benchmark(testBM1))
fmt.Println(testing.Benchmark(testBM2))
}
For me this shows the non-pointer struct taking about 12ms/op, while the one with pointers takes 13ms/op. Not a huge difference, but it's interesting that the property still holds.
I notice the biggest percentage difference in ns/op when I set as, bs, and cs to "a", "b", and "c", respectively. As I increase the length of the strings, they approach each other. They seem to always be about 1000 ns/op different.
So I believe all that's going on is that it takes 1000 ns on my machine (1450 on yours) to reflect and follow the pointers. Passing a smaller struct up front doesn't seem to counteract this effect because once the pointers are followed, Marshal still passes the data around internally in the process of generating and returning the equivalent JSON.