Add Graph.DebugOperation
The method marks the start of a set of operations on the Graph, with extra information optionally provided in the second paramter. This returns a function with a single End method to mark the end of the set in the logs. Refactor the existing graph Begin/End Operation calls to use this single method. Remove the *string types in the marshal structs, these are strictly informational and don't need to differentiate empty vs unset strings. Add calls to DebugOperation for each step while building the graph.
This commit is contained in:
parent
7e66df3290
commit
de0cb17a39
|
@ -103,7 +103,7 @@ func (g *AcyclicGraph) TransitiveReduction() {
|
||||||
// v such that the edge (u,v) exists (v is a direct descendant of u).
|
// 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).
|
// For each v-prime reachable from v, remove the edge (u, v-prime).
|
||||||
defer g.debug.BeginReduction().End()
|
defer g.debug.BeginOperation("TransitiveReduction", "").End("")
|
||||||
|
|
||||||
for _, u := range g.Vertices() {
|
for _, u := range g.Vertices() {
|
||||||
uTargets := g.DownEdges(u)
|
uTargets := g.DownEdges(u)
|
||||||
|
@ -167,7 +167,7 @@ func (g *AcyclicGraph) Cycles() [][]Vertex {
|
||||||
// This will walk nodes in parallel if it can. Because the walk is done
|
// This will walk nodes in parallel if it can. Because the walk is done
|
||||||
// in parallel, the error returned will be a multierror.
|
// in parallel, the error returned will be a multierror.
|
||||||
func (g *AcyclicGraph) Walk(cb WalkFunc) error {
|
func (g *AcyclicGraph) Walk(cb WalkFunc) error {
|
||||||
defer g.debug.BeginWalk().End()
|
defer g.debug.BeginOperation("Walk", "").End("")
|
||||||
|
|
||||||
// Cache the vertices since we use it multiple times
|
// Cache the vertices since we use it multiple times
|
||||||
vertices := g.Vertices()
|
vertices := g.Vertices()
|
||||||
|
@ -278,7 +278,7 @@ type vertexAtDepth struct {
|
||||||
// the vertices in start. This is not exported now but it would make sense
|
// the vertices in start. This is not exported now but it would make sense
|
||||||
// to export this publicly at some point.
|
// to export this publicly at some point.
|
||||||
func (g *AcyclicGraph) DepthFirstWalk(start []Vertex, f DepthWalkFunc) error {
|
func (g *AcyclicGraph) DepthFirstWalk(start []Vertex, f DepthWalkFunc) error {
|
||||||
defer g.debug.BeginDepthFirstWalk().End()
|
defer g.debug.BeginOperation("DepthFirstWalk", "").End("")
|
||||||
|
|
||||||
seen := make(map[Vertex]struct{})
|
seen := make(map[Vertex]struct{})
|
||||||
frontier := make([]*vertexAtDepth, len(start))
|
frontier := make([]*vertexAtDepth, len(start))
|
||||||
|
@ -322,7 +322,7 @@ func (g *AcyclicGraph) DepthFirstWalk(start []Vertex, f DepthWalkFunc) error {
|
||||||
// reverseDepthFirstWalk does a depth-first walk _up_ the graph starting from
|
// reverseDepthFirstWalk does a depth-first walk _up_ the graph starting from
|
||||||
// the vertices in start.
|
// the vertices in start.
|
||||||
func (g *AcyclicGraph) ReverseDepthFirstWalk(start []Vertex, f DepthWalkFunc) error {
|
func (g *AcyclicGraph) ReverseDepthFirstWalk(start []Vertex, f DepthWalkFunc) error {
|
||||||
defer g.debug.BeginReverseDepthFirstWalk().End()
|
defer g.debug.BeginOperation("ReverseDepthFirstWalk", "").End("")
|
||||||
|
|
||||||
seen := make(map[Vertex]struct{})
|
seen := make(map[Vertex]struct{})
|
||||||
frontier := make([]*vertexAtDepth, len(start))
|
frontier := make([]*vertexAtDepth, len(start))
|
||||||
|
|
17
dag/graph.go
17
dag/graph.go
|
@ -139,7 +139,7 @@ func (g *Graph) Replace(original, replacement Vertex) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
defer g.debug.BeginReplace().End()
|
defer g.debug.BeginOperation("Replace", "").End("")
|
||||||
|
|
||||||
// If they're the same, then don't do anything
|
// If they're the same, then don't do anything
|
||||||
if original == replacement {
|
if original == replacement {
|
||||||
|
@ -357,6 +357,21 @@ func (g *Graph) DebugEdgeInfo(e Edge, info string) {
|
||||||
g.debug.Encode(ea)
|
g.debug.Encode(ea)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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.
|
// VertexName returns the name of a vertex.
|
||||||
func VertexName(raw Vertex) string {
|
func VertexName(raw Vertex) string {
|
||||||
switch v := raw.(type) {
|
switch v := raw.(type) {
|
||||||
|
|
106
dag/marshal.go
106
dag/marshal.go
|
@ -230,10 +230,13 @@ func marshalSubgrapher(v Vertex) (*Graph, bool) {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// ender provides a way to call any End* method expression via an End method
|
// The DebugOperationEnd func type provides a way to call an End function via a
|
||||||
type ender func()
|
// method call, allowing for the chaining of methods in a defer statement.
|
||||||
|
type DebugOperationEnd func(string)
|
||||||
|
|
||||||
func (e ender) End() { e() }
|
// 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
|
// encoder provides methods to write debug data to an io.Writer, and is a noop
|
||||||
// when no writer is present
|
// when no writer is present
|
||||||
|
@ -290,89 +293,26 @@ func (e *encoder) RemoveEdge(edge Edge) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// BeginReplace marks the start of a replace operation, and returns the encoder
|
// BeginOperation marks the start of set of graph transformations, and returns
|
||||||
// to chain the EndReplace call.
|
// an EndDebugOperation func to be called once the opration is complete.
|
||||||
func (e *encoder) BeginReplace() ender {
|
func (e *encoder) BeginOperation(op string, info string) DebugOperationEnd {
|
||||||
e.Encode(marshalOperation{
|
if e == nil {
|
||||||
Type: "Operation",
|
return func(string) {}
|
||||||
Begin: newString("Replace"),
|
}
|
||||||
})
|
|
||||||
return e.EndReplace
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *encoder) EndReplace() {
|
|
||||||
e.Encode(marshalOperation{
|
e.Encode(marshalOperation{
|
||||||
Type: "Operation",
|
Type: "Operation",
|
||||||
End: newString("Replace"),
|
Begin: op,
|
||||||
|
Info: info,
|
||||||
})
|
})
|
||||||
}
|
|
||||||
|
|
||||||
// BeginReduction marks the start of a replace operation, and returns the encoder
|
return func(info string) {
|
||||||
// to chain the EndReduction call.
|
|
||||||
func (e *encoder) BeginReduction() ender {
|
|
||||||
e.Encode(marshalOperation{
|
e.Encode(marshalOperation{
|
||||||
Type: "Operation",
|
Type: "Operation",
|
||||||
Begin: newString("Reduction"),
|
End: op,
|
||||||
})
|
Info: info,
|
||||||
return e.EndReduction
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *encoder) EndReduction() {
|
|
||||||
e.Encode(marshalOperation{
|
|
||||||
Type: "Operation",
|
|
||||||
End: newString("Reduction"),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// BeginDepthFirstWalk marks the start of a replace operation, and returns the
|
|
||||||
// encoder to chain the EndDepthFirstWalk call.
|
|
||||||
func (e *encoder) BeginDepthFirstWalk() ender {
|
|
||||||
e.Encode(marshalOperation{
|
|
||||||
Type: "Operation",
|
|
||||||
Begin: newString("DepthFirstWalk"),
|
|
||||||
})
|
|
||||||
return e.EndDepthFirstWalk
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *encoder) EndDepthFirstWalk() {
|
|
||||||
e.Encode(marshalOperation{
|
|
||||||
Type: "Operation",
|
|
||||||
End: newString("DepthFirstWalk"),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// BeginReverseDepthFirstWalk marks the start of a replace operation, and
|
|
||||||
// returns the encoder to chain the EndReverseDepthFirstWalk call.
|
|
||||||
func (e *encoder) BeginReverseDepthFirstWalk() ender {
|
|
||||||
e.Encode(marshalOperation{
|
|
||||||
Type: "Operation",
|
|
||||||
Begin: newString("ReverseDepthFirstWalk"),
|
|
||||||
})
|
|
||||||
return e.EndReverseDepthFirstWalk
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *encoder) EndReverseDepthFirstWalk() {
|
|
||||||
e.Encode(marshalOperation{
|
|
||||||
Type: "Operation",
|
|
||||||
End: newString("ReverseDepthFirstWalk"),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// BeginWalk marks the start of a replace operation, and returns the encoder
|
|
||||||
// to chain the EndWalk call.
|
|
||||||
func (e *encoder) BeginWalk() ender {
|
|
||||||
e.Encode(marshalOperation{
|
|
||||||
Type: "Operation",
|
|
||||||
Begin: newString("Walk"),
|
|
||||||
})
|
|
||||||
return e.EndWalk
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *encoder) EndWalk() {
|
|
||||||
e.Encode(marshalOperation{
|
|
||||||
Type: "Operation",
|
|
||||||
End: newString("Walk"),
|
|
||||||
})
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// structure for recording graph transformations
|
// structure for recording graph transformations
|
||||||
|
@ -424,15 +364,11 @@ func (s *streamDecode) UnmarshalJSON(d []byte) error {
|
||||||
// graph state.
|
// graph state.
|
||||||
type marshalOperation struct {
|
type marshalOperation struct {
|
||||||
Type string
|
Type string
|
||||||
Begin *string `json:",omitempty"`
|
Begin string `json:",omitempty"`
|
||||||
End *string `json:",omitempty"`
|
End string `json:",omitempty"`
|
||||||
Info *string `json:".omitempty"`
|
Info string `json:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func newBool(b bool) *bool { return &b }
|
|
||||||
|
|
||||||
func newString(s string) *string { return &s }
|
|
||||||
|
|
||||||
// decodeGraph decodes a marshalGraph from an encoded graph stream.
|
// decodeGraph decodes a marshalGraph from an encoded graph stream.
|
||||||
func decodeGraph(r io.Reader) (*marshalGraph, error) {
|
func decodeGraph(r io.Reader) (*marshalGraph, error) {
|
||||||
dec := json.NewDecoder(r)
|
dec := json.NewDecoder(r)
|
||||||
|
|
|
@ -49,8 +49,15 @@ func (b *BasicGraphBuilder) Build(path []string) (*Graph, error) {
|
||||||
stepName = stepName[dot+1:]
|
stepName = stepName[dot+1:]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debugOp := g.DebugOperation(stepName, "")
|
||||||
err := step.Transform(g)
|
err := step.Transform(g)
|
||||||
|
|
||||||
|
errMsg := ""
|
||||||
|
if err != nil {
|
||||||
|
errMsg = err.Error()
|
||||||
|
}
|
||||||
|
debugOp.End(errMsg)
|
||||||
|
|
||||||
// always log the graph state to see what transformations may have happened
|
// always log the graph state to see what transformations may have happened
|
||||||
debugName := "build-" + stepName
|
debugName := "build-" + stepName
|
||||||
if b.Name != "" {
|
if b.Name != "" {
|
||||||
|
|
Loading…
Reference in New Issue