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