Beispiel #1
0
// ParseSubGraph parses the provided DOT file into a subgraph with a dedicated
// entry and exit node. The entry and exit nodes are identified using the node
// "label" attribute, e.g.
//
//    digraph if {
//       A->B [label="true"]
//       A->C [label="false"]
//       B->C
//       A [label="entry"]
//       B
//       C [label="exit"]
//    }
func ParseSubGraph(path string) (*SubGraph, error) {
	graph, err := dot.ParseFile(path)
	if err != nil {
		return nil, err
	}
	return NewSubGraph(graph)
}
Beispiel #2
0
func main() {
	g, err := dot.ParseFile("dotu.dot")
	if err != nil {
		panic(err)
	}
	s := g.String()
	fmt.Println(s)
}
Beispiel #3
0
// locateIntervals locates all intervals in the provided graph.
func locateIntervals(dotPath string) ([]*graphs.Interval, error) {
	// Parse graph.
	g, err := dot.ParseFile(dotPath)
	if err != nil {
		return nil, errutil.Err(err)
	}

	// Locate intervals
	return graphs.GetIntervals(g), nil
}
Beispiel #4
0
// locate parses the provided graphs and tries to locate isomorphisms of the
// subgraph in the graph.
func locate(graphPath, subPath string) error {
	// Parse graph.
	graph, err := dot.ParseFile(graphPath)
	if err != nil {
		return errutil.Err(err)
	}

	// Search for subgraph in GOPATH if not found.
	if ok, _ := osutil.Exists(subPath); !ok {
		dir, err := goutil.SrcDir("decomp.org/decomp/graphs/testdata/primitives")
		if err != nil {
			return errutil.Err(err)
		}
		subPath = filepath.Join(dir, subPath)
	}
	sub, err := graphs.ParseSubGraph(subPath)
	if err != nil {
		return errutil.Err(err)
	}

	// Locate isomorphisms.
	found := false
	if len(flagStart) > 0 {
		// Locate an isomorphism of sub in graph which starts at the node
		// specified by the "-start" flag.
		m, ok := iso.Isomorphism(graph, flagStart, sub)
		if ok {
			found = true
			printMapping(graph, sub, m)
		}
	} else {
		// Locate all isomorphisms of sub in graph.
		var names []string
		for name := range graph.Nodes.Lookup {
			names = append(names, name)
		}
		sort.Strings(names)
		for _, name := range names {
			m, ok := iso.Isomorphism(graph, name, sub)
			if !ok {
				continue
			}
			found = true
			printMapping(graph, sub, m)
		}
	}
	if !found {
		fmt.Println("not found.")
	}

	return nil
}
Beispiel #5
0
// restructure attempts to recover the control flow primitives of a given
// control flow graph. It does so by repeatedly locating and merging structured
// subgraphs (graph representations of control flow primitives) into single
// nodes until the entire graph is reduced into a single node or no structured
// subgraphs may be located. The list of primitives is ordered in the same
// sequence as they were located.
func restructure(dotPath string, subs []*graphs.SubGraph) (prims []*primitive.Primitive, err error) {
	// Parse the unstructured CFG.
	var graph *dot.Graph
	switch dotPath {
	case "-":
		// Read from stdin.
		buf, err := ioutil.ReadAll(os.Stdin)
		if err != nil {
			return nil, errutil.Err(err)
		}
		graph, err = dot.Read(buf)
		if err != nil {
			return nil, errutil.Err(err)
		}
	default:
		// Read from FILE.
		graph, err = dot.ParseFile(dotPath)
		if err != nil {
			return nil, errutil.Err(err)
		}
	}
	if len(graph.Nodes.Nodes) == 0 {
		return nil, errutil.Newf("unable to restructure empty graph %q", dotPath)
	}

	// Locate control flow primitives.
	for step := 1; len(graph.Nodes.Nodes) > 1; step++ {
		prim, err := findPrim(graph, subs, step)
		if err != nil {
			return nil, errutil.Err(err)
		}
		prims = append(prims, prim)
	}

	return prims, nil
}
Beispiel #6
0
// parseCFG parses the control flow graph of the function.
//
// For a source file "foo.ll" containing the functions "bar" and "baz" the
// following DOT files will be created:
//
//    foo_graphs/bar.dot
//    foo_graphs/baz.dot
func parseCFG(basePath, funcName string) (graph *dot.Graph, err error) {
	dotDir := basePath + "_graphs"
	dotName := funcName + ".dot"
	dotPath := fmt.Sprintf("%s/%s", dotDir, dotName)
	return dot.ParseFile(dotPath)
}
Beispiel #7
0
func TestEquationIsValid(t *testing.T) {
	golden := []struct {
		subPath   string
		graphPath string
		eq        *equation
		want      bool
	}{
		// i=0
		{
			subPath:   "../testdata/primitives/if.dot",
			graphPath: "../testdata/c4_graphs/stmt.dot",
			eq: &equation{
				m: map[string]string{
					"cond": "71",
					"body": "74",
					"exit": "75",
				},
			},
			want: true,
		},
		// i=1
		{
			subPath:   "../testdata/primitives/if.dot",
			graphPath: "../testdata/c4_graphs/stmt.dot",
			eq: &equation{
				m: map[string]string{
					"cond": "17",
					"body": "24",
					"exit": "32",
				},
			},
			want: true,
		},
		// i=2
		{
			subPath:   "../testdata/primitives/if.dot",
			graphPath: "../testdata/c4_graphs/stmt.dot",
			eq: &equation{
				m: map[string]string{
					"cond": "89",
					"body": "92",
					"exit": "93",
				},
			},
			want: false,
		},
		// i=3
		{
			subPath:   "../testdata/primitives/if.dot",
			graphPath: "../testdata/c4_graphs/stmt.dot",
			eq: &equation{
				m: map[string]string{
					"cond": "94",
					"body": "97",
					"exit": "98",
				},
			},
			want: false,
		},
		// i=4
		{
			subPath:   "../testdata/primitives/if_else.dot",
			graphPath: "../testdata/c4_graphs/expr.dot",
			eq: &equation{
				m: map[string]string{
					"cond":       "282",
					"body_true":  "292",
					"body_false": "287",
					"exit":       "299",
				},
			},
			want: true,
		},
		// i=5
		{
			subPath:   "../testdata/primitives/if_else.dot",
			graphPath: "../testdata/c4_graphs/expr.dot",
			eq: &equation{
				m: map[string]string{
					"cond":       "282",
					"body_true":  "287",
					"body_false": "292",
					"exit":       "299",
				},
			},
			want: true,
		},
		// i=6
		{
			subPath:   "../testdata/primitives/if_else.dot",
			graphPath: "../testdata/c4_graphs/next.dot",
			eq: &equation{
				m: map[string]string{
					"cond":       "438",
					"body_true":  "446",
					"body_false": "443",
					"exit":       "447",
				},
			},
			want: true,
		},
		// i=7
		{
			subPath:   "../testdata/primitives/if_else.dot",
			graphPath: "../testdata/c4_graphs/next.dot",
			eq: &equation{
				m: map[string]string{
					"cond":       "438",
					"body_true":  "443",
					"body_false": "446",
					"exit":       "447",
				},
			},
			want: true,
		},
		// i=8
		{
			subPath:   "../testdata/primitives/if_else.dot",
			graphPath: "../testdata/c4_graphs/next.dot",
			eq: &equation{
				m: map[string]string{
					"cond":       "487",
					"body_true":  "492",
					"body_false": "495",
					"exit":       "496",
				},
			},
			want: true,
		},
		// i=9
		{
			subPath:   "../testdata/primitives/if_else.dot",
			graphPath: "../testdata/c4_graphs/next.dot",
			eq: &equation{
				m: map[string]string{
					"cond":       "487",
					"body_true":  "495",
					"body_false": "492",
					"exit":       "496",
				},
			},
			want: true,
		},
		// i=10
		{
			subPath:   "../testdata/primitives/if_else.dot",
			graphPath: "../testdata/c4_graphs/next.dot",
			eq: &equation{
				m: map[string]string{
					"cond":       "124",
					"body_true":  "134",
					"body_false": "126",
					"exit":       "145",
				},
			},
			want: false,
		},
		// i=11
		{
			subPath:   "../testdata/primitives/list.dot",
			graphPath: "../testdata/c4_graphs/main.dot",
			eq: &equation{
				m: map[string]string{
					"entry": "740",
					"exit":  "760",
				},
			},
			want: true,
		},
		// i=12
		{
			subPath:   "../testdata/primitives/list.dot",
			graphPath: "../testdata/c4_graphs/main.dot",
			eq: &equation{
				m: map[string]string{
					"entry": "761",
					"exit":  "762",
				},
			},
			want: false,
		},
		// i=13
		{
			subPath:   "../testdata/primitives/pre_loop.dot",
			graphPath: "../testdata/c4_graphs/expr.dot",
			eq: &equation{
				m: map[string]string{
					"cond": "191",
					"body": "194",
					"exit": "196",
				},
			},
			want: true,
		},
		// i=14
		{
			subPath:   "../testdata/primitives/pre_loop.dot",
			graphPath: "../testdata/c4_graphs/expr.dot",
			eq: &equation{
				m: map[string]string{
					"cond": "370",
					"body": "378",
					"exit": "374",
				},
			},
			want: false,
		},
		// i=15
		{
			subPath:   "../testdata/primitives/pre_loop.dot",
			graphPath: "../testdata/c4_graphs/expr.dot",
			eq: &equation{
				m: map[string]string{
					"cond": "526",
					"body": "530",
					"exit": "539",
				},
			},
			want: false,
		},
		// i=16
		{
			subPath:   "../testdata/primitives/if_else.dot",
			graphPath: "../testdata/c4_graphs/expr.dot",
			eq: &equation{
				m: map[string]string{
					"cond":       "611",
					"body_true":  "615",
					"body_false": "615",
					"exit":       "631",
				},
			},
			want: false,
		},
		// i=17
		{
			subPath:   "../testdata/primitives/if_else.dot",
			graphPath: "../testdata/c4_graphs/expr.dot",
			eq: &equation{
				m: map[string]string{
					"cond":      "611",
					"body_true": "615",
					"exit":      "631",
				},
			},
			want: false,
		},
		// i=18
		{
			subPath:   "../testdata/primitives/if.dot",
			graphPath: "../testdata/c4_graphs/main.dot",
			eq: &equation{
				m: map[string]string{
					"cond": "20",
					"body": "25",
					"exit": "34",
				},
			},
			want: false,
		},
		// i=19
		{
			subPath:   "../testdata/primitives/pre_loop.dot",
			graphPath: "../testdata/c4_graphs/stmt.dot",
			eq: &equation{
				m: map[string]string{
					"cond": "39", // 48
					"body": "44",
					"exit": "52", // 45
				},
			},
			want: false,
		},
		// i=20
		{
			subPath:   "../testdata/primitives/pre_loop.dot",
			graphPath: "../testdata/c4_graphs/stmt.dot",
			eq: &equation{
				m: map[string]string{
					"cond": "39",
					"body": "44",
					"exit": "45",
				},
			},
			want: false,
		},
	}

	for i, g := range golden {
		sub, err := graphs.ParseSubGraph(g.subPath)
		if err != nil {
			t.Errorf("i=%d: %v", i, err)
			continue
		}
		graph, err := dot.ParseFile(g.graphPath)
		if err != nil {
			t.Errorf("i=%d: %v", i, err)
			continue
		}
		got := g.eq.isValid(graph, sub)
		if got != g.want {
			t.Errorf("i=%d: ok mismatch; expected %v, got %v", i, g.want, got)
			continue
		}
	}
}
Beispiel #8
0
func TestSolveBrute(t *testing.T) {
	golden := []struct {
		subPath   string
		graphPath string
		entry     string
		wants     []map[string]string
		err       string
	}{
		// i=0
		{
			subPath:   "../testdata/primitives/if_else.dot",
			graphPath: "../testdata/primitives/if_else.dot",
			entry:     "cond",
			wants: []map[string]string{
				{
					"cond":       "cond",
					"body_true":  "body_true",
					"body_false": "body_false",
					"exit":       "exit",
				},
				{
					"cond":       "cond",
					"body_true":  "body_false",
					"body_false": "body_true",
					"exit":       "exit",
				},
			},
			err: "",
		},
		// i=1
		{
			subPath:   "../testdata/primitives/if_else.dot",
			graphPath: "../testdata/c4_graphs/stmt.dot",
			entry:     "85",
			wants:     []map[string]string{nil},
			err:       "unable to locate node pair mapping",
		},
		// i=2
		{
			subPath:   "../testdata/primitives/pre_loop.dot",
			graphPath: "../testdata/c4_graphs/stmt.dot",
			entry:     "71",
			wants:     []map[string]string{nil},
			err:       "unable to locate node pair mapping",
		},
		// i=3
		{
			subPath:   "../testdata/primitives/pre_loop.dot",
			graphPath: "../testdata/c4_graphs/stmt.dot",
			entry:     "89",
			wants: []map[string]string{
				{
					"cond": "89",
					"body": "92",
					"exit": "93",
				},
			},
		},
		// i=4
		{
			subPath:   "../testdata/primitives/if.dot",
			graphPath: "../testdata/c4_graphs/stmt.dot",
			entry:     "71",
			wants: []map[string]string{
				{
					"cond": "71",
					"body": "74",
					"exit": "75",
				},
			},
		},
	}

loop:
	for i, g := range golden {
		graph, err := dot.ParseFile(g.graphPath)
		if err != nil {
			t.Errorf("i=%d: %v", i, err)
			continue
		}
		sub, err := graphs.ParseSubGraph(g.subPath)
		if err != nil {
			t.Errorf("i=%d: %v", i, err)
			continue
		}
		eq, err := candidates(graph, g.entry, sub)
		if err != nil {
			t.Errorf("i=%d: %v", i, err)
			continue
		}
		m, err := eq.solveBrute(graph, sub)
		if !sameError(err, g.err) {
			t.Errorf("i=%d: error mismatch; expected %v, got %v", i, g.err, err)
			continue
		} else if err != nil {
			// Expected error, check next test case.
			continue
		}
		for _, want := range g.wants {
			if reflect.DeepEqual(m, want) {
				continue loop
			}
		}
		t.Errorf("i=%d: node pair map mismatch; expected one of %v, got %v", i, g.wants, m)
	}
}
Beispiel #9
0
func TestSearch(t *testing.T) {
	golden := []struct {
		subPath   string
		graphPath string
		m         map[string]string
		ok        bool
	}{
		// i=0
		{
			subPath:   "../testdata/primitives/if.dot",
			graphPath: "../testdata/c4_graphs/stmt.dot",
			m: map[string]string{
				"cond": "17",
				"body": "24",
				"exit": "32",
			},
			ok: true,
		},
		// i=1
		{
			subPath:   "../testdata/primitives/if_else.dot",
			graphPath: "../testdata/c4_graphs/stmt.dot",
			m:         nil,
			ok:        false,
		},
		// i=2
		{
			subPath:   "../testdata/primitives/list.dot",
			graphPath: "../testdata/c4_graphs/stmt.dot",
			m: map[string]string{
				"entry": "101",
				"exit":  "105",
			},
			ok: true,
		},
		// i=3
		{
			subPath:   "../testdata/primitives/pre_loop.dot",
			graphPath: "../testdata/c4_graphs/stmt.dot",
			m: map[string]string{
				"cond": "89",
				"body": "92",
				"exit": "93",
			},
			ok: true,
		},

		// i=4
		{
			subPath:   "../testdata/primitives/if.dot",
			graphPath: "../testdata/c4_graphs/expr.dot",
			m: map[string]string{
				"cond": "120",
				"body": "122",
				"exit": "127",
			},
			ok: true,
		},
		// i=5
		{
			subPath:   "../testdata/primitives/if_else.dot",
			graphPath: "../testdata/c4_graphs/expr.dot",
			m: map[string]string{
				"cond":       "282",
				"body_true":  "292",
				"body_false": "287",
				"exit":       "299",
			},
			ok: true,
		},
		// i=6
		{
			subPath:   "../testdata/primitives/list.dot",
			graphPath: "../testdata/c4_graphs/expr.dot",
			m: map[string]string{
				"entry": "109",
				"exit":  "119",
			},
			ok: true,
		},
		// i=7
		{
			subPath:   "../testdata/primitives/pre_loop.dot",
			graphPath: "../testdata/c4_graphs/expr.dot",
			m: map[string]string{
				"cond": "191",
				"body": "194",
				"exit": "196",
			},
			ok: true,
		},

		// i=8
		{
			subPath:   "../testdata/primitives/if.dot",
			graphPath: "../testdata/c4_graphs/next.dot",
			m: map[string]string{
				"cond": "195",
				"body": "200",
				"exit": "205",
			},
			ok: true,
		},
		// i=9
		{
			subPath:   "../testdata/primitives/if_else.dot",
			graphPath: "../testdata/c4_graphs/next.dot",
			m: map[string]string{
				"cond":       "28",
				"body_true":  "44",
				"body_false": "39",
				"exit":       "46",
			},
			ok: true,
		},
		// i=10
		{
			subPath:   "../testdata/primitives/list.dot",
			graphPath: "../testdata/c4_graphs/next.dot",
			m: map[string]string{
				"entry": "320",
				"exit":  "322",
			},
			ok: true,
		},
		// i=11
		{
			subPath:   "../testdata/primitives/pre_loop.dot",
			graphPath: "../testdata/c4_graphs/next.dot",
			m:         nil,
			ok:        false,
		},

		// i=12
		{
			subPath:   "../testdata/primitives/if.dot",
			graphPath: "../testdata/c4_graphs/main.dot",
			m: map[string]string{
				"cond": "135",
				"body": "138",
				"exit": "139",
			},
			ok: true,
		},
		// i=13
		{
			subPath:   "../testdata/primitives/if_else.dot",
			graphPath: "../testdata/c4_graphs/main.dot",
			m: map[string]string{
				"cond":       "442",
				"body_true":  "451",
				"body_false": "448",
				"exit":       "453",
			},
			ok: true,
		},
		// i=14
		{
			subPath:   "../testdata/primitives/list.dot",
			graphPath: "../testdata/c4_graphs/main.dot",
			m: map[string]string{
				"entry": "740",
				"exit":  "760",
			},
			ok: true,
		},
		// i=15
		{
			subPath:   "../testdata/primitives/pre_loop.dot",
			graphPath: "../testdata/c4_graphs/main.dot",
			m: map[string]string{
				"cond": "190",
				"body": "193",
				"exit": "195",
			},
			ok: true,
		},
	}

	for i, g := range golden {
		sub, err := graphs.ParseSubGraph(g.subPath)
		if err != nil {
			t.Errorf("i=%d: %v", i, err)
			continue
		}
		graph, err := dot.ParseFile(g.graphPath)
		if err != nil {
			t.Errorf("i=%d: %v", i, err)
			continue
		}
		m, ok := Search(graph, sub)
		if ok != g.ok {
			t.Errorf("i=%d: ok mismatch; expected %v, got %v", i, g.ok, ok)
			continue
		}
		if !reflect.DeepEqual(m, g.m) {
			t.Errorf("i=%d: node pair mapping mismatch; expected %v, got %v", i, g.m, m)
		}
	}
}
Beispiel #10
0
func TestCandidates(t *testing.T) {
	golden := []struct {
		subPath   string
		graphPath string
		entry     string
		want      map[string]map[string]bool
		err       string
	}{
		// i=0
		{
			subPath:   "../testdata/primitives/if_else.dot",
			graphPath: "../testdata/primitives/if_else.dot",
			entry:     "cond",
			want: map[string]map[string]bool{
				"cond": {
					"cond": true,
				},
				"body_true": {
					"body_true":  true,
					"body_false": true,
				},
				"body_false": {
					"body_true":  true,
					"body_false": true,
				},
				"exit": {
					"exit": true,
				},
			},
			err: "",
		},
		// i=1
		{
			subPath:   "../testdata/primitives/if_else.dot",
			graphPath: "../testdata/c4_graphs/stmt.dot",
			entry:     "85",
			want: map[string]map[string]bool{
				"cond": {
					"85": true,
				},
				"body_true": {
					"88": true,
				},
				"body_false": {
					"88": true,
				},
				"exit": {
					"89": true,
				},
			},
			err: "",
		},
		// i=2
		{
			subPath:   "../testdata/primitives/pre_loop.dot",
			graphPath: "../testdata/c4_graphs/stmt.dot",
			entry:     "71",
			want: map[string]map[string]bool{
				"cond": {
					"71": true,
				},
				"body": {
					"74": true,
				},
				"exit": {
					"74": true,
				},
			},
			err: "",
		},
		// i=3
		{
			subPath:   "../testdata/primitives/pre_loop.dot",
			graphPath: "../testdata/c4_graphs/stmt.dot",
			entry:     "89",
			want: map[string]map[string]bool{
				"cond": {
					"89": true,
				},
				"body": {
					"92": true,
					"93": true,
				},
				"exit": {
					"92": true,
					"93": true,
				},
			},
			err: "",
		},
		// i=4
		{
			subPath:   "../testdata/primitives/if.dot",
			graphPath: "../testdata/c4_graphs/stmt.dot",
			entry:     "71",
			want: map[string]map[string]bool{
				"cond": {
					"71": true,
				},
				"body": {
					"74": true,
				},
				"exit": {
					"75": true,
				},
			},
			err: "",
		},
		// i=5
		{
			subPath:   "../testdata/primitives/if.dot",
			graphPath: "../testdata/c4_graphs/stmt.dot",
			entry:     "foo",
			want:      nil,
			err:       `unable to locate entry node "foo" in graph`,
		},
		// i=6
		{
			subPath:   "../testdata/primitives/if.dot",
			graphPath: "../testdata/c4_graphs/stmt.dot",
			entry:     "97",
			want:      nil,
			err:       `invalid entry node candidate "97"; expected 2 successors, got 1`,
		},
		// i=7
		{
			subPath:   "../testdata/primitives/if_else.dot",
			graphPath: "../testdata/c4_graphs/stmt.dot",
			entry:     "68",
			want:      nil,
			err:       "incomplete candidate mapping; expected 4 map entites, got 1",
		},
	}

	for i, g := range golden {
		sub, err := graphs.ParseSubGraph(g.subPath)
		if err != nil {
			t.Errorf("i=%d: %v", i, err)
			continue
		}
		graph, err := dot.ParseFile(g.graphPath)
		if err != nil {
			t.Errorf("i=%d: %v", i, err)
			continue
		}
		eq, err := candidates(graph, g.entry, sub)
		if !sameError(err, g.err) {
			t.Errorf("i=%d: error mismatch; expected %v, got %v", i, g.err, err)
			continue
		} else if err != nil {
			// Expected error, check next test case.
			continue
		}
		if !reflect.DeepEqual(eq.c, g.want) {
			t.Errorf("i=%d: candidate map mismatch; expected %v, got %v", i, g.want, eq.c)
		}
	}
}
Beispiel #11
0
// locateAndMerge parses the provided graphs and tries to merge isomorphisms of
// the subgraph in the graph into single nodes.
func locateAndMerge(graphPath, subPath string) error {
	// Parse graph.
	graph, err := dot.ParseFile(graphPath)
	if err != nil {
		return errutil.Err(err)
	}

	// Search for subgraph in GOPATH if not found.
	if ok, _ := osutil.Exists(subPath); !ok {
		dir, err := goutil.SrcDir("decomp.org/decomp/graphs/testdata/primitives")
		if err != nil {
			return errutil.Err(err)
		}
		subPath = filepath.Join(dir, subPath)
	}
	sub, err := graphs.ParseSubGraph(subPath)
	if err != nil {
		return errutil.Err(err)
	}

	// Merge isomorphisms.
	found := false
	if len(flagStart) > 0 {
		// Merge an isomorphism of sub in graph which starts at the node
		// specified by the "-start" flag.
		m, ok := iso.Isomorphism(graph, flagStart, sub)
		if ok {
			found = true
			printMapping(graph, sub, m)
			_, err := merge.Merge(graph, m, sub)
			if err != nil {
				return errutil.Err(err)
			}
		}
	} else {
		// Merge all isomorphisms of sub in graph.
		for {
			m, ok := iso.Search(graph, sub)
			if !ok {
				break
			}
			found = true
			printMapping(graph, sub, m)
			_, err := merge.Merge(graph, m, sub)
			if err != nil {
				return errutil.Err(err)
			}
		}
	}

	// Store DOT and PNG representation of graph.
	if found {
		err = dump(graph)
		if err != nil {
			return errutil.Err(err)
		}
	} else {
		fmt.Println("not found.")
	}

	return nil
}