...

Source file src/golang.org/x/arch/x86/x86asm/ext_test.go

Documentation: golang.org/x/arch/x86/x86asm

     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  
     7  package x86asm
     8  
     9  import (
    10  	"bufio"
    11  	"bytes"
    12  	"encoding/hex"
    13  	"flag"
    14  	"fmt"
    15  	"io"
    16  	"io/ioutil"
    17  	"log"
    18  	"math/rand"
    19  	"os"
    20  	"os/exec"
    21  	"regexp"
    22  	"runtime"
    23  	"strings"
    24  	"testing"
    25  	"time"
    26  )
    27  
    28  var (
    29  	printTests = flag.Bool("printtests", false, "print test cases that exercise new code paths")
    30  	dumpTest   = flag.Bool("dump", false, "dump all encodings")
    31  	mismatch   = flag.Bool("mismatch", false, "log allowed mismatches")
    32  	longTest   = flag.Bool("long", false, "long test")
    33  	keep       = flag.Bool("keep", false, "keep object files around")
    34  	debug      = false
    35  )
    36  
    37  // An ExtInst represents a single decoded instruction parsed
    38  // from an external disassembler's output.
    39  type ExtInst struct {
    40  	addr uint32
    41  	enc  [32]byte
    42  	nenc int
    43  	text string
    44  }
    45  
    46  func (r ExtInst) String() string {
    47  	return fmt.Sprintf("%#x: % x: %s", r.addr, r.enc, r.text)
    48  }
    49  
    50  // An ExtDis is a connection between an external disassembler and a test.
    51  type ExtDis struct {
    52  	Arch     int
    53  	Dec      chan ExtInst
    54  	File     *os.File
    55  	Size     int
    56  	KeepFile bool
    57  	Cmd      *exec.Cmd
    58  }
    59  
    60  // Run runs the given command - the external disassembler - and returns
    61  // a buffered reader of its standard output.
    62  func (ext *ExtDis) Run(cmd ...string) (*bufio.Reader, error) {
    63  	if *keep {
    64  		log.Printf("%s\n", strings.Join(cmd, " "))
    65  	}
    66  	ext.Cmd = exec.Command(cmd[0], cmd[1:]...)
    67  	out, err := ext.Cmd.StdoutPipe()
    68  	if err != nil {
    69  		return nil, fmt.Errorf("stdoutpipe: %v", err)
    70  	}
    71  	if err := ext.Cmd.Start(); err != nil {
    72  		return nil, fmt.Errorf("exec: %v", err)
    73  	}
    74  
    75  	b := bufio.NewReaderSize(out, 1<<20)
    76  	return b, nil
    77  }
    78  
    79  // Wait waits for the command started with Run to exit.
    80  func (ext *ExtDis) Wait() error {
    81  	return ext.Cmd.Wait()
    82  }
    83  
    84  // testExtDis tests a set of byte sequences against an external disassembler.
    85  // The disassembler is expected to produce the given syntax and be run
    86  // in the given architecture mode (16, 32, or 64-bit).
    87  // The extdis function must start the external disassembler
    88  // and then parse its output, sending the parsed instructions on ext.Dec.
    89  // The generate function calls its argument f once for each byte sequence
    90  // to be tested. The generate function itself will be called twice, and it must
    91  // make the same sequence of calls to f each time.
    92  // When a disassembly does not match the internal decoding,
    93  // allowedMismatch determines whether this mismatch should be
    94  // allowed, or else considered an error.
    95  func testExtDis(
    96  	t *testing.T,
    97  	syntax string,
    98  	arch int,
    99  	extdis func(ext *ExtDis) error,
   100  	generate func(f func([]byte)),
   101  	allowedMismatch func(text string, size int, inst *Inst, dec ExtInst) bool,
   102  ) {
   103  	decoderCover = make([]bool, len(decoder))
   104  	defer func() {
   105  		decoderCover = nil
   106  	}()
   107  
   108  	start := time.Now()
   109  	ext := &ExtDis{
   110  		Dec:  make(chan ExtInst),
   111  		Arch: arch,
   112  	}
   113  	errc := make(chan error)
   114  
   115  	// First pass: write instructions to input file for external disassembler.
   116  	file, f, size, err := writeInst(generate)
   117  	if err != nil {
   118  		t.Fatal(err)
   119  	}
   120  	ext.Size = size
   121  	ext.File = f
   122  	defer func() {
   123  		f.Close()
   124  		if !*keep {
   125  			os.Remove(file)
   126  		}
   127  	}()
   128  
   129  	// Second pass: compare disassembly against our decodings.
   130  	var (
   131  		totalTests  = 0
   132  		totalSkips  = 0
   133  		totalErrors = 0
   134  
   135  		errors = make([]string, 0, 100) // sampled errors, at most cap
   136  	)
   137  	go func() {
   138  		errc <- extdis(ext)
   139  	}()
   140  	generate(func(enc []byte) {
   141  		dec, ok := <-ext.Dec
   142  		if !ok {
   143  			t.Errorf("decoding stream ended early")
   144  			return
   145  		}
   146  		inst, text := disasm(syntax, arch, pad(enc))
   147  		totalTests++
   148  		if *dumpTest {
   149  			fmt.Printf("%x -> %s [%d]\n", enc[:len(enc)], dec.text, dec.nenc)
   150  		}
   151  		if text != dec.text || inst.Len != dec.nenc {
   152  			suffix := ""
   153  			if allowedMismatch(text, size, &inst, dec) {
   154  				totalSkips++
   155  				if !*mismatch {
   156  					return
   157  				}
   158  				suffix += " (allowed mismatch)"
   159  			}
   160  			totalErrors++
   161  			if len(errors) >= cap(errors) {
   162  				j := rand.Intn(totalErrors)
   163  				if j >= cap(errors) {
   164  					return
   165  				}
   166  				errors = append(errors[:j], errors[j+1:]...)
   167  			}
   168  			errors = append(errors, fmt.Sprintf("decode(%x) = %q, %d, want %q, %d%s", enc, text, inst.Len, dec.text, dec.nenc, suffix))
   169  		}
   170  	})
   171  
   172  	if *mismatch {
   173  		totalErrors -= totalSkips
   174  	}
   175  
   176  	for _, b := range errors {
   177  		t.Log(b)
   178  	}
   179  
   180  	if totalErrors > 0 {
   181  		t.Fail()
   182  	}
   183  	t.Logf("%d test cases, %d expected mismatches, %d failures; %.0f cases/second", totalTests, totalSkips, totalErrors, float64(totalTests)/time.Since(start).Seconds())
   184  
   185  	if err := <-errc; err != nil {
   186  		t.Fatalf("external disassembler: %v", err)
   187  	}
   188  }
   189  
   190  const start = 0x8000 // start address of text
   191  
   192  // writeInst writes the generated byte sequences to a new file
   193  // starting at offset start. That file is intended to be the input to
   194  // the external disassembler.
   195  func writeInst(generate func(func([]byte))) (file string, f *os.File, size int, err error) {
   196  	f, err = ioutil.TempFile("", "x86map")
   197  	if err != nil {
   198  		return
   199  	}
   200  
   201  	file = f.Name()
   202  
   203  	f.Seek(start, io.SeekStart)
   204  	w := bufio.NewWriter(f)
   205  	defer w.Flush()
   206  	size = 0
   207  	generate(func(x []byte) {
   208  		if len(x) > 16 {
   209  			x = x[:16]
   210  		}
   211  		if debug {
   212  			fmt.Printf("%#x: %x%x\n", start+size, x, pops[len(x):])
   213  		}
   214  		w.Write(x)
   215  		w.Write(pops[len(x):])
   216  		size += len(pops)
   217  	})
   218  	return file, f, size, nil
   219  }
   220  
   221  // 0x5F is a single-byte pop instruction.
   222  // We pad the bytes we want decoded with enough 0x5Fs
   223  // that no matter what state the instruction stream is in
   224  // after reading our bytes, the pops will get us back to
   225  // a forced instruction boundary.
   226  var pops = []byte{
   227  	0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
   228  	0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
   229  	0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
   230  	0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
   231  }
   232  
   233  // pad pads the code sequence with pops.
   234  func pad(enc []byte) []byte {
   235  	return append(enc[:len(enc):len(enc)], pops...)
   236  }
   237  
   238  // disasm returns the decoded instruction and text
   239  // for the given source bytes, using the given syntax and mode.
   240  func disasm(syntax string, mode int, src []byte) (inst Inst, text string) {
   241  	// If printTests is set, we record the coverage value
   242  	// before and after, and we write out the inputs for which
   243  	// coverage went up, in the format expected in testdata/decode.text.
   244  	// This produces a fairly small set of test cases that exercise nearly
   245  	// all the code.
   246  	var cover float64
   247  	if *printTests {
   248  		cover -= coverage()
   249  	}
   250  
   251  	inst, err := decode1(src, mode, syntax == "gnu")
   252  	if err != nil {
   253  		text = "error: " + err.Error()
   254  	} else {
   255  		switch syntax {
   256  		case "gnu":
   257  			text = GNUSyntax(inst, 0, nil)
   258  		case "intel":
   259  			text = IntelSyntax(inst, 0, nil)
   260  		case "plan9": // [sic]
   261  			text = GoSyntax(inst, 0, nil)
   262  		default:
   263  			text = "error: unknown syntax " + syntax
   264  		}
   265  	}
   266  
   267  	if *printTests {
   268  		cover += coverage()
   269  		if cover > 0 {
   270  			max := len(src)
   271  			if max > 16 && inst.Len <= 16 {
   272  				max = 16
   273  			}
   274  			fmt.Printf("%x|%x\t%d\t%s\t%s\n", src[:inst.Len], src[inst.Len:max], mode, syntax, text)
   275  		}
   276  	}
   277  
   278  	return
   279  }
   280  
   281  // coverage returns a floating point number denoting the
   282  // test coverage until now. The number increases when new code paths are exercised,
   283  // both in the Go program and in the decoder byte code.
   284  func coverage() float64 {
   285  	/*
   286  		testing.Coverage is not in the main distribution.
   287  		The implementation, which must go in package testing, is:
   288  
   289  		// Coverage reports the current code coverage as a fraction in the range [0, 1].
   290  		func Coverage() float64 {
   291  			var n, d int64
   292  			for _, counters := range cover.Counters {
   293  				for _, c := range counters {
   294  					if c > 0 {
   295  						n++
   296  					}
   297  					d++
   298  				}
   299  			}
   300  			if d == 0 {
   301  				return 0
   302  			}
   303  			return float64(n) / float64(d)
   304  		}
   305  	*/
   306  
   307  	var f float64
   308  	// f += testing.Coverage()
   309  	f += decodeCoverage()
   310  	return f
   311  }
   312  
   313  func decodeCoverage() float64 {
   314  	n := 0
   315  	for _, t := range decoderCover {
   316  		if t {
   317  			n++
   318  		}
   319  	}
   320  	return float64(1+n) / float64(1+len(decoderCover))
   321  }
   322  
   323  // Helpers for writing disassembler output parsers.
   324  
   325  // isPrefix reports whether text is the name of an instruction prefix.
   326  func isPrefix(text string) bool {
   327  	return prefixByte[text] > 0
   328  }
   329  
   330  // prefixByte maps instruction prefix text to actual prefix byte values.
   331  var prefixByte = map[string]byte{
   332  	"es":       0x26,
   333  	"cs":       0x2e,
   334  	"ss":       0x36,
   335  	"ds":       0x3e,
   336  	"fs":       0x64,
   337  	"gs":       0x65,
   338  	"data16":   0x66,
   339  	"addr16":   0x67,
   340  	"lock":     0xf0,
   341  	"repn":     0xf2,
   342  	"repne":    0xf2,
   343  	"rep":      0xf3,
   344  	"repe":     0xf3,
   345  	"xacquire": 0xf2,
   346  	"xrelease": 0xf3,
   347  	"bnd":      0xf2,
   348  	"addr32":   0x66,
   349  	"data32":   0x67,
   350  }
   351  
   352  // hasPrefix reports whether any of the space-separated words in the text s
   353  // begins with any of the given prefixes.
   354  func hasPrefix(s string, prefixes ...string) bool {
   355  	for _, prefix := range prefixes {
   356  		for s := s; s != ""; {
   357  			if strings.HasPrefix(s, prefix) {
   358  				return true
   359  			}
   360  			i := strings.Index(s, " ")
   361  			if i < 0 {
   362  				break
   363  			}
   364  			s = s[i+1:]
   365  		}
   366  	}
   367  	return false
   368  }
   369  
   370  // contains reports whether the text s contains any of the given substrings.
   371  func contains(s string, substrings ...string) bool {
   372  	for _, sub := range substrings {
   373  		if strings.Contains(s, sub) {
   374  			return true
   375  		}
   376  	}
   377  	return false
   378  }
   379  
   380  // isHex reports whether b is a hexadecimal character (0-9A-Fa-f).
   381  func isHex(b byte) bool { return b == '0' || unhex[b] > 0 }
   382  
   383  // parseHex parses the hexadecimal byte dump in hex,
   384  // appending the parsed bytes to raw and returning the updated slice.
   385  // The returned bool signals whether any invalid hex was found.
   386  // Spaces and tabs between bytes are okay but any other non-hex is not.
   387  func parseHex(hex []byte, raw []byte) ([]byte, bool) {
   388  	hex = trimSpace(hex)
   389  	for j := 0; j < len(hex); {
   390  		for hex[j] == ' ' || hex[j] == '\t' {
   391  			j++
   392  		}
   393  		if j >= len(hex) {
   394  			break
   395  		}
   396  		if j+2 > len(hex) || !isHex(hex[j]) || !isHex(hex[j+1]) {
   397  			return nil, false
   398  		}
   399  		raw = append(raw, unhex[hex[j]]<<4|unhex[hex[j+1]])
   400  		j += 2
   401  	}
   402  	return raw, true
   403  }
   404  
   405  var unhex = [256]byte{
   406  	'0': 0,
   407  	'1': 1,
   408  	'2': 2,
   409  	'3': 3,
   410  	'4': 4,
   411  	'5': 5,
   412  	'6': 6,
   413  	'7': 7,
   414  	'8': 8,
   415  	'9': 9,
   416  	'A': 10,
   417  	'B': 11,
   418  	'C': 12,
   419  	'D': 13,
   420  	'E': 14,
   421  	'F': 15,
   422  	'a': 10,
   423  	'b': 11,
   424  	'c': 12,
   425  	'd': 13,
   426  	'e': 14,
   427  	'f': 15,
   428  }
   429  
   430  // index is like bytes.Index(s, []byte(t)) but avoids the allocation.
   431  func index(s []byte, t string) int {
   432  	i := 0
   433  	for {
   434  		j := bytes.IndexByte(s[i:], t[0])
   435  		if j < 0 {
   436  			return -1
   437  		}
   438  		i = i + j
   439  		if i+len(t) > len(s) {
   440  			return -1
   441  		}
   442  		for k := 1; k < len(t); k++ {
   443  			if s[i+k] != t[k] {
   444  				goto nomatch
   445  			}
   446  		}
   447  		return i
   448  	nomatch:
   449  		i++
   450  	}
   451  }
   452  
   453  // fixSpace rewrites runs of spaces, tabs, and newline characters into single spaces in s.
   454  // If s must be rewritten, it is rewritten in place.
   455  func fixSpace(s []byte) []byte {
   456  	s = trimSpace(s)
   457  	for i := 0; i < len(s); i++ {
   458  		if s[i] == '\t' || s[i] == '\n' || i > 0 && s[i] == ' ' && s[i-1] == ' ' {
   459  			goto Fix
   460  		}
   461  	}
   462  	return s
   463  
   464  Fix:
   465  	b := s
   466  	w := 0
   467  	for i := 0; i < len(s); i++ {
   468  		c := s[i]
   469  		if c == '\t' || c == '\n' {
   470  			c = ' '
   471  		}
   472  		if c == ' ' && w > 0 && b[w-1] == ' ' {
   473  			continue
   474  		}
   475  		b[w] = c
   476  		w++
   477  	}
   478  	if w > 0 && b[w-1] == ' ' {
   479  		w--
   480  	}
   481  	return b[:w]
   482  }
   483  
   484  // trimSpace trims leading and trailing space from s, returning a subslice of s.
   485  func trimSpace(s []byte) []byte {
   486  	j := len(s)
   487  	for j > 0 && (s[j-1] == ' ' || s[j-1] == '\t' || s[j-1] == '\n') {
   488  		j--
   489  	}
   490  	i := 0
   491  	for i < j && (s[i] == ' ' || s[i] == '\t') {
   492  		i++
   493  	}
   494  	return s[i:j]
   495  }
   496  
   497  // pcrel and pcrelw match instructions using relative addressing mode.
   498  var (
   499  	pcrel  = regexp.MustCompile(`^((?:.* )?(?:j[a-z]+|call|ljmp|loopn?e?w?|xbegin)q?(?:,p[nt])?) 0x([0-9a-f]+)$`)
   500  	pcrelw = regexp.MustCompile(`^((?:.* )?(?:callw|jmpw|xbeginw|ljmpw)(?:,p[nt])?) 0x([0-9a-f]+)$`)
   501  )
   502  
   503  // Generators.
   504  //
   505  // The test cases are described as functions that invoke a callback repeatedly,
   506  // with a new input sequence each time. These helpers make writing those
   507  // a little easier.
   508  
   509  // hexCases generates the cases written in hexadecimal in the encoded string.
   510  // Spaces in 'encoded' separate entire test cases, not individual bytes.
   511  func hexCases(t *testing.T, encoded string) func(func([]byte)) {
   512  	return func(try func([]byte)) {
   513  		for _, x := range strings.Fields(encoded) {
   514  			src, err := hex.DecodeString(x)
   515  			if err != nil {
   516  				t.Errorf("parsing %q: %v", x, err)
   517  			}
   518  			try(src)
   519  		}
   520  	}
   521  }
   522  
   523  // testdataCases generates the test cases recorded in testdata/decode.txt.
   524  // It only uses the inputs; it ignores the answers recorded in that file.
   525  func testdataCases(t *testing.T) func(func([]byte)) {
   526  	var codes [][]byte
   527  	data, err := ioutil.ReadFile("testdata/decode.txt")
   528  	if err != nil {
   529  		t.Fatal(err)
   530  	}
   531  	for _, line := range strings.Split(string(data), "\n") {
   532  		line = strings.TrimSpace(line)
   533  		if line == "" || strings.HasPrefix(line, "#") {
   534  			continue
   535  		}
   536  		f := strings.Fields(line)[0]
   537  		i := strings.Index(f, "|")
   538  		if i < 0 {
   539  			t.Errorf("parsing %q: missing | separator", f)
   540  			continue
   541  		}
   542  		if i%2 != 0 {
   543  			t.Errorf("parsing %q: misaligned | separator", f)
   544  		}
   545  		code, err := hex.DecodeString(f[:i] + f[i+1:])
   546  		if err != nil {
   547  			t.Errorf("parsing %q: %v", f, err)
   548  			continue
   549  		}
   550  		codes = append(codes, code)
   551  	}
   552  
   553  	return func(try func([]byte)) {
   554  		for _, code := range codes {
   555  			try(code)
   556  		}
   557  	}
   558  }
   559  
   560  // manyPrefixes generates all possible 2⁹ combinations of nine chosen prefixes.
   561  // The relative ordering of the prefixes within the combinations varies deterministically.
   562  func manyPrefixes(try func([]byte)) {
   563  	var prefixBytes = []byte{0x66, 0x67, 0xF0, 0xF2, 0xF3, 0x3E, 0x36, 0x66, 0x67}
   564  	var enc []byte
   565  	for i := 0; i < 1<<uint(len(prefixBytes)); i++ {
   566  		enc = enc[:0]
   567  		for j, p := range prefixBytes {
   568  			if i&(1<<uint(j)) != 0 {
   569  				enc = append(enc, p)
   570  			}
   571  		}
   572  		if len(enc) > 0 {
   573  			k := i % len(enc)
   574  			enc[0], enc[k] = enc[k], enc[0]
   575  		}
   576  		try(enc)
   577  	}
   578  }
   579  
   580  // basicPrefixes geneartes 8 different possible prefix cases: no prefix
   581  // and then one each of seven different prefix bytes.
   582  func basicPrefixes(try func([]byte)) {
   583  	try(nil)
   584  	for _, b := range []byte{0x66, 0x67, 0xF0, 0xF2, 0xF3, 0x3E, 0x36} {
   585  		try([]byte{b})
   586  	}
   587  }
   588  
   589  func rexPrefixes(try func([]byte)) {
   590  	try(nil)
   591  	for _, b := range []byte{0x40, 0x48, 0x43, 0x4C} {
   592  		try([]byte{b})
   593  	}
   594  }
   595  
   596  // concat takes two generators and returns a generator for the
   597  // cross product of the two, concatenating the results from each.
   598  func concat(gen1, gen2 func(func([]byte))) func(func([]byte)) {
   599  	return func(try func([]byte)) {
   600  		gen1(func(enc1 []byte) {
   601  			gen2(func(enc2 []byte) {
   602  				try(append(enc1[:len(enc1):len(enc1)], enc2...))
   603  			})
   604  		})
   605  	}
   606  }
   607  
   608  // concat3 takes three generators and returns a generator for the
   609  // cross product of the three, concatenating the results from each.
   610  func concat3(gen1, gen2, gen3 func(func([]byte))) func(func([]byte)) {
   611  	return func(try func([]byte)) {
   612  		gen1(func(enc1 []byte) {
   613  			gen2(func(enc2 []byte) {
   614  				gen3(func(enc3 []byte) {
   615  					try(append(append(enc1[:len(enc1):len(enc1)], enc2...), enc3...))
   616  				})
   617  			})
   618  		})
   619  	}
   620  }
   621  
   622  // concat4 takes four generators and returns a generator for the
   623  // cross product of the four, concatenating the results from each.
   624  func concat4(gen1, gen2, gen3, gen4 func(func([]byte))) func(func([]byte)) {
   625  	return func(try func([]byte)) {
   626  		gen1(func(enc1 []byte) {
   627  			gen2(func(enc2 []byte) {
   628  				gen3(func(enc3 []byte) {
   629  					gen4(func(enc4 []byte) {
   630  						try(append(append(append(enc1[:len(enc1):len(enc1)], enc2...), enc3...), enc4...))
   631  					})
   632  				})
   633  			})
   634  		})
   635  	}
   636  }
   637  
   638  // filter generates the sequences from gen that satisfy ok.
   639  func filter(gen func(func([]byte)), ok func([]byte) bool) func(func([]byte)) {
   640  	return func(try func([]byte)) {
   641  		gen(func(enc []byte) {
   642  			if ok(enc) {
   643  				try(enc)
   644  			}
   645  		})
   646  	}
   647  }
   648  
   649  // enum8bit generates all possible 1-byte sequences, followed by distinctive padding.
   650  func enum8bit(try func([]byte)) {
   651  	for i := 0; i < 1<<8; i++ {
   652  		try([]byte{byte(i), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88})
   653  	}
   654  }
   655  
   656  // enum8bit generates all possible 2-byte sequences, followed by distinctive padding.
   657  func enum16bit(try func([]byte)) {
   658  	for i := 0; i < 1<<16; i++ {
   659  		try([]byte{byte(i), byte(i >> 8), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88})
   660  	}
   661  }
   662  
   663  // enum24bit generates all possible 3-byte sequences, followed by distinctive padding.
   664  func enum24bit(try func([]byte)) {
   665  	for i := 0; i < 1<<24; i++ {
   666  		try([]byte{byte(i), byte(i >> 8), byte(i >> 16), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88})
   667  	}
   668  }
   669  
   670  // enumModRM generates all possible modrm bytes and, for modrm values that indicate
   671  // a following sib byte, all possible modrm, sib combinations.
   672  func enumModRM(try func([]byte)) {
   673  	for i := 0; i < 256; i++ {
   674  		if (i>>3)&07 == 04 && i>>6 != 3 { // has sib
   675  			for j := 0; j < 256; j++ {
   676  				try([]byte{0, byte(i), byte(j), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}) // byte encodings
   677  				try([]byte{1, byte(i), byte(j), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}) // word encodings
   678  			}
   679  		} else {
   680  			try([]byte{0, byte(i), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}) // byte encodings
   681  			try([]byte{1, byte(i), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}) // word encodings
   682  		}
   683  	}
   684  }
   685  
   686  // fixed generates the single case b.
   687  // It's mainly useful to prepare an argument for concat or concat3.
   688  func fixed(b ...byte) func(func([]byte)) {
   689  	return func(try func([]byte)) {
   690  		try(b)
   691  	}
   692  }
   693  
   694  // testBasic runs the given test function with cases all using opcode as the initial opcode bytes.
   695  // It runs three phases:
   696  //
   697  // First, zero-or-one prefixes followed by opcode followed by all possible 1-byte values.
   698  // If in -short mode, that's all.
   699  //
   700  // Second, zero-or-one prefixes followed by opcode followed by all possible 2-byte values.
   701  // If not in -long mode, that's all. This phase and the next run in parallel with other tests
   702  // (using t.Parallel).
   703  //
   704  // Finally, opcode followed by all possible 3-byte values. The test can take a very long time
   705  // and prints progress messages to package log.
   706  func testBasic(t *testing.T, testfn func(*testing.T, func(func([]byte))), opcode ...byte) {
   707  	testfn(t, concat3(basicPrefixes, fixed(opcode...), enum8bit))
   708  	if testing.Short() {
   709  		return
   710  	}
   711  
   712  	t.Parallel()
   713  	testfn(t, concat3(basicPrefixes, fixed(opcode...), enum16bit))
   714  	if !*longTest {
   715  		return
   716  	}
   717  
   718  	name := caller(2)
   719  	op1 := make([]byte, len(opcode)+1)
   720  	copy(op1, opcode)
   721  	for i := 0; i < 256; i++ {
   722  		log.Printf("%s 24-bit: %d/256\n", name, i)
   723  		op1[len(opcode)] = byte(i)
   724  		testfn(t, concat(fixed(op1...), enum16bit))
   725  	}
   726  }
   727  
   728  func testBasicREX(t *testing.T, testfn func(*testing.T, func(func([]byte))), opcode ...byte) {
   729  	testfn(t, filter(concat4(basicPrefixes, rexPrefixes, fixed(opcode...), enum8bit), isValidREX))
   730  	if testing.Short() {
   731  		return
   732  	}
   733  
   734  	t.Parallel()
   735  	testfn(t, filter(concat4(basicPrefixes, rexPrefixes, fixed(opcode...), enum16bit), isValidREX))
   736  	if !*longTest {
   737  		return
   738  	}
   739  
   740  	name := caller(2)
   741  	op1 := make([]byte, len(opcode)+1)
   742  	copy(op1, opcode)
   743  	for i := 0; i < 256; i++ {
   744  		log.Printf("%s 24-bit: %d/256\n", name, i)
   745  		op1[len(opcode)] = byte(i)
   746  		testfn(t, filter(concat3(rexPrefixes, fixed(op1...), enum16bit), isValidREX))
   747  	}
   748  }
   749  
   750  // testPrefix runs the given test function for all many prefix possibilities
   751  // followed by all possible 1-byte sequences.
   752  //
   753  // If in -long mode, it then runs a test of all the prefix possibilities followed
   754  // by all possible 2-byte sequences.
   755  func testPrefix(t *testing.T, testfn func(*testing.T, func(func([]byte)))) {
   756  	t.Parallel()
   757  	testfn(t, concat(manyPrefixes, enum8bit))
   758  	if testing.Short() || !*longTest {
   759  		return
   760  	}
   761  
   762  	name := caller(2)
   763  	for i := 0; i < 256; i++ {
   764  		log.Printf("%s 16-bit: %d/256\n", name, i)
   765  		testfn(t, concat3(manyPrefixes, fixed(byte(i)), enum8bit))
   766  	}
   767  }
   768  
   769  func testPrefixREX(t *testing.T, testfn func(*testing.T, func(func([]byte)))) {
   770  	t.Parallel()
   771  	testfn(t, filter(concat3(manyPrefixes, rexPrefixes, enum8bit), isValidREX))
   772  	if testing.Short() || !*longTest {
   773  		return
   774  	}
   775  
   776  	name := caller(2)
   777  	for i := 0; i < 256; i++ {
   778  		log.Printf("%s 16-bit: %d/256\n", name, i)
   779  		testfn(t, filter(concat4(manyPrefixes, rexPrefixes, fixed(byte(i)), enum8bit), isValidREX))
   780  	}
   781  }
   782  
   783  func caller(skip int) string {
   784  	pc, _, _, _ := runtime.Caller(skip)
   785  	f := runtime.FuncForPC(pc)
   786  	name := "?"
   787  	if f != nil {
   788  		name = f.Name()
   789  		if i := strings.LastIndex(name, "."); i >= 0 {
   790  			name = name[i+1:]
   791  		}
   792  	}
   793  	return name
   794  }
   795  
   796  func isValidREX(x []byte) bool {
   797  	i := 0
   798  	for i < len(x) && isPrefixByte(x[i]) {
   799  		i++
   800  	}
   801  	if i < len(x) && Prefix(x[i]).IsREX() {
   802  		i++
   803  		if i < len(x) {
   804  			return !isPrefixByte(x[i]) && !Prefix(x[i]).IsREX()
   805  		}
   806  	}
   807  	return true
   808  }
   809  
   810  func isPrefixByte(b byte) bool {
   811  	switch b {
   812  	case 0x26, 0x2E, 0x36, 0x3E, 0x64, 0x65, 0x66, 0x67, 0xF0, 0xF2, 0xF3:
   813  		return true
   814  	}
   815  	return false
   816  }
   817  

View as plain text