From 0c1ab6142b5c0c3ce0f7cdc5e33d63e312a771e9 Mon Sep 17 00:00:00 2001 From: James Bardin Date: Tue, 7 Jan 2020 17:33:22 -0500 Subject: [PATCH] remove debug code Remove the abandoned graph debugger --- dag/dag.go | 8 -- dag/graph.go | 60 --------- dag/marshal.go | 242 ------------------------------------- dag/marshal_test.go | 289 -------------------------------------------- 4 files changed, 599 deletions(-) diff --git a/dag/dag.go b/dag/dag.go index c77e775fd..8ca4e910e 100644 --- a/dag/dag.go +++ b/dag/dag.go @@ -100,8 +100,6 @@ func (g *AcyclicGraph) TransitiveReduction() { // v such that the edge (u,v) exists (v is a direct descendant of u). // // For each v-prime reachable from v, remove the edge (u, v-prime). - defer g.debug.BeginOperation("TransitiveReduction", "").End("") - for _, u := range g.Vertices() { uTargets := g.DownEdges(u) @@ -163,8 +161,6 @@ func (g *AcyclicGraph) Cycles() [][]Vertex { // This will walk nodes in parallel if it can. The resulting diagnostics // contains problems from all graphs visited, in no particular order. func (g *AcyclicGraph) Walk(cb WalkFunc) tfdiags.Diagnostics { - defer g.debug.BeginOperation(typeWalk, "").End("") - w := &Walker{Callback: cb, Reverse: true} w.Update(g) return w.Wait() @@ -226,8 +222,6 @@ func (g *AcyclicGraph) DepthFirstWalk(start Set, f DepthWalkFunc) error { // SortedDepthFirstWalk does a depth-first walk of the graph starting from // the vertices in start, always iterating the nodes in a consistent order. func (g *AcyclicGraph) SortedDepthFirstWalk(start []Vertex, f DepthWalkFunc) error { - defer g.debug.BeginOperation(typeDepthFirstWalk, "").End("") - seen := make(map[Vertex]struct{}) frontier := make([]*vertexAtDepth, len(start)) for i, v := range start { @@ -310,8 +304,6 @@ func (g *AcyclicGraph) ReverseDepthFirstWalk(start Set, f DepthWalkFunc) error { // SortedReverseDepthFirstWalk does a depth-first walk _up_ the graph starting from // the vertices in start, always iterating the nodes in a consistent order. func (g *AcyclicGraph) SortedReverseDepthFirstWalk(start []Vertex, f DepthWalkFunc) error { - defer g.debug.BeginOperation(typeReverseDepthFirstWalk, "").End("") - seen := make(map[Vertex]struct{}) frontier := make([]*vertexAtDepth, len(start)) for i, v := range start { diff --git a/dag/graph.go b/dag/graph.go index 35ea44324..4ce0dbccb 100644 --- a/dag/graph.go +++ b/dag/graph.go @@ -2,9 +2,7 @@ package dag import ( "bytes" - "encoding/json" "fmt" - "io" "sort" ) @@ -14,9 +12,6 @@ type Graph struct { edges Set downEdges map[interface{}]Set upEdges map[interface{}]Set - - // JSON encoder for recording debug information - debug *encoder } // Subgrapher allows a Vertex to be a Graph itself, by returning a Grapher. @@ -106,7 +101,6 @@ func (g *Graph) HasEdge(e Edge) bool { func (g *Graph) Add(v Vertex) Vertex { g.init() g.vertices.Add(v) - g.debug.Add(v) return v } @@ -115,7 +109,6 @@ func (g *Graph) Add(v Vertex) Vertex { func (g *Graph) Remove(v Vertex) Vertex { // Delete the vertex itself g.vertices.Delete(v) - g.debug.Remove(v) // Delete the edges to non-existent things for _, target := range g.DownEdges(v) { @@ -137,8 +130,6 @@ func (g *Graph) Replace(original, replacement Vertex) bool { return false } - defer g.debug.BeginOperation("Replace", "").End("") - // If they're the same, then don't do anything if original == replacement { return true @@ -162,7 +153,6 @@ func (g *Graph) Replace(original, replacement Vertex) bool { // RemoveEdge removes an edge from the graph. func (g *Graph) RemoveEdge(edge Edge) { g.init() - g.debug.RemoveEdge(edge) // Delete the edge from the set g.edges.Delete(edge) @@ -194,7 +184,6 @@ func (g *Graph) UpEdges(v Vertex) Set { // value of the edge itself. func (g *Graph) Connect(edge Edge) { g.init() - g.debug.Connect(edge) source := edge.Source() target := edge.Target() @@ -327,55 +316,6 @@ func (g *Graph) Dot(opts *DotOpts) []byte { return newMarshalGraph("", g).Dot(opts) } -// MarshalJSON returns a JSON representation of the entire Graph. -func (g *Graph) MarshalJSON() ([]byte, error) { - dg := newMarshalGraph("root", g) - return json.MarshalIndent(dg, "", " ") -} - -// SetDebugWriter sets the io.Writer where the Graph will record debug -// information. After this is set, the graph will immediately encode itself to -// the stream, and continue to record all subsequent operations. -func (g *Graph) SetDebugWriter(w io.Writer) { - g.debug = &encoder{w: w} - g.debug.Encode(newMarshalGraph("root", g)) -} - -// DebugVertexInfo encodes arbitrary information about a vertex in the graph -// debug logs. -func (g *Graph) DebugVertexInfo(v Vertex, info string) { - va := newVertexInfo(typeVertexInfo, v, info) - g.debug.Encode(va) -} - -// DebugEdgeInfo encodes arbitrary information about an edge in the graph debug -// logs. -func (g *Graph) DebugEdgeInfo(e Edge, info string) { - ea := newEdgeInfo(typeEdgeInfo, e, info) - g.debug.Encode(ea) -} - -// DebugVisitInfo records a visit to a Vertex during a walk operation. -func (g *Graph) DebugVisitInfo(v Vertex, info string) { - vi := newVertexInfo(typeVisitInfo, v, info) - g.debug.Encode(vi) -} - -// DebugOperation marks the start of a set of graph transformations in -// the debug log, and returns a DebugOperationEnd func, which marks the end of -// the operation in the log. Additional information can be added to the log via -// the info parameter. -// -// The returned func's End method allows this method to be called from a single -// defer statement: -// defer g.DebugOperationBegin("OpName", "operating").End("") -// -// The returned function must be called to properly close the logical operation -// in the logs. -func (g *Graph) DebugOperation(operation string, info string) DebugOperationEnd { - return g.debug.BeginOperation(operation, info) -} - // VertexName returns the name of a vertex. func VertexName(raw Vertex) string { switch v := raw.(type) { diff --git a/dag/marshal.go b/dag/marshal.go index c567d2719..ebb8a0a63 100644 --- a/dag/marshal.go +++ b/dag/marshal.go @@ -1,14 +1,10 @@ package dag import ( - "encoding/json" "fmt" - "io" - "log" "reflect" "sort" "strconv" - "sync" ) const ( @@ -234,241 +230,3 @@ func marshalSubgrapher(v Vertex) (*Graph, bool) { return nil, false } - -// The DebugOperationEnd func type provides a way to call an End function via a -// method call, allowing for the chaining of methods in a defer statement. -type DebugOperationEnd func(string) - -// End calls function e with the info parameter, marking the end of this -// operation in the logs. -func (e DebugOperationEnd) End(info string) { e(info) } - -// encoder provides methods to write debug data to an io.Writer, and is a noop -// when no writer is present -type encoder struct { - sync.Mutex - w io.Writer -} - -// Encode is analogous to json.Encoder.Encode -func (e *encoder) Encode(i interface{}) { - if e == nil || e.w == nil { - return - } - e.Lock() - defer e.Unlock() - - js, err := json.Marshal(i) - if err != nil { - log.Println("[ERROR] dag:", err) - return - } - js = append(js, '\n') - - _, err = e.w.Write(js) - if err != nil { - log.Println("[ERROR] dag:", err) - return - } -} - -func (e *encoder) Add(v Vertex) { - if e == nil { - return - } - e.Encode(marshalTransform{ - Type: typeTransform, - AddVertex: newMarshalVertex(v), - }) -} - -// Remove records the removal of Vertex v. -func (e *encoder) Remove(v Vertex) { - if e == nil { - return - } - e.Encode(marshalTransform{ - Type: typeTransform, - RemoveVertex: newMarshalVertex(v), - }) -} - -func (e *encoder) Connect(edge Edge) { - if e == nil { - return - } - e.Encode(marshalTransform{ - Type: typeTransform, - AddEdge: newMarshalEdge(edge), - }) -} - -func (e *encoder) RemoveEdge(edge Edge) { - if e == nil { - return - } - e.Encode(marshalTransform{ - Type: typeTransform, - RemoveEdge: newMarshalEdge(edge), - }) -} - -// BeginOperation marks the start of set of graph transformations, and returns -// an EndDebugOperation func to be called once the opration is complete. -func (e *encoder) BeginOperation(op string, info string) DebugOperationEnd { - if e == nil { - return func(string) {} - } - - e.Encode(marshalOperation{ - Type: typeOperation, - Begin: op, - Info: info, - }) - - return func(info string) { - e.Encode(marshalOperation{ - Type: typeOperation, - End: op, - Info: info, - }) - } -} - -// structure for recording graph transformations -type marshalTransform struct { - // Type: "Transform" - Type string - AddEdge *marshalEdge `json:",omitempty"` - RemoveEdge *marshalEdge `json:",omitempty"` - AddVertex *marshalVertex `json:",omitempty"` - RemoveVertex *marshalVertex `json:",omitempty"` -} - -func (t marshalTransform) Transform(g *marshalGraph) { - switch { - case t.AddEdge != nil: - g.connect(t.AddEdge) - case t.RemoveEdge != nil: - g.removeEdge(t.RemoveEdge) - case t.AddVertex != nil: - g.add(t.AddVertex) - case t.RemoveVertex != nil: - g.remove(t.RemoveVertex) - } -} - -// this structure allows us to decode any object in the json stream for -// inspection, then re-decode it into a proper struct if needed. -type streamDecode struct { - Type string - Map map[string]interface{} - JSON []byte -} - -func (s *streamDecode) UnmarshalJSON(d []byte) error { - s.JSON = d - err := json.Unmarshal(d, &s.Map) - if err != nil { - return err - } - - if t, ok := s.Map["Type"]; ok { - s.Type, _ = t.(string) - } - return nil -} - -// structure for recording the beginning and end of any multi-step -// transformations. These are informational, and not required to reproduce the -// graph state. -type marshalOperation struct { - Type string - Begin string `json:",omitempty"` - End string `json:",omitempty"` - Info string `json:",omitempty"` -} - -// decodeGraph decodes a marshalGraph from an encoded graph stream. -func decodeGraph(r io.Reader) (*marshalGraph, error) { - dec := json.NewDecoder(r) - - // a stream should always start with a graph - g := &marshalGraph{} - - err := dec.Decode(g) - if err != nil { - return nil, err - } - - // now replay any operations that occurred on the original graph - for dec.More() { - s := &streamDecode{} - err := dec.Decode(s) - if err != nil { - return g, err - } - - // the only Type we're concerned with here is Transform to complete the - // Graph - if s.Type != typeTransform { - continue - } - - t := &marshalTransform{} - err = json.Unmarshal(s.JSON, t) - if err != nil { - return g, err - } - t.Transform(g) - } - return g, nil -} - -// marshalVertexInfo allows encoding arbitrary information about the a single -// Vertex in the logs. These are accumulated for informational display while -// rebuilding the graph. -type marshalVertexInfo struct { - Type string - Vertex *marshalVertex - Info string -} - -func newVertexInfo(infoType string, v Vertex, info string) *marshalVertexInfo { - return &marshalVertexInfo{ - Type: infoType, - Vertex: newMarshalVertex(v), - Info: info, - } -} - -// marshalEdgeInfo allows encoding arbitrary information about the a single -// Edge in the logs. These are accumulated for informational display while -// rebuilding the graph. -type marshalEdgeInfo struct { - Type string - Edge *marshalEdge - Info string -} - -func newEdgeInfo(infoType string, e Edge, info string) *marshalEdgeInfo { - return &marshalEdgeInfo{ - Type: infoType, - Edge: newMarshalEdge(e), - Info: info, - } -} - -// JSON2Dot reads a Graph debug log from and io.Reader, and converts the final -// graph dot format. -// -// TODO: Allow returning the output at a certain point during decode. -// Encode extra information from the json log into the Dot. -func JSON2Dot(r io.Reader) ([]byte, error) { - g, err := decodeGraph(r) - if err != nil { - return nil, err - } - - return g.Dot(nil), nil -} diff --git a/dag/marshal_test.go b/dag/marshal_test.go index e5839c887..a7e468dc1 100644 --- a/dag/marshal_test.go +++ b/dag/marshal_test.go @@ -1,12 +1,8 @@ package dag import ( - "bytes" - "encoding/json" "strings" "testing" - - "github.com/hashicorp/terraform/tfdiags" ) func TestGraphDot_empty(t *testing.T) { @@ -80,288 +76,3 @@ const testGraphDotAttrsStr = `digraph { "[root] foo" [foo = "bar"] } }` - -func TestGraphJSON_empty(t *testing.T) { - var g Graph - g.Add(1) - g.Add(2) - g.Add(3) - - js, err := g.MarshalJSON() - if err != nil { - t.Fatal(err) - } - - actual := strings.TrimSpace(string(js)) - expected := strings.TrimSpace(testGraphJSONEmptyStr) - if actual != expected { - t.Fatalf("bad: %s", actual) - } -} - -func TestGraphJSON_basic(t *testing.T) { - var g Graph - g.Add(1) - g.Add(2) - g.Add(3) - g.Connect(BasicEdge(1, 3)) - - js, err := g.MarshalJSON() - if err != nil { - t.Fatal(err) - } - actual := strings.TrimSpace(string(js)) - expected := strings.TrimSpace(testGraphJSONBasicStr) - if actual != expected { - t.Fatalf("bad: %s", actual) - } -} - -// Verify that Vertex and Edge annotations appear in the debug output -func TestGraphJSON_debugInfo(t *testing.T) { - var g Graph - var buf bytes.Buffer - g.SetDebugWriter(&buf) - - g.Add(1) - g.Add(2) - g.Add(3) - g.Connect(BasicEdge(1, 2)) - - g.DebugVertexInfo(2, "2") - g.DebugVertexInfo(3, "3") - g.DebugEdgeInfo(BasicEdge(1, 2), "1|2") - - dec := json.NewDecoder(bytes.NewReader(buf.Bytes())) - - var found2, found3, foundEdge bool - for dec.More() { - var d streamDecode - - err := dec.Decode(&d) - if err != nil { - t.Fatal(err) - } - - switch d.Type { - case typeVertexInfo: - va := &marshalVertexInfo{} - err := json.Unmarshal(d.JSON, va) - if err != nil { - t.Fatal(err) - } - - switch va.Info { - case "2": - if va.Vertex.Name != "2" { - t.Fatalf("wrong vertex annotated 2: %#v", va) - } - found2 = true - case "3": - if va.Vertex.Name != "3" { - t.Fatalf("wrong vertex annotated 3: %#v", va) - } - found3 = true - default: - t.Fatalf("unexpected annotation: %#v", va) - } - case typeEdgeInfo: - ea := &marshalEdgeInfo{} - err := json.Unmarshal(d.JSON, ea) - if err != nil { - t.Fatal(err) - } - - switch ea.Info { - case "1|2": - if ea.Edge.Name != "1|2" { - t.Fatalf("incorrect edge annotation: %#v\n", ea) - } - foundEdge = true - default: - t.Fatalf("unexpected edge Info: %#v", ea) - } - } - } - - if !found2 { - t.Fatal("annotation 2 not found") - } - if !found3 { - t.Fatal("annotation 3 not found") - } - if !foundEdge { - t.Fatal("edge annotation not found") - } -} - -// Verify that debug operations appear in the debug output -func TestGraphJSON_debugOperations(t *testing.T) { - var g Graph - var buf bytes.Buffer - g.SetDebugWriter(&buf) - - debugOp := g.DebugOperation("AddOne", "adding node 1") - g.Add(1) - debugOp.End("done adding node 1") - - // use an immediate closure to test defers - func() { - defer g.DebugOperation("AddTwo", "adding nodes 2 and 3").End("done adding 2 and 3") - g.Add(2) - defer g.DebugOperation("NestedAddThree", "second defer").End("done adding node 3") - g.Add(3) - }() - - g.Connect(BasicEdge(1, 2)) - - dec := json.NewDecoder(bytes.NewReader(buf.Bytes())) - - var ops []string - for dec.More() { - var d streamDecode - - err := dec.Decode(&d) - if err != nil { - t.Fatal(err) - } - - if d.Type != typeOperation { - continue - } - - o := &marshalOperation{} - err = json.Unmarshal(d.JSON, o) - if err != nil { - t.Fatal(err) - } - - switch { - case o.Begin == "AddOne": - ops = append(ops, "BeginAddOne") - case o.End == "AddOne": - ops = append(ops, "EndAddOne") - case o.Begin == "AddTwo": - ops = append(ops, "BeginAddTwo") - case o.End == "AddTwo": - ops = append(ops, "EndAddTwo") - case o.Begin == "NestedAddThree": - ops = append(ops, "BeginAddThree") - case o.End == "NestedAddThree": - ops = append(ops, "EndAddThree") - } - } - - expectedOps := []string{ - "BeginAddOne", - "EndAddOne", - "BeginAddTwo", - "BeginAddThree", - "EndAddThree", - "EndAddTwo", - } - - if strings.Join(ops, ",") != strings.Join(expectedOps, ",") { - t.Fatalf("incorrect order of operations: %v", ops) - } -} - -// Verify that we can replay visiting each vertex in order -func TestGraphJSON_debugVisits(t *testing.T) { - var g Graph - var buf bytes.Buffer - g.SetDebugWriter(&buf) - - g.Add(1) - g.Add(2) - g.Add(3) - g.Add(4) - - g.Connect(BasicEdge(2, 1)) - g.Connect(BasicEdge(4, 2)) - g.Connect(BasicEdge(3, 4)) - - err := (&AcyclicGraph{g}).Walk(func(v Vertex) tfdiags.Diagnostics { - g.DebugVisitInfo(v, "basic walk") - return nil - }) - - if err != nil { - t.Fatal(err) - } - - var visited []string - - dec := json.NewDecoder(bytes.NewReader(buf.Bytes())) - for dec.More() { - var d streamDecode - - err := dec.Decode(&d) - if err != nil { - t.Fatal(err) - } - - if d.Type != typeVisitInfo { - continue - } - - o := &marshalVertexInfo{} - err = json.Unmarshal(d.JSON, o) - if err != nil { - t.Fatal(err) - } - - visited = append(visited, o.Vertex.ID) - } - - expected := []string{"1", "2", "4", "3"} - - if strings.Join(visited, "-") != strings.Join(expected, "-") { - t.Fatalf("incorrect order of operations: %v", visited) - } -} - -const testGraphJSONEmptyStr = `{ - "Type": "Graph", - "Name": "root", - "Vertices": [ - { - "ID": "1", - "Name": "1" - }, - { - "ID": "2", - "Name": "2" - }, - { - "ID": "3", - "Name": "3" - } - ] -}` - -const testGraphJSONBasicStr = `{ - "Type": "Graph", - "Name": "root", - "Vertices": [ - { - "ID": "1", - "Name": "1" - }, - { - "ID": "2", - "Name": "2" - }, - { - "ID": "3", - "Name": "3" - } - ], - "Edges": [ - { - "Name": "1|3", - "Source": "1", - "Target": "3" - } - ] -}`