...

Source file src/golang.org/x/arch/arm/armasm/ext_test.go

Documentation: golang.org/x/arch/arm/armasm

     1  // Copyright 2014 The Go Authors.  All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Support for testing against external disassembler program.
     6  // Copied and simplified from ../../x86/x86asm/ext_test.go.
     7  
     8  package armasm
     9  
    10  import (
    11  	"bufio"
    12  	"bytes"
    13  	"encoding/hex"
    14  	"flag"
    15  	"fmt"
    16  	"io"
    17  	"io/ioutil"
    18  	"log"
    19  	"math/rand"
    20  	"os"
    21  	"os/exec"
    22  	"regexp"
    23  	"runtime"
    24  	"strings"
    25  	"testing"
    26  	"time"
    27  )
    28  
    29  var (
    30  	printTests = flag.Bool("printtests", false, "print test cases that exercise new code paths")
    31  	dumpTest   = flag.Bool("dump", false, "dump all encodings")
    32  	mismatch   = flag.Bool("mismatch", false, "log allowed mismatches")
    33  	longTest   = flag.Bool("long", false, "long test")
    34  	keep       = flag.Bool("keep", false, "keep object files around")
    35  	debug      = false
    36  )
    37  
    38  // An ExtInst represents a single decoded instruction parsed
    39  // from an external disassembler's output.
    40  type ExtInst struct {
    41  	addr uint32
    42  	enc  [4]byte
    43  	nenc int
    44  	text string
    45  }
    46  
    47  func (r ExtInst) String() string {
    48  	return fmt.Sprintf("%#x: % x: %s", r.addr, r.enc, r.text)
    49  }
    50  
    51  // An ExtDis is a connection between an external disassembler and a test.
    52  type ExtDis struct {
    53  	Arch     Mode
    54  	Dec      chan ExtInst
    55  	File     *os.File
    56  	Size     int
    57  	KeepFile bool
    58  	Cmd      *exec.Cmd
    59  }
    60  
    61  // Run runs the given command - the external disassembler - and returns
    62  // a buffered reader of its standard output.
    63  func (ext *ExtDis) Run(cmd ...string) (*bufio.Reader, error) {
    64  	if *keep {
    65  		log.Printf("%s\n", strings.Join(cmd, " "))
    66  	}
    67  	ext.Cmd = exec.Command(cmd[0], cmd[1:]...)
    68  	out, err := ext.Cmd.StdoutPipe()
    69  	if err != nil {
    70  		return nil, fmt.Errorf("stdoutpipe: %v", err)
    71  	}
    72  	if err := ext.Cmd.Start(); err != nil {
    73  		return nil, fmt.Errorf("exec: %v", err)
    74  	}
    75  
    76  	b := bufio.NewReaderSize(out, 1<<20)
    77  	return b, nil
    78  }
    79  
    80  // Wait waits for the command started with Run to exit.
    81  func (ext *ExtDis) Wait() error {
    82  	return ext.Cmd.Wait()
    83  }
    84  
    85  // testExtDis tests a set of byte sequences against an external disassembler.
    86  // The disassembler is expected to produce the given syntax and be run
    87  // in the given architecture mode (16, 32, or 64-bit).
    88  // The extdis function must start the external disassembler
    89  // and then parse its output, sending the parsed instructions on ext.Dec.
    90  // The generate function calls its argument f once for each byte sequence
    91  // to be tested. The generate function itself will be called twice, and it must
    92  // make the same sequence of calls to f each time.
    93  // When a disassembly does not match the internal decoding,
    94  // allowedMismatch determines whether this mismatch should be
    95  // allowed, or else considered an error.
    96  func testExtDis(
    97  	t *testing.T,
    98  	syntax string,
    99  	arch Mode,
   100  	extdis func(ext *ExtDis) error,
   101  	generate func(f func([]byte)),
   102  	allowedMismatch func(text string, size int, inst *Inst, dec ExtInst) bool,
   103  ) {
   104  	start := time.Now()
   105  	ext := &ExtDis{
   106  		Dec:  make(chan ExtInst),
   107  		Arch: arch,
   108  	}
   109  	errc := make(chan error)
   110  
   111  	// First pass: write instructions to input file for external disassembler.
   112  	file, f, size, err := writeInst(generate)
   113  	if err != nil {
   114  		t.Fatal(err)
   115  	}
   116  	ext.Size = size
   117  	ext.File = f
   118  	defer func() {
   119  		f.Close()
   120  		if !*keep {
   121  			os.Remove(file)
   122  		}
   123  	}()
   124  
   125  	// Second pass: compare disassembly against our decodings.
   126  	var (
   127  		totalTests  = 0
   128  		totalSkips  = 0
   129  		totalErrors = 0
   130  
   131  		errors = make([]string, 0, 100) // sampled errors, at most cap
   132  	)
   133  	go func() {
   134  		errc <- extdis(ext)
   135  	}()
   136  	generate(func(enc []byte) {
   137  		dec, ok := <-ext.Dec
   138  		if !ok {
   139  			t.Errorf("decoding stream ended early")
   140  			return
   141  		}
   142  		inst, text := disasm(syntax, arch, pad(enc))
   143  		totalTests++
   144  		if *dumpTest {
   145  			fmt.Printf("%x -> %s [%d]\n", enc[:len(enc)], dec.text, dec.nenc)
   146  		}
   147  		if text != dec.text || inst.Len != dec.nenc {
   148  			suffix := ""
   149  			if allowedMismatch(text, size, &inst, dec) {
   150  				totalSkips++
   151  				if !*mismatch {
   152  					return
   153  				}
   154  				suffix += " (allowed mismatch)"
   155  			}
   156  			totalErrors++
   157  			if len(errors) >= cap(errors) {
   158  				j := rand.Intn(totalErrors)
   159  				if j >= cap(errors) {
   160  					return
   161  				}
   162  				errors = append(errors[:j], errors[j+1:]...)
   163  			}
   164  			errors = append(errors, fmt.Sprintf("decode(%x) = %q, %d, want %q, %d%s", enc, text, inst.Len, dec.text, dec.nenc, suffix))
   165  		}
   166  	})
   167  
   168  	if *mismatch {
   169  		totalErrors -= totalSkips
   170  	}
   171  
   172  	for _, b := range errors {
   173  		t.Log(b)
   174  	}
   175  
   176  	if totalErrors > 0 {
   177  		t.Fail()
   178  	}
   179  	t.Logf("%d test cases, %d expected mismatches, %d failures; %.0f cases/second", totalTests, totalSkips, totalErrors, float64(totalTests)/time.Since(start).Seconds())
   180  
   181  	if err := <-errc; err != nil {
   182  		t.Fatalf("external disassembler: %v", err)
   183  	}
   184  
   185  }
   186  
   187  const start = 0x8000 // start address of text
   188  
   189  // writeInst writes the generated byte sequences to a new file
   190  // starting at offset start. That file is intended to be the input to
   191  // the external disassembler.
   192  func writeInst(generate func(func([]byte))) (file string, f *os.File, size int, err error) {
   193  	f, err = ioutil.TempFile("", "armasm")
   194  	if err != nil {
   195  		return
   196  	}
   197  
   198  	file = f.Name()
   199  
   200  	f.Seek(start, io.SeekStart)
   201  	w := bufio.NewWriter(f)
   202  	defer w.Flush()
   203  	size = 0
   204  	generate(func(x []byte) {
   205  		if len(x) > 4 {
   206  			x = x[:4]
   207  		}
   208  		if debug {
   209  			fmt.Printf("%#x: %x%x\n", start+size, x, zeros[len(x):])
   210  		}
   211  		w.Write(x)
   212  		w.Write(zeros[len(x):])
   213  		size += len(zeros)
   214  	})
   215  	return file, f, size, nil
   216  }
   217  
   218  var zeros = []byte{0, 0, 0, 0}
   219  
   220  // pad pads the code sequence with pops.
   221  func pad(enc []byte) []byte {
   222  	if len(enc) < 4 {
   223  		enc = append(enc[:len(enc):len(enc)], zeros[:4-len(enc)]...)
   224  	}
   225  	return enc
   226  }
   227  
   228  // disasm returns the decoded instruction and text
   229  // for the given source bytes, using the given syntax and mode.
   230  func disasm(syntax string, mode Mode, src []byte) (inst Inst, text string) {
   231  	// If printTests is set, we record the coverage value
   232  	// before and after, and we write out the inputs for which
   233  	// coverage went up, in the format expected in testdata/decode.text.
   234  	// This produces a fairly small set of test cases that exercise nearly
   235  	// all the code.
   236  	var cover float64
   237  	if *printTests {
   238  		cover -= coverage()
   239  	}
   240  
   241  	inst, err := Decode(src, mode)
   242  	if err != nil {
   243  		text = "error: " + err.Error()
   244  	} else {
   245  		text = inst.String()
   246  		switch syntax {
   247  		//case "arm":
   248  		//	text = ARMSyntax(inst)
   249  		case "gnu":
   250  			text = GNUSyntax(inst)
   251  		//case "plan9": // [sic]
   252  		//	text = GoSyntax(inst, 0, nil)
   253  		default:
   254  			text = "error: unknown syntax " + syntax
   255  		}
   256  	}
   257  
   258  	if *printTests {
   259  		cover += coverage()
   260  		if cover > 0 {
   261  			max := len(src)
   262  			if max > 4 && inst.Len <= 4 {
   263  				max = 4
   264  			}
   265  			fmt.Printf("%x|%x\t%d\t%s\t%s\n", src[:inst.Len], src[inst.Len:max], mode, syntax, text)
   266  		}
   267  	}
   268  
   269  	return
   270  }
   271  
   272  // coverage returns a floating point number denoting the
   273  // test coverage until now. The number increases when new code paths are exercised,
   274  // both in the Go program and in the decoder byte code.
   275  func coverage() float64 {
   276  	/*
   277  		testing.Coverage is not in the main distribution.
   278  		The implementation, which must go in package testing, is:
   279  
   280  		// Coverage reports the current code coverage as a fraction in the range [0, 1].
   281  		func Coverage() float64 {
   282  			var n, d int64
   283  			for _, counters := range cover.Counters {
   284  				for _, c := range counters {
   285  					if c > 0 {
   286  						n++
   287  					}
   288  					d++
   289  				}
   290  			}
   291  			if d == 0 {
   292  				return 0
   293  			}
   294  			return float64(n) / float64(d)
   295  		}
   296  	*/
   297  
   298  	var f float64
   299  	f += testing.Coverage()
   300  	f += decodeCoverage()
   301  	return f
   302  }
   303  
   304  func decodeCoverage() float64 {
   305  	n := 0
   306  	for _, t := range decoderCover {
   307  		if t {
   308  			n++
   309  		}
   310  	}
   311  	return float64(1+n) / float64(1+len(decoderCover))
   312  }
   313  
   314  // Helpers for writing disassembler output parsers.
   315  
   316  // hasPrefix reports whether any of the space-separated words in the text s
   317  // begins with any of the given prefixes.
   318  func hasPrefix(s string, prefixes ...string) bool {
   319  	for _, prefix := range prefixes {
   320  		for s := s; s != ""; {
   321  			if strings.HasPrefix(s, prefix) {
   322  				return true
   323  			}
   324  			i := strings.Index(s, " ")
   325  			if i < 0 {
   326  				break
   327  			}
   328  			s = s[i+1:]
   329  		}
   330  	}
   331  	return false
   332  }
   333  
   334  // contains reports whether the text s contains any of the given substrings.
   335  func contains(s string, substrings ...string) bool {
   336  	for _, sub := range substrings {
   337  		if strings.Contains(s, sub) {
   338  			return true
   339  		}
   340  	}
   341  	return false
   342  }
   343  
   344  // isHex reports whether b is a hexadecimal character (0-9A-Fa-f).
   345  func isHex(b byte) bool { return b == '0' || unhex[b] > 0 }
   346  
   347  // parseHex parses the hexadecimal byte dump in hex,
   348  // appending the parsed bytes to raw and returning the updated slice.
   349  // The returned bool signals whether any invalid hex was found.
   350  // Spaces and tabs between bytes are okay but any other non-hex is not.
   351  func parseHex(hex []byte, raw []byte) ([]byte, bool) {
   352  	hex = trimSpace(hex)
   353  	for j := 0; j < len(hex); {
   354  		for hex[j] == ' ' || hex[j] == '\t' {
   355  			j++
   356  		}
   357  		if j >= len(hex) {
   358  			break
   359  		}
   360  		if j+2 > len(hex) || !isHex(hex[j]) || !isHex(hex[j+1]) {
   361  			return nil, false
   362  		}
   363  		raw = append(raw, unhex[hex[j]]<<4|unhex[hex[j+1]])
   364  		j += 2
   365  	}
   366  	return raw, true
   367  }
   368  
   369  var unhex = [256]byte{
   370  	'0': 0,
   371  	'1': 1,
   372  	'2': 2,
   373  	'3': 3,
   374  	'4': 4,
   375  	'5': 5,
   376  	'6': 6,
   377  	'7': 7,
   378  	'8': 8,
   379  	'9': 9,
   380  	'A': 10,
   381  	'B': 11,
   382  	'C': 12,
   383  	'D': 13,
   384  	'E': 14,
   385  	'F': 15,
   386  	'a': 10,
   387  	'b': 11,
   388  	'c': 12,
   389  	'd': 13,
   390  	'e': 14,
   391  	'f': 15,
   392  }
   393  
   394  // index is like bytes.Index(s, []byte(t)) but avoids the allocation.
   395  func index(s []byte, t string) int {
   396  	i := 0
   397  	for {
   398  		j := bytes.IndexByte(s[i:], t[0])
   399  		if j < 0 {
   400  			return -1
   401  		}
   402  		i = i + j
   403  		if i+len(t) > len(s) {
   404  			return -1
   405  		}
   406  		for k := 1; k < len(t); k++ {
   407  			if s[i+k] != t[k] {
   408  				goto nomatch
   409  			}
   410  		}
   411  		return i
   412  	nomatch:
   413  		i++
   414  	}
   415  }
   416  
   417  // fixSpace rewrites runs of spaces, tabs, and newline characters into single spaces in s.
   418  // If s must be rewritten, it is rewritten in place.
   419  func fixSpace(s []byte) []byte {
   420  	s = trimSpace(s)
   421  	for i := 0; i < len(s); i++ {
   422  		if s[i] == '\t' || s[i] == '\n' || i > 0 && s[i] == ' ' && s[i-1] == ' ' {
   423  			goto Fix
   424  		}
   425  	}
   426  	return s
   427  
   428  Fix:
   429  	b := s
   430  	w := 0
   431  	for i := 0; i < len(s); i++ {
   432  		c := s[i]
   433  		if c == '\t' || c == '\n' {
   434  			c = ' '
   435  		}
   436  		if c == ' ' && w > 0 && b[w-1] == ' ' {
   437  			continue
   438  		}
   439  		b[w] = c
   440  		w++
   441  	}
   442  	if w > 0 && b[w-1] == ' ' {
   443  		w--
   444  	}
   445  	return b[:w]
   446  }
   447  
   448  // trimSpace trims leading and trailing space from s, returning a subslice of s.
   449  func trimSpace(s []byte) []byte {
   450  	j := len(s)
   451  	for j > 0 && (s[j-1] == ' ' || s[j-1] == '\t' || s[j-1] == '\n') {
   452  		j--
   453  	}
   454  	i := 0
   455  	for i < j && (s[i] == ' ' || s[i] == '\t') {
   456  		i++
   457  	}
   458  	return s[i:j]
   459  }
   460  
   461  // pcrel matches instructions using relative addressing mode.
   462  var (
   463  	pcrel = regexp.MustCompile(`^((?:.* )?(?:b|bl)x?(?:eq|ne|cs|cc|mi|pl|vs|vc|hi|ls|ge|lt|gt|le)?) 0x([0-9a-f]+)$`)
   464  )
   465  
   466  // Generators.
   467  //
   468  // The test cases are described as functions that invoke a callback repeatedly,
   469  // with a new input sequence each time. These helpers make writing those
   470  // a little easier.
   471  
   472  // condCases generates conditional instructions.
   473  func condCases(t *testing.T) func(func([]byte)) {
   474  	return func(try func([]byte)) {
   475  		// All the strides are relatively prime to 2 and therefore to 2²⁸,
   476  		// so we will not repeat any instructions until we have tried all 2²⁸.
   477  		// Using a stride other than 1 is meant to visit the instructions in a
   478  		// pseudorandom order, which gives better variety in the set of
   479  		// test cases chosen by -printtests.
   480  		stride := uint32(10007)
   481  		n := 1 << 28 / 7
   482  		if testing.Short() {
   483  			stride = 100003
   484  			n = 1 << 28 / 1001
   485  		} else if *longTest {
   486  			stride = 200000033
   487  			n = 1 << 28
   488  		}
   489  		x := uint32(0)
   490  		for i := 0; i < n; i++ {
   491  			enc := (x%15)<<28 | x&(1<<28-1)
   492  			try([]byte{byte(enc), byte(enc >> 8), byte(enc >> 16), byte(enc >> 24)})
   493  			x += stride
   494  		}
   495  	}
   496  }
   497  
   498  // uncondCases generates unconditional instructions.
   499  func uncondCases(t *testing.T) func(func([]byte)) {
   500  	return func(try func([]byte)) {
   501  		condCases(t)(func(enc []byte) {
   502  			enc[3] |= 0xF0
   503  			try(enc)
   504  		})
   505  	}
   506  }
   507  
   508  func countBits(x uint32) int {
   509  	n := 0
   510  	for ; x != 0; x >>= 1 {
   511  		n += int(x & 1)
   512  	}
   513  	return n
   514  }
   515  
   516  func expandBits(x, m uint32) uint32 {
   517  	var out uint32
   518  	for i := uint(0); i < 32; i++ {
   519  		out >>= 1
   520  		if m&1 != 0 {
   521  			out |= (x & 1) << 31
   522  			x >>= 1
   523  		}
   524  		m >>= 1
   525  	}
   526  	return out
   527  }
   528  
   529  func tryCondMask(mask, val uint32, try func([]byte)) {
   530  	n := countBits(^mask)
   531  	bits := uint32(0)
   532  	for i := 0; i < 1<<uint(n); i++ {
   533  		bits += 848251 // arbitrary prime
   534  		x := val | expandBits(bits, ^mask) | uint32(i)%15<<28
   535  		try([]byte{byte(x), byte(x >> 8), byte(x >> 16), byte(x >> 24)})
   536  	}
   537  }
   538  
   539  // vfpCases generates VFP instructions.
   540  func vfpCases(t *testing.T) func(func([]byte)) {
   541  	const (
   542  		vfpmask uint32 = 0xFF00FE10
   543  		vfp     uint32 = 0x0E009A00
   544  	)
   545  	return func(try func([]byte)) {
   546  		tryCondMask(0xff00fe10, 0x0e009a00, try) // standard VFP instruction space
   547  		tryCondMask(0xffc00f7f, 0x0e000b10, try) // VFP MOV core reg to/from float64 half
   548  		tryCondMask(0xffe00f7f, 0x0e000a10, try) // VFP MOV core reg to/from float32
   549  		tryCondMask(0xffef0fff, 0x0ee10a10, try) // VFP MOV core reg to/from cond codes
   550  	}
   551  }
   552  
   553  // hexCases generates the cases written in hexadecimal in the encoded string.
   554  // Spaces in 'encoded' separate entire test cases, not individual bytes.
   555  func hexCases(t *testing.T, encoded string) func(func([]byte)) {
   556  	return func(try func([]byte)) {
   557  		for _, x := range strings.Fields(encoded) {
   558  			src, err := hex.DecodeString(x)
   559  			if err != nil {
   560  				t.Errorf("parsing %q: %v", x, err)
   561  			}
   562  			try(src)
   563  		}
   564  	}
   565  }
   566  
   567  // testdataCases generates the test cases recorded in testdata/decode.txt.
   568  // It only uses the inputs; it ignores the answers recorded in that file.
   569  func testdataCases(t *testing.T) func(func([]byte)) {
   570  	var codes [][]byte
   571  	data, err := ioutil.ReadFile("testdata/decode.txt")
   572  	if err != nil {
   573  		t.Fatal(err)
   574  	}
   575  	for _, line := range strings.Split(string(data), "\n") {
   576  		line = strings.TrimSpace(line)
   577  		if line == "" || strings.HasPrefix(line, "#") {
   578  			continue
   579  		}
   580  		f := strings.Fields(line)[0]
   581  		i := strings.Index(f, "|")
   582  		if i < 0 {
   583  			t.Errorf("parsing %q: missing | separator", f)
   584  			continue
   585  		}
   586  		if i%2 != 0 {
   587  			t.Errorf("parsing %q: misaligned | separator", f)
   588  		}
   589  		code, err := hex.DecodeString(f[:i] + f[i+1:])
   590  		if err != nil {
   591  			t.Errorf("parsing %q: %v", f, err)
   592  			continue
   593  		}
   594  		codes = append(codes, code)
   595  	}
   596  
   597  	return func(try func([]byte)) {
   598  		for _, code := range codes {
   599  			try(code)
   600  		}
   601  	}
   602  }
   603  
   604  func caller(skip int) string {
   605  	pc, _, _, _ := runtime.Caller(skip)
   606  	f := runtime.FuncForPC(pc)
   607  	name := "?"
   608  	if f != nil {
   609  		name = f.Name()
   610  		if i := strings.LastIndex(name, "."); i >= 0 {
   611  			name = name[i+1:]
   612  		}
   613  	}
   614  	return name
   615  }
   616  

View as plain text