github.com/april1989/origin-go-tools@v0.0.32/cmd/toolstash/cmp.go (about)

     1  // Copyright 2015 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  package main
     6  
     7  import (
     8  	"bufio"
     9  	"bytes"
    10  	"fmt"
    11  	"io"
    12  	"log"
    13  	"os"
    14  	"regexp"
    15  	"strconv"
    16  	"strings"
    17  )
    18  
    19  var (
    20  	hexDumpRE = regexp.MustCompile(`^\t(0x[0-9a-f]{4,})(( ([0-9a-f]{2}|  )){16})  [ -\x7F]{1,16}\n`)
    21  	listingRE = regexp.MustCompile(`^\t(0x[0-9a-f]{4,}) ([0-9]{4,}) \(.*:[0-9]+\)\t`)
    22  )
    23  
    24  // okdiffs lists regular expressions for lines to consider minor mismatches.
    25  // If one of these regexps matches both of a pair of unequal lines, the mismatch
    26  // is reported but not treated as the one worth looking for.
    27  // For example, differences in the TEXT line are typically frame size
    28  // changes due to optimization decisions made in the body of the function.
    29  // Better to keep looking for the actual difference.
    30  // Similarly, forward jumps might have different offsets due to a
    31  // change in instruction encoding later on.
    32  // Better to find that change.
    33  var okdiffs = []*regexp.Regexp{
    34  	regexp.MustCompile(`\)	TEXT[ 	].*,\$`),
    35  	regexp.MustCompile(`\)	WORD[ 	].*,\$`),
    36  	regexp.MustCompile(`\)	(B|BR|JMP)	`),
    37  	regexp.MustCompile(`\)	FUNCDATA	`),
    38  	regexp.MustCompile(`\\(z|x00)`),
    39  	regexp.MustCompile(`\$\([0-9]\.[0-9]+e[+\-][0-9]+\)`),
    40  	regexp.MustCompile(`size=.*value=.*args=.*locals=`),
    41  }
    42  
    43  func compareLogs(outfile string) string {
    44  	f1, err := os.Open(outfile + ".log")
    45  	if err != nil {
    46  		log.Fatal(err)
    47  	}
    48  	defer f1.Close()
    49  
    50  	f2, err := os.Open(outfile + ".stash.log")
    51  	if err != nil {
    52  		log.Fatal(err)
    53  	}
    54  	defer f2.Close()
    55  
    56  	b1 := bufio.NewReader(f1)
    57  	b2 := bufio.NewReader(f2)
    58  
    59  	offset := int64(0)
    60  	textOffset := offset
    61  	textLineno := 0
    62  	lineno := 0
    63  	var line1, line2 string
    64  	var prefix bytes.Buffer
    65  Reading:
    66  	for {
    67  		var err1, err2 error
    68  		line1, err1 = b1.ReadString('\n')
    69  		line2, err2 = b2.ReadString('\n')
    70  		if strings.Contains(line1, ")\tTEXT\t") {
    71  			textOffset = offset
    72  			textLineno = lineno
    73  		}
    74  		offset += int64(len(line1))
    75  		lineno++
    76  		if err1 == io.EOF && err2 == io.EOF {
    77  			return "no differences in debugging output"
    78  		}
    79  
    80  		if lineno == 1 || line1 == line2 && err1 == nil && err2 == nil {
    81  			continue
    82  		}
    83  		// Lines are inconsistent. Worth stopping?
    84  		for _, re := range okdiffs {
    85  			if re.MatchString(line1) && re.MatchString(line2) {
    86  				fmt.Fprintf(&prefix, "inconsistent log line:\n%s:%d:\n\t%s\n%s:%d:\n\t%s\n\n",
    87  					f1.Name(), lineno, strings.TrimSuffix(line1, "\n"),
    88  					f2.Name(), lineno, strings.TrimSuffix(line2, "\n"))
    89  				continue Reading
    90  			}
    91  		}
    92  
    93  		if err1 != nil {
    94  			line1 = err1.Error()
    95  		}
    96  		if err2 != nil {
    97  			line2 = err2.Error()
    98  		}
    99  		break
   100  	}
   101  
   102  	msg := fmt.Sprintf("inconsistent log line:\n%s:%d:\n\t%s\n%s:%d:\n\t%s",
   103  		f1.Name(), lineno, strings.TrimSuffix(line1, "\n"),
   104  		f2.Name(), lineno, strings.TrimSuffix(line2, "\n"))
   105  
   106  	if m := hexDumpRE.FindStringSubmatch(line1); m != nil {
   107  		target, err := strconv.ParseUint(m[1], 0, 64)
   108  		if err != nil {
   109  			goto Skip
   110  		}
   111  
   112  		m2 := hexDumpRE.FindStringSubmatch(line2)
   113  		if m2 == nil {
   114  			goto Skip
   115  		}
   116  
   117  		fields1 := strings.Fields(m[2])
   118  		fields2 := strings.Fields(m2[2])
   119  		i := 0
   120  		for i < len(fields1) && i < len(fields2) && fields1[i] == fields2[i] {
   121  			i++
   122  		}
   123  		target += uint64(i)
   124  
   125  		f1.Seek(textOffset, 0)
   126  		b1 = bufio.NewReader(f1)
   127  		last := ""
   128  		lineno := textLineno
   129  		limitAddr := uint64(0)
   130  		lastAddr := uint64(0)
   131  		for {
   132  			line1, err1 := b1.ReadString('\n')
   133  			if err1 != nil {
   134  				break
   135  			}
   136  			lineno++
   137  			if m := listingRE.FindStringSubmatch(line1); m != nil {
   138  				addr, _ := strconv.ParseUint(m[1], 0, 64)
   139  				if addr > target {
   140  					limitAddr = addr
   141  					break
   142  				}
   143  				last = line1
   144  				lastAddr = addr
   145  			} else if hexDumpRE.FindStringSubmatch(line1) != nil {
   146  				break
   147  			}
   148  		}
   149  		if last != "" {
   150  			msg = fmt.Sprintf("assembly instruction at %#04x-%#04x:\n%s:%d\n\t%s\n\n%s",
   151  				lastAddr, limitAddr, f1.Name(), lineno-1, strings.TrimSuffix(last, "\n"), msg)
   152  		}
   153  	}
   154  Skip:
   155  
   156  	return prefix.String() + msg
   157  }