Skip to content
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

Draft configuration setup #41

Open
wants to merge 7 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
15 changes: 0 additions & 15 deletions .github/ISSUE_TEMPLATE.md

This file was deleted.

177 changes: 0 additions & 177 deletions .github/fib.svg

This file was deleted.

55 changes: 55 additions & 0 deletions .snapshots/TestConfig_MapFullNames
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
digraph structs {
node [shape=Mrecord];
3 [label="<name> 0"];
4 [label="<name> 0"];
5 [label="<name> (0+0i)"];
6 [label="<name> (0+0i)"];
7 [label="<name> 0"];
8 [label="<name> 0"];
9 [label="<name> 0"];
10 [label="<name> 0"];
11 [label="<name> 0"];
12 [label="<name> 0"];
13 [label="<name> 0"];
14 [label="<name> 0"];
15 [label="<name> 0"];
16 [label="<name> (0+0i)"];
17 [label="<name> (0+0i)"];
18 [label="<name> 0"];
19 [label="<name> 0"];
20 [label="<name> 0"];
21 [label="<name> 0"];
22 [label="<name> 0"];
2 [label="<name> github.com/bradleyjkemp/memviz_test.basicNumerics |{<f0> uint8 | 0} |{<f1> uint32 | 0} |{<f2> uint64 | 0} |{<f3> int8 | 0} |{<f4> int16 | 0} |{<f5> int32 | 0} |{<f6> int64 | 0} |<f7> float32|<f8> float64|<f9> complex64|<f10> complex128|{<f11> byte | 0} |{<f12> rune | 0} |{<f13> uint | 0} |{<f14> int | 0} |<f15> uintptr|<f16> Ptruint32|<f17> Ptruint64|<f18> Ptrint8|<f19> Ptrint16|<f20> Ptrint32|<f21> Ptrint64|<f22> Ptrfloat32|<f23> Ptrfloat64|<f24> Ptrcomplex64|<f25> Ptrcomplex128|<f26> Ptrbyte|<f27> Ptrrune|<f28> Ptruint|<f29> Ptrint|<f30> Ptruintptr "];
2:f7 -> 3:name;
2:f8 -> 4:name;
2:f9 -> 5:name;
2:f10 -> 6:name;
2:f15 -> 7:name;
2:f16 -> 8:name;
2:f17 -> 9:name;
2:f18 -> 10:name;
2:f19 -> 11:name;
2:f20 -> 12:name;
2:f21 -> 13:name;
2:f22 -> 14:name;
2:f23 -> 15:name;
2:f24 -> 16:name;
2:f25 -> 17:name;
2:f26 -> 18:name;
2:f27 -> 19:name;
2:f28 -> 20:name;
2:f29 -> 21:name;
2:f30 -> 22:name;
23 [label="<name> []string |{<23index0> 0|<23value0> \"Hello\"}|{<23index1> 1|<23value1> \"Brave\"}|{<23index2> 2|<23value2> \"New\"}|{<23index3> 3|<23value3> \"World\"} "];
24 [label="<name> \"Hello\""];
25 [label="<name> \"interfaceValue\""];
1 [label="<name> github.com/bradleyjkemp/memviz_test.basics |<f0> numerics|{<f1> string | \"Hi\"} |<f2> slice|<f3> ptr|<f4> iface "];
1:f0 -> 2:name;
1:f2 -> 23:name;
1:f3 -> 24:name;
1:f4 -> 25:name;
26 [label="<name> *memviz_test.basics"];
26:name -> 1:name;
}

11 changes: 11 additions & 0 deletions .snapshots/TestConfig_MapIgnoreOneField
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
digraph structs {
node [shape=Mrecord];
3 [label="<name> \"Hello\""];
4 [label="<name> \"interfaceValue\""];
1 [label="<name> basics2 |{<f1> string | \"Hi\"} |{<f2> slice | {{<2index0> 0|<2value0> \"Hello\"}|{<2index1> 1|<2value1> \"World\"}}} |<f3> ptr|<f4> iface "];
1:f3 -> 3:name;
1:f4 -> 4:name;
5 [label="<name> *memviz_test.basics2"];
5:name -> 1:name;
}

11 changes: 11 additions & 0 deletions .snapshots/TestConfig_MapMaxDepth1
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
digraph structs {
node [shape=Mrecord];
2 [label="<name> \"Hello\""];
3 [label="<name> \"interfaceValue\""];
1 [label="<name> basics |{<f1> string | \"Hi\"} |<f3> ptr|<f4> iface "];
1:f3 -> 2:name;
1:f4 -> 3:name;
4 [label="<name> *memviz_test.basics"];
4:name -> 1:name;
}

13 changes: 13 additions & 0 deletions .snapshots/TestConfig_MapMaxDepth2
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
digraph structs {
node [shape=Mrecord];
2 [label="<name> []string |{<2index0> 0|<2value0> \"Hello\"}|{<2index1> 1|<2value1> \"Brave\"}|{<2index2> 2|<2value2> \"New\"}|{<2index3> 3|<2value3> \"World\"} "];
3 [label="<name> \"Hello\""];
4 [label="<name> \"interfaceValue\""];
1 [label="<name> basics |{<f1> string | \"Hi\"} |<f2> slice|<f3> ptr|<f4> iface "];
1:f2 -> 2:name;
1:f3 -> 3:name;
1:f4 -> 4:name;
5 [label="<name> *memviz_test.basics"];
5:name -> 1:name;
}

55 changes: 55 additions & 0 deletions .snapshots/TestConfig_MapMaxDepth3
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
digraph structs {
node [shape=Mrecord];
3 [label="<name> 0"];
4 [label="<name> 0"];
5 [label="<name> (0+0i)"];
6 [label="<name> (0+0i)"];
7 [label="<name> 0"];
8 [label="<name> 0"];
9 [label="<name> 0"];
10 [label="<name> 0"];
11 [label="<name> 0"];
12 [label="<name> 0"];
13 [label="<name> 0"];
14 [label="<name> 0"];
15 [label="<name> 0"];
16 [label="<name> (0+0i)"];
17 [label="<name> (0+0i)"];
18 [label="<name> 0"];
19 [label="<name> 0"];
20 [label="<name> 0"];
21 [label="<name> 0"];
22 [label="<name> 0"];
2 [label="<name> basicNumerics |{<f0> uint8 | 0} |{<f1> uint32 | 0} |{<f2> uint64 | 0} |{<f3> int8 | 0} |{<f4> int16 | 0} |{<f5> int32 | 0} |{<f6> int64 | 0} |<f7> float32|<f8> float64|<f9> complex64|<f10> complex128|{<f11> byte | 0} |{<f12> rune | 0} |{<f13> uint | 0} |{<f14> int | 0} |<f15> uintptr|<f16> Ptruint32|<f17> Ptruint64|<f18> Ptrint8|<f19> Ptrint16|<f20> Ptrint32|<f21> Ptrint64|<f22> Ptrfloat32|<f23> Ptrfloat64|<f24> Ptrcomplex64|<f25> Ptrcomplex128|<f26> Ptrbyte|<f27> Ptrrune|<f28> Ptruint|<f29> Ptrint|<f30> Ptruintptr "];
2:f7 -> 3:name;
2:f8 -> 4:name;
2:f9 -> 5:name;
2:f10 -> 6:name;
2:f15 -> 7:name;
2:f16 -> 8:name;
2:f17 -> 9:name;
2:f18 -> 10:name;
2:f19 -> 11:name;
2:f20 -> 12:name;
2:f21 -> 13:name;
2:f22 -> 14:name;
2:f23 -> 15:name;
2:f24 -> 16:name;
2:f25 -> 17:name;
2:f26 -> 18:name;
2:f27 -> 19:name;
2:f28 -> 20:name;
2:f29 -> 21:name;
2:f30 -> 22:name;
23 [label="<name> []string |{<23index0> 0|<23value0> \"Hello\"}|{<23index1> 1|<23value1> \"Brave\"}|{<23index2> 2|<23value2> \"New\"}|{<23index3> 3|<23value3> \"World\"} "];
24 [label="<name> \"Hello\""];
25 [label="<name> \"interfaceValue\""];
1 [label="<name> basics |<f0> numerics|{<f1> string | \"Hi\"} |<f2> slice|<f3> ptr|<f4> iface "];
1:f0 -> 2:name;
1:f2 -> 23:name;
1:f3 -> 24:name;
1:f4 -> 25:name;
26 [label="<name> *memviz_test.basics"];
26:name -> 1:name;
}

53 changes: 53 additions & 0 deletions .snapshots/TestConfig_MapMaxInlineMedium
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
digraph structs {
node [shape=Mrecord];
3 [label="<name> 0"];
4 [label="<name> 0"];
5 [label="<name> (0+0i)"];
6 [label="<name> (0+0i)"];
7 [label="<name> 0"];
8 [label="<name> 0"];
9 [label="<name> 0"];
10 [label="<name> 0"];
11 [label="<name> 0"];
12 [label="<name> 0"];
13 [label="<name> 0"];
14 [label="<name> 0"];
15 [label="<name> 0"];
16 [label="<name> (0+0i)"];
17 [label="<name> (0+0i)"];
18 [label="<name> 0"];
19 [label="<name> 0"];
20 [label="<name> 0"];
21 [label="<name> 0"];
22 [label="<name> 0"];
2 [label="<name> basicNumerics |{<f0> uint8 | 0} |{<f1> uint32 | 0} |{<f2> uint64 | 0} |{<f3> int8 | 0} |{<f4> int16 | 0} |{<f5> int32 | 0} |{<f6> int64 | 0} |<f7> float32|<f8> float64|<f9> complex64|<f10> complex128|{<f11> byte | 0} |{<f12> rune | 0} |{<f13> uint | 0} |{<f14> int | 0} |<f15> uintptr|<f16> Ptruint32|<f17> Ptruint64|<f18> Ptrint8|<f19> Ptrint16|<f20> Ptrint32|<f21> Ptrint64|<f22> Ptrfloat32|<f23> Ptrfloat64|<f24> Ptrcomplex64|<f25> Ptrcomplex128|<f26> Ptrbyte|<f27> Ptrrune|<f28> Ptruint|<f29> Ptrint|<f30> Ptruintptr "];
2:f7 -> 3:name;
2:f8 -> 4:name;
2:f9 -> 5:name;
2:f10 -> 6:name;
2:f15 -> 7:name;
2:f16 -> 8:name;
2:f17 -> 9:name;
2:f18 -> 10:name;
2:f19 -> 11:name;
2:f20 -> 12:name;
2:f21 -> 13:name;
2:f22 -> 14:name;
2:f23 -> 15:name;
2:f24 -> 16:name;
2:f25 -> 17:name;
2:f26 -> 18:name;
2:f27 -> 19:name;
2:f28 -> 20:name;
2:f29 -> 21:name;
2:f30 -> 22:name;
24 [label="<name> \"Hello\""];
25 [label="<name> \"interfaceValue\""];
1 [label="<name> basics |<f0> numerics|{<f1> string | \"Hi\"} |{<f2> slice | {{<23index0> 0|<23value0> \"Hello\"}|{<23index1> 1|<23value1> \"Brave\"}|{<23index2> 2|<23value2> \"New\"}|{<23index3> 3|<23value3> \"World\"}}} |<f3> ptr|<f4> iface "];
1:f0 -> 2:name;
1:f3 -> 24:name;
1:f4 -> 25:name;
26 [label="<name> *memviz_test.basics"];
26:name -> 1:name;
}

9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,12 @@ How would you rather debug a data structure?
Simply pass in your data structure like so: ```memviz.Map(out, &data)``` and then pipe the output into graphviz.

For more complete examples see the tests in [memviz_test.go](https://github.com/bradleyjkemp/memviz/blob/master/memviz_test.go).

## Rendering with graphviz

Using the `memviz` output you can render the output directly as a png file (check out the implementation of at [DataViz](https://github.com/Arafatk/DataViz/blob/master/utils/utils.go))
```go
b := &bytes.Buffer{}
memviz.Map(foo, b)
dataviz_utils.WriteDotStringToPng(b.String(), "foo.png")
```
103 changes: 101 additions & 2 deletions basic.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,37 @@ package memviz

import (
"fmt"
"io"
"reflect"
"strconv"
)

func (m *mapper) mapPtrIface(iVal reflect.Value, inlineable bool) (nodeID, string) {
type nodeKey string
type nodeID int

var (
// for values that aren't addressable keep an incrementing counter instead
keyCounter int
)

const (
kTagName = "memviz"
nilKey nodeKey = "nil0"
)

type mapper struct {
writer io.Writer
nodeIDs map[nodeKey]nodeID
nodeSummaries map[nodeKey]string
config *Config
}

func (m *mapper) mapPtrIface(iVal reflect.Value, inlineable bool, depth uint32) (nodeID, string) {
pointee := iVal.Elem()
key := getNodeKey(iVal)

// inlineable=false so an invalid parentID is fine
pointeeNode, pointeeSummary := m.mapValue(pointee, 0, false)
pointeeNode, pointeeSummary := m.mapValue(pointee, 0, false, depth+1)
summary := escapeString(iVal.Type().String())
m.nodeSummaries[key] = summary

Expand Down Expand Up @@ -69,3 +90,81 @@ func (m *mapper) mapUint(numVal reflect.Value, inlineable bool) (nodeID, string)
m.nodeSummaries[getNodeKey(numVal)] = "uint"
return m.newBasicNode(numVal, printed), "uint"
}

func (m *mapper) getNodeID(iVal reflect.Value) nodeID {
// have to key on kind and address because a struct and its first element have the same UnsafeAddr()
key := getNodeKey(iVal)
var id nodeID
var ok bool
if id, ok = m.nodeIDs[key]; !ok {
id = nodeID(len(m.nodeIDs))
m.nodeIDs[key] = id
return id
}

return id
}

func (m *mapper) newBasicNode(iVal reflect.Value, text string) nodeID {
id := m.getNodeID(iVal)
fmt.Fprintf(m.writer, " %d [label=\"<name> %s\"];\n", id, text)
return id
}

func (m *mapper) mapValue(iVal reflect.Value, parentID nodeID, inlineable bool, depth uint32) (nodeID, string) {
if !iVal.IsValid() {
// zero value => probably result of nil pointer
return m.nodeIDs[nilKey], m.nodeSummaries[nilKey]
}

key := getNodeKey(iVal)
if summary, ok := m.nodeSummaries[key]; ok {
// already seen this address so no need to map again
return m.nodeIDs[key], summary
}
switch iVal.Kind() {
// Indirections
case reflect.Ptr, reflect.Interface:
return m.mapPtrIface(iVal, inlineable, depth)
// Collections
case reflect.Struct:
if m.config.maxDepth > 0 && depth > m.config.maxDepth {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can probably lift this repeated depth check to just run once at the start of this function. If we're taking depth to mean "number of graphviz nodes" then I think we can use:

if depth >= m.config.maxDepth {
    return 0, ""
}

i.e. if this value is going to cause another node to be created and we've already reached the limit then stop. This is perhaps a bit tricky because of the behaviour of inlineable though.
I'd have to remind myself what exactly this does but I think it's more of a hint than an instruction so accurately calculating depth is tricky.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pretty sure we have interpreted �������depth differently. 😆 When you say "number of graphviz nodes", do you mean a) in total? or b) the number of graphviz nodes down the embedded tree of the root object?

i.e. if this value is going to cause another node to be created and we've already reached the limit then stop. This is perhaps a bit tricky because of the behaviour of inlineable though.

I think this actually caught my attention during the first testing, and I worked around it. If it's inlineable it will still be rendered, maxdepth "only" has an influence on next level objects. Please go in and test this, I will too, I am unclear on the details 😉

return -1, "" // max depth reached
}
return m.mapStruct(iVal, depth)
case reflect.Slice, reflect.Array:
if m.config.maxDepth > 0 && depth > m.config.maxDepth {
return -1, "" // max depth reached
}
return m.mapSlice(iVal, parentID, inlineable, depth)
case reflect.Map:
if m.config.maxDepth > 0 && depth > m.config.maxDepth {
return -1, "" // max depth reached
}
return m.mapMap(iVal, parentID, inlineable, depth)

// Simple types
case reflect.Bool:
return m.mapBool(iVal, inlineable)
case reflect.String:
return m.mapString(iVal, inlineable)
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
return m.mapInt(iVal, inlineable)
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
return m.mapUint(iVal, inlineable)

// If we've missed anything then just fmt.Sprint it
default:
return m.newBasicNode(iVal, fmt.Sprint(iVal.Interface())), iVal.Kind().String()
}
}

func getNodeKey(val reflect.Value) nodeKey {
if val.CanAddr() {
return nodeKey(fmt.Sprint(val.Kind()) + fmt.Sprint(val.UnsafeAddr()))
}

// reverse order of type and "address" to prevent (incredibly unlikely) collisions
keyCounter++
return nodeKey(fmt.Sprint(keyCounter) + fmt.Sprint(val.Kind()))
}
Loading