github.com/AndrienkoAleksandr/go@v0.0.19/src/intern/bisect/bisect.go (about)

     1  // Copyright 2023 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 bisect can be used by compilers and other programs
     6  // to serve as a target for the bisect debugging tool.
     7  // See [golang.org/x/tools/cmd/bisect] for details about using the tool.
     8  //
     9  // To be a bisect target, allowing bisect to help determine which of a set of independent
    10  // changes provokes a failure, a program needs to:
    11  //
    12  //  1. Define a way to accept a change pattern on its command line or in its environment.
    13  //     The most common mechanism is a command-line flag.
    14  //     The pattern can be passed to [New] to create a [Matcher], the compiled form of a pattern.
    15  //
    16  //  2. Assign each change a unique ID. One possibility is to use a sequence number,
    17  //     but the most common mechanism is to hash some kind of identifying information
    18  //     like the file and line number where the change might be applied.
    19  //     [Hash] hashes its arguments to compute an ID.
    20  //
    21  //  3. Enable each change that the pattern says should be enabled.
    22  //     The [Matcher.ShouldEnable] method answers this question for a given change ID.
    23  //
    24  //  4. Print a report identifying each change that the pattern says should be printed.
    25  //     The [Matcher.ShouldPrint] method answers this question for a given change ID.
    26  //     The report consists of one more lines on standard error or standard output
    27  //     that contain a “match marker”. [Marker] returns the match marker for a given ID.
    28  //     When bisect reports a change as causing the failure, it identifies the change
    29  //     by printing the report lines with the match marker removed.
    30  //
    31  // # Example Usage
    32  //
    33  // A program starts by defining how it receives the pattern. In this example, we will assume a flag.
    34  // The next step is to compile the pattern:
    35  //
    36  //	m, err := bisect.New(patternFlag)
    37  //	if err != nil {
    38  //		log.Fatal(err)
    39  //	}
    40  //
    41  // Then, each time a potential change is considered, the program computes
    42  // a change ID by hashing identifying information (source file and line, in this case)
    43  // and then calls m.ShouldPrint and m.ShouldEnable to decide whether to
    44  // print and enable the change, respectively. The two can return different values
    45  // depending on whether bisect is trying to find a minimal set of changes to
    46  // disable or to enable to provoke the failure.
    47  //
    48  // It is usually helpful to write a helper function that accepts the identifying information
    49  // and then takes care of hashing, printing, and reporting whether the identified change
    50  // should be enabled. For example, a helper for changes identified by a file and line number
    51  // would be:
    52  //
    53  //	func ShouldEnable(file string, line int) {
    54  //		h := bisect.Hash(file, line)
    55  //		if m.ShouldPrint(h) {
    56  //			fmt.Fprintf(os.Stderr, "%v %s:%d\n", bisect.Marker(h), file, line)
    57  //		}
    58  //		return m.ShouldEnable(h)
    59  //	}
    60  //
    61  // Finally, note that New returns a nil Matcher when there is no pattern,
    62  // meaning that the target is not running under bisect at all,
    63  // so all changes should be enabled and none should be printed.
    64  // In that common case, the computation of the hash can be avoided entirely
    65  // by checking for m == nil first:
    66  //
    67  //	func ShouldEnable(file string, line int) bool {
    68  //		if m == nil {
    69  //			return false
    70  //		}
    71  //		h := bisect.Hash(file, line)
    72  //		if m.ShouldPrint(h) {
    73  //			fmt.Fprintf(os.Stderr, "%v %s:%d\n", bisect.Marker(h), file, line)
    74  //		}
    75  //		return m.ShouldEnable(h)
    76  //	}
    77  //
    78  // When the identifying information is expensive to format, this code can call
    79  // [Matcher.MarkerOnly] to find out whether short report lines containing only the
    80  // marker are permitted for a given run. (Bisect permits such lines when it is
    81  // still exploring the space of possible changes and will not be showing the
    82  // output to the user.) If so, the client can choose to print only the marker:
    83  //
    84  //	func ShouldEnable(file string, line int) bool {
    85  //		if m == nil {
    86  //			return false
    87  //		}
    88  //		h := bisect.Hash(file, line)
    89  //		if m.ShouldPrint(h) {
    90  //			if m.MarkerOnly() {
    91  //				bisect.PrintMarker(os.Stderr)
    92  //			} else {
    93  //				fmt.Fprintf(os.Stderr, "%v %s:%d\n", bisect.Marker(h), file, line)
    94  //			}
    95  //		}
    96  //		return m.ShouldEnable(h)
    97  //	}
    98  //
    99  // This specific helper – deciding whether to enable a change identified by
   100  // file and line number and printing about the change when necessary – is
   101  // provided by the [Matcher.FileLine] method.
   102  //
   103  // Another common usage is deciding whether to make a change in a function
   104  // based on the caller's stack, to identify the specific calling contexts that the
   105  // change breaks. The [Matcher.Stack] method takes care of obtaining the stack,
   106  // printing it when necessary, and reporting whether to enable the change
   107  // based on that stack.
   108  //
   109  // # Pattern Syntax
   110  //
   111  // Patterns are generated by the bisect tool and interpreted by [New].
   112  // Users should not have to understand the patterns except when
   113  // debugging a target's bisect support or debugging the bisect tool itself.
   114  //
   115  // The pattern syntax selecting a change is a sequence of bit strings
   116  // separated by + and - operators. Each bit string denotes the set of
   117  // changes with IDs ending in those bits, + is set addition, - is set subtraction,
   118  // and the expression is evaluated in the usual left-to-right order.
   119  // The special binary number “y” denotes the set of all changes,
   120  // standing in for the empty bit string.
   121  // In the expression, all the + operators must appear before all the - operators.
   122  // A leading + adds to an empty set. A leading - subtracts from the set of all
   123  // possible suffixes.
   124  //
   125  // For example:
   126  //
   127  //   - “01+10” and “+01+10” both denote the set of changes
   128  //     with IDs ending with the bits 01 or 10.
   129  //
   130  //   - “01+10-1001” denotes the set of changes with IDs
   131  //     ending with the bits 01 or 10, but excluding those ending in 1001.
   132  //
   133  //   - “-01-1000” and “y-01-1000 both denote the set of all changes
   134  //     with IDs not ending in 01 nor 1000.
   135  //
   136  //   - “0+1-01+001” is not a valid pattern, because all the + operators do not
   137  //     appear before all the - operators.
   138  //
   139  // In the syntaxes described so far, the pattern specifies the changes to
   140  // enable and report. If a pattern is prefixed by a “!”, the meaning
   141  // changes: the pattern specifies the changes to DISABLE and report. This
   142  // mode of operation is needed when a program passes with all changes
   143  // enabled but fails with no changes enabled. In this case, bisect
   144  // searches for minimal sets of changes to disable.
   145  // Put another way, the leading “!” inverts the result from [Matcher.ShouldEnable]
   146  // but does not invert the result from [Matcher.ShouldPrint].
   147  //
   148  // As a convenience for manual debugging, “n” is an alias for “!y”,
   149  // meaning to disable and report all changes.
   150  //
   151  // Finally, a leading “v” in the pattern indicates that the reports will be shown
   152  // to the user of bisect to describe the changes involved in a failure.
   153  // At the API level, the leading “v” causes [Matcher.Visible] to return true.
   154  // See the next section for details.
   155  //
   156  // # Match Reports
   157  //
   158  // The target program must enable only those changed matched
   159  // by the pattern, and it must print a match report for each such change.
   160  // A match report consists of one or more lines of text that will be
   161  // printed by the bisect tool to describe a change implicated in causing
   162  // a failure. Each line in the report for a given change must contain a
   163  // match marker with that change ID, as returned by [Marker].
   164  // The markers are elided when displaying the lines to the user.
   165  //
   166  // A match marker has the form “[bisect-match 0x1234]” where
   167  // 0x1234 is the change ID in hexadecimal.
   168  // An alternate form is “[bisect-match 010101]”, giving the change ID in binary.
   169  //
   170  // When [Matcher.Visible] returns false, the match reports are only
   171  // being processed by bisect to learn the set of enabled changes,
   172  // not shown to the user, meaning that each report can be a match
   173  // marker on a line by itself, eliding the usual textual description.
   174  // When the textual description is expensive to compute,
   175  // checking [Matcher.Visible] can help the avoid that expense
   176  // in most runs.
   177  package bisect
   178  
   179  import (
   180  	"runtime"
   181  	"sync"
   182  	"sync/atomic"
   183  	"unsafe"
   184  )
   185  
   186  // New creates and returns a new Matcher implementing the given pattern.
   187  // The pattern syntax is defined in the package doc comment.
   188  //
   189  // In addition to the pattern syntax syntax, New("") returns nil, nil.
   190  // The nil *Matcher is valid for use: it returns true from ShouldEnable
   191  // and false from ShouldPrint for all changes. Callers can avoid calling
   192  // [Hash], [Matcher.ShouldEnable], and [Matcher.ShouldPrint] entirely
   193  // when they recognize the nil Matcher.
   194  func New(pattern string) (*Matcher, error) {
   195  	if pattern == "" {
   196  		return nil, nil
   197  	}
   198  
   199  	m := new(Matcher)
   200  
   201  	// Allow multiple v, so that “bisect cmd vPATTERN” can force verbose all the time.
   202  	p := pattern
   203  	for len(p) > 0 && p[0] == 'v' {
   204  		m.verbose = true
   205  		p = p[1:]
   206  		if p == "" {
   207  			return nil, &parseError{"invalid pattern syntax: " + pattern}
   208  		}
   209  	}
   210  
   211  	// Allow multiple !, each negating the last, so that “bisect cmd !PATTERN” works
   212  	// even when bisect chooses to add its own !.
   213  	m.enable = true
   214  	for len(p) > 0 && p[0] == '!' {
   215  		m.enable = !m.enable
   216  		p = p[1:]
   217  		if p == "" {
   218  			return nil, &parseError{"invalid pattern syntax: " + pattern}
   219  		}
   220  	}
   221  
   222  	if p == "n" {
   223  		// n is an alias for !y.
   224  		m.enable = !m.enable
   225  		p = "y"
   226  	}
   227  
   228  	// Parse actual pattern syntax.
   229  	result := true
   230  	bits := uint64(0)
   231  	start := 0
   232  	wid := 1 // 1-bit (binary); sometimes 4-bit (hex)
   233  	for i := 0; i <= len(p); i++ {
   234  		// Imagine a trailing - at the end of the pattern to flush final suffix
   235  		c := byte('-')
   236  		if i < len(p) {
   237  			c = p[i]
   238  		}
   239  		if i == start && wid == 1 && c == 'x' { // leading x for hex
   240  			start = i + 1
   241  			wid = 4
   242  			continue
   243  		}
   244  		switch c {
   245  		default:
   246  			return nil, &parseError{"invalid pattern syntax: " + pattern}
   247  		case '2', '3', '4', '5', '6', '7', '8', '9':
   248  			if wid != 4 {
   249  				return nil, &parseError{"invalid pattern syntax: " + pattern}
   250  			}
   251  			fallthrough
   252  		case '0', '1':
   253  			bits <<= wid
   254  			bits |= uint64(c - '0')
   255  		case 'a', 'b', 'c', 'd', 'e', 'f', 'A', 'B', 'C', 'D', 'E', 'F':
   256  			if wid != 4 {
   257  				return nil, &parseError{"invalid pattern syntax: " + pattern}
   258  			}
   259  			bits <<= 4
   260  			bits |= uint64(c&^0x20 - 'A' + 10)
   261  		case 'y':
   262  			if i+1 < len(p) && (p[i+1] == '0' || p[i+1] == '1') {
   263  				return nil, &parseError{"invalid pattern syntax: " + pattern}
   264  			}
   265  			bits = 0
   266  		case '+', '-':
   267  			if c == '+' && result == false {
   268  				// Have already seen a -. Should be - from here on.
   269  				return nil, &parseError{"invalid pattern syntax (+ after -): " + pattern}
   270  			}
   271  			if i > 0 {
   272  				n := (i - start) * wid
   273  				if n > 64 {
   274  					return nil, &parseError{"pattern bits too long: " + pattern}
   275  				}
   276  				if n <= 0 {
   277  					return nil, &parseError{"invalid pattern syntax: " + pattern}
   278  				}
   279  				if p[start] == 'y' {
   280  					n = 0
   281  				}
   282  				mask := uint64(1)<<n - 1
   283  				m.list = append(m.list, cond{mask, bits, result})
   284  			} else if c == '-' {
   285  				// leading - subtracts from complete set
   286  				m.list = append(m.list, cond{0, 0, true})
   287  			}
   288  			bits = 0
   289  			result = c == '+'
   290  			start = i + 1
   291  			wid = 1
   292  		}
   293  	}
   294  	return m, nil
   295  }
   296  
   297  // A Matcher is the parsed, compiled form of a PATTERN string.
   298  // The nil *Matcher is valid: it has all changes enabled but none reported.
   299  type Matcher struct {
   300  	verbose bool
   301  	enable  bool   // when true, list is for “enable and report” (when false, “disable and report”)
   302  	list    []cond // conditions; later ones win over earlier ones
   303  	dedup   atomicPointerDedup
   304  }
   305  
   306  // atomicPointerDedup is an atomic.Pointer[dedup],
   307  // but we are avoiding using Go 1.19's atomic.Pointer
   308  // until the bootstrap toolchain can be relied upon to have it.
   309  type atomicPointerDedup struct {
   310  	p unsafe.Pointer
   311  }
   312  
   313  func (p *atomicPointerDedup) Load() *dedup {
   314  	return (*dedup)(atomic.LoadPointer(&p.p))
   315  }
   316  
   317  func (p *atomicPointerDedup) CompareAndSwap(old, new *dedup) bool {
   318  	return atomic.CompareAndSwapPointer(&p.p, unsafe.Pointer(old), unsafe.Pointer(new))
   319  }
   320  
   321  // A cond is a single condition in the matcher.
   322  // Given an input id, if id&mask == bits, return the result.
   323  type cond struct {
   324  	mask   uint64
   325  	bits   uint64
   326  	result bool
   327  }
   328  
   329  // MarkerOnly reports whether it is okay to print only the marker for
   330  // a given change, omitting the identifying information.
   331  // MarkerOnly returns true when bisect is using the printed reports
   332  // only for an intermediate search step, not for showing to users.
   333  func (m *Matcher) MarkerOnly() bool {
   334  	return !m.verbose
   335  }
   336  
   337  // ShouldEnable reports whether the change with the given id should be enabled.
   338  func (m *Matcher) ShouldEnable(id uint64) bool {
   339  	if m == nil {
   340  		return true
   341  	}
   342  	for i := len(m.list) - 1; i >= 0; i-- {
   343  		c := &m.list[i]
   344  		if id&c.mask == c.bits {
   345  			return c.result == m.enable
   346  		}
   347  	}
   348  	return false == m.enable
   349  }
   350  
   351  // ShouldPrint reports whether to print identifying information about the change with the given id.
   352  func (m *Matcher) ShouldPrint(id uint64) bool {
   353  	if m == nil {
   354  		return false
   355  	}
   356  	for i := len(m.list) - 1; i >= 0; i-- {
   357  		c := &m.list[i]
   358  		if id&c.mask == c.bits {
   359  			return c.result
   360  		}
   361  	}
   362  	return false
   363  }
   364  
   365  // FileLine reports whether the change identified by file and line should be enabled.
   366  // If the change should be printed, FileLine prints a one-line report to w.
   367  func (m *Matcher) FileLine(w Writer, file string, line int) bool {
   368  	if m == nil {
   369  		return true
   370  	}
   371  	return m.fileLine(w, file, line)
   372  }
   373  
   374  // fileLine does the real work for FileLine.
   375  // This lets FileLine's body handle m == nil and potentially be inlined.
   376  func (m *Matcher) fileLine(w Writer, file string, line int) bool {
   377  	h := Hash(file, line)
   378  	if m.ShouldPrint(h) {
   379  		if m.MarkerOnly() {
   380  			PrintMarker(w, h)
   381  		} else {
   382  			printFileLine(w, h, file, line)
   383  		}
   384  	}
   385  	return m.ShouldEnable(h)
   386  }
   387  
   388  // printFileLine prints a non-marker-only report for file:line to w.
   389  func printFileLine(w Writer, h uint64, file string, line int) error {
   390  	const markerLen = 40 // overestimate
   391  	b := make([]byte, 0, markerLen+len(file)+24)
   392  	b = AppendMarker(b, h)
   393  	b = appendFileLine(b, file, line)
   394  	b = append(b, '\n')
   395  	_, err := w.Write(b)
   396  	return err
   397  }
   398  
   399  // appendFileLine appends file:line to dst, returning the extended slice.
   400  func appendFileLine(dst []byte, file string, line int) []byte {
   401  	dst = append(dst, file...)
   402  	dst = append(dst, ':')
   403  	u := uint(line)
   404  	if line < 0 {
   405  		dst = append(dst, '-')
   406  		u = -u
   407  	}
   408  	var buf [24]byte
   409  	i := len(buf)
   410  	for i == len(buf) || u > 0 {
   411  		i--
   412  		buf[i] = '0' + byte(u%10)
   413  		u /= 10
   414  	}
   415  	dst = append(dst, buf[i:]...)
   416  	return dst
   417  }
   418  
   419  // MatchStack assigns the current call stack a change ID.
   420  // If the stack should be printed, MatchStack prints it.
   421  // Then MatchStack reports whether a change at the current call stack should be enabled.
   422  func (m *Matcher) Stack(w Writer) bool {
   423  	if m == nil {
   424  		return true
   425  	}
   426  	return m.stack(w)
   427  }
   428  
   429  // stack does the real work for Stack.
   430  // This lets stack's body handle m == nil and potentially be inlined.
   431  func (m *Matcher) stack(w Writer) bool {
   432  	const maxStack = 16
   433  	var stk [maxStack]uintptr
   434  	n := runtime.Callers(2, stk[:])
   435  	// caller #2 is not for printing; need it to normalize PCs if ASLR.
   436  	if n <= 1 {
   437  		return false
   438  	}
   439  
   440  	base := stk[0]
   441  	// normalize PCs
   442  	for i := range stk[:n] {
   443  		stk[i] -= base
   444  	}
   445  
   446  	h := Hash(stk[:n])
   447  	if m.ShouldPrint(h) {
   448  		var d *dedup
   449  		for {
   450  			d = m.dedup.Load()
   451  			if d != nil {
   452  				break
   453  			}
   454  			d = new(dedup)
   455  			if m.dedup.CompareAndSwap(nil, d) {
   456  				break
   457  			}
   458  		}
   459  
   460  		if m.MarkerOnly() {
   461  			if !d.seenLossy(h) {
   462  				PrintMarker(w, h)
   463  			}
   464  		} else {
   465  			if !d.seen(h) {
   466  				// Restore PCs in stack for printing
   467  				for i := range stk[:n] {
   468  					stk[i] += base
   469  				}
   470  				printStack(w, h, stk[1:n])
   471  			}
   472  		}
   473  	}
   474  	return m.ShouldEnable(h)
   475  
   476  }
   477  
   478  // Writer is the same interface as io.Writer.
   479  // It is duplicated here to avoid importing io.
   480  type Writer interface {
   481  	Write([]byte) (int, error)
   482  }
   483  
   484  // PrintMarker prints to w a one-line report containing only the marker for h.
   485  // It is appropriate to use when [Matcher.ShouldPrint] and [Matcher.MarkerOnly] both return true.
   486  func PrintMarker(w Writer, h uint64) error {
   487  	var buf [50]byte
   488  	b := AppendMarker(buf[:], h)
   489  	b = append(b, '\n')
   490  	_, err := w.Write(b)
   491  	return err
   492  }
   493  
   494  // printStack prints to w a multi-line report containing a formatting of the call stack stk,
   495  // with each line preceded by the marker for h.
   496  func printStack(w Writer, h uint64, stk []uintptr) error {
   497  	buf := make([]byte, 0, 2048)
   498  
   499  	var prefixBuf [100]byte
   500  	prefix := AppendMarker(prefixBuf[:0], h)
   501  
   502  	frames := runtime.CallersFrames(stk)
   503  	for {
   504  		f, more := frames.Next()
   505  		buf = append(buf, prefix...)
   506  		buf = append(buf, f.Func.Name()...)
   507  		buf = append(buf, "()\n"...)
   508  		buf = append(buf, prefix...)
   509  		buf = append(buf, '\t')
   510  		buf = appendFileLine(buf, f.File, f.Line)
   511  		buf = append(buf, '\n')
   512  		if !more {
   513  			break
   514  		}
   515  	}
   516  	buf = append(buf, prefix...)
   517  	buf = append(buf, '\n')
   518  	_, err := w.Write(buf)
   519  	return err
   520  }
   521  
   522  // Marker returns the match marker text to use on any line reporting details
   523  // about a match of the given ID.
   524  // It always returns the hexadecimal format.
   525  func Marker(id uint64) string {
   526  	return string(AppendMarker(nil, id))
   527  }
   528  
   529  // AppendMarker is like [Marker] but appends the marker to dst.
   530  func AppendMarker(dst []byte, id uint64) []byte {
   531  	const prefix = "[bisect-match 0x"
   532  	var buf [len(prefix) + 16 + 1]byte
   533  	copy(buf[:], prefix)
   534  	for i := 0; i < 16; i++ {
   535  		buf[len(prefix)+i] = "0123456789abcdef"[id>>60]
   536  		id <<= 4
   537  	}
   538  	buf[len(prefix)+16] = ']'
   539  	return append(dst, buf[:]...)
   540  }
   541  
   542  // CutMarker finds the first match marker in line and removes it,
   543  // returning the shortened line (with the marker removed),
   544  // the ID from the match marker,
   545  // and whether a marker was found at all.
   546  // If there is no marker, CutMarker returns line, 0, false.
   547  func CutMarker(line string) (short string, id uint64, ok bool) {
   548  	// Find first instance of prefix.
   549  	prefix := "[bisect-match "
   550  	i := 0
   551  	for ; ; i++ {
   552  		if i >= len(line)-len(prefix) {
   553  			return line, 0, false
   554  		}
   555  		if line[i] == '[' && line[i:i+len(prefix)] == prefix {
   556  			break
   557  		}
   558  	}
   559  
   560  	// Scan to ].
   561  	j := i + len(prefix)
   562  	for j < len(line) && line[j] != ']' {
   563  		j++
   564  	}
   565  	if j >= len(line) {
   566  		return line, 0, false
   567  	}
   568  
   569  	// Parse id.
   570  	idstr := line[i+len(prefix) : j]
   571  	if len(idstr) >= 3 && idstr[:2] == "0x" {
   572  		// parse hex
   573  		if len(idstr) > 2+16 { // max 0x + 16 digits
   574  			return line, 0, false
   575  		}
   576  		for i := 2; i < len(idstr); i++ {
   577  			id <<= 4
   578  			switch c := idstr[i]; {
   579  			case '0' <= c && c <= '9':
   580  				id |= uint64(c - '0')
   581  			case 'a' <= c && c <= 'f':
   582  				id |= uint64(c - 'a' + 10)
   583  			case 'A' <= c && c <= 'F':
   584  				id |= uint64(c - 'A' + 10)
   585  			}
   586  		}
   587  	} else {
   588  		if idstr == "" || len(idstr) > 64 { // min 1 digit, max 64 digits
   589  			return line, 0, false
   590  		}
   591  		// parse binary
   592  		for i := 0; i < len(idstr); i++ {
   593  			id <<= 1
   594  			switch c := idstr[i]; c {
   595  			default:
   596  				return line, 0, false
   597  			case '0', '1':
   598  				id |= uint64(c - '0')
   599  			}
   600  		}
   601  	}
   602  
   603  	// Construct shortened line.
   604  	// Remove at most one space from around the marker,
   605  	// so that "foo [marker] bar" shortens to "foo bar".
   606  	j++ // skip ]
   607  	if i > 0 && line[i-1] == ' ' {
   608  		i--
   609  	} else if j < len(line) && line[j] == ' ' {
   610  		j++
   611  	}
   612  	short = line[:i] + line[j:]
   613  	return short, id, true
   614  }
   615  
   616  // Hash computes a hash of the data arguments,
   617  // each of which must be of type string, byte, int, uint, int32, uint32, int64, uint64, uintptr, or a slice of one of those types.
   618  func Hash(data ...any) uint64 {
   619  	h := offset64
   620  	for _, v := range data {
   621  		switch v := v.(type) {
   622  		default:
   623  			// Note: Not printing the type, because reflect.ValueOf(v)
   624  			// would make the interfaces prepared by the caller escape
   625  			// and therefore allocate. This way, Hash(file, line) runs
   626  			// without any allocation. It should be clear from the
   627  			// source code calling Hash what the bad argument was.
   628  			panic("bisect.Hash: unexpected argument type")
   629  		case string:
   630  			h = fnvString(h, v)
   631  		case byte:
   632  			h = fnv(h, v)
   633  		case int:
   634  			h = fnvUint64(h, uint64(v))
   635  		case uint:
   636  			h = fnvUint64(h, uint64(v))
   637  		case int32:
   638  			h = fnvUint32(h, uint32(v))
   639  		case uint32:
   640  			h = fnvUint32(h, v)
   641  		case int64:
   642  			h = fnvUint64(h, uint64(v))
   643  		case uint64:
   644  			h = fnvUint64(h, v)
   645  		case uintptr:
   646  			h = fnvUint64(h, uint64(v))
   647  		case []string:
   648  			for _, x := range v {
   649  				h = fnvString(h, x)
   650  			}
   651  		case []byte:
   652  			for _, x := range v {
   653  				h = fnv(h, x)
   654  			}
   655  		case []int:
   656  			for _, x := range v {
   657  				h = fnvUint64(h, uint64(x))
   658  			}
   659  		case []uint:
   660  			for _, x := range v {
   661  				h = fnvUint64(h, uint64(x))
   662  			}
   663  		case []int32:
   664  			for _, x := range v {
   665  				h = fnvUint32(h, uint32(x))
   666  			}
   667  		case []uint32:
   668  			for _, x := range v {
   669  				h = fnvUint32(h, x)
   670  			}
   671  		case []int64:
   672  			for _, x := range v {
   673  				h = fnvUint64(h, uint64(x))
   674  			}
   675  		case []uint64:
   676  			for _, x := range v {
   677  				h = fnvUint64(h, x)
   678  			}
   679  		case []uintptr:
   680  			for _, x := range v {
   681  				h = fnvUint64(h, uint64(x))
   682  			}
   683  		}
   684  	}
   685  	return h
   686  }
   687  
   688  // Trivial error implementation, here to avoid importing errors.
   689  
   690  // parseError is a trivial error implementation,
   691  // defined here to avoid importing errors.
   692  type parseError struct{ text string }
   693  
   694  func (e *parseError) Error() string { return e.text }
   695  
   696  // FNV-1a implementation. See Go's hash/fnv/fnv.go.
   697  // Copied here for simplicity (can handle integers more directly)
   698  // and to avoid importing hash/fnv.
   699  
   700  const (
   701  	offset64 uint64 = 14695981039346656037
   702  	prime64  uint64 = 1099511628211
   703  )
   704  
   705  func fnv(h uint64, x byte) uint64 {
   706  	h ^= uint64(x)
   707  	h *= prime64
   708  	return h
   709  }
   710  
   711  func fnvString(h uint64, x string) uint64 {
   712  	for i := 0; i < len(x); i++ {
   713  		h ^= uint64(x[i])
   714  		h *= prime64
   715  	}
   716  	return h
   717  }
   718  
   719  func fnvUint64(h uint64, x uint64) uint64 {
   720  	for i := 0; i < 8; i++ {
   721  		h ^= uint64(x & 0xFF)
   722  		x >>= 8
   723  		h *= prime64
   724  	}
   725  	return h
   726  }
   727  
   728  func fnvUint32(h uint64, x uint32) uint64 {
   729  	for i := 0; i < 4; i++ {
   730  		h ^= uint64(x & 0xFF)
   731  		x >>= 8
   732  		h *= prime64
   733  	}
   734  	return h
   735  }
   736  
   737  // A dedup is a deduplicator for call stacks, so that we only print
   738  // a report for new call stacks, not for call stacks we've already
   739  // reported.
   740  //
   741  // It has two modes: an approximate but lock-free mode that
   742  // may still emit some duplicates, and a precise mode that uses
   743  // a lock and never emits duplicates.
   744  type dedup struct {
   745  	// 128-entry 4-way, lossy cache for seenLossy
   746  	recent [128][4]uint64
   747  
   748  	// complete history for seen
   749  	mu sync.Mutex
   750  	m  map[uint64]bool
   751  }
   752  
   753  // seen records that h has now been seen and reports whether it was seen before.
   754  // When seen returns false, the caller is expected to print a report for h.
   755  func (d *dedup) seen(h uint64) bool {
   756  	d.mu.Lock()
   757  	if d.m == nil {
   758  		d.m = make(map[uint64]bool)
   759  	}
   760  	seen := d.m[h]
   761  	d.m[h] = true
   762  	d.mu.Unlock()
   763  	return seen
   764  }
   765  
   766  // seenLossy is a variant of seen that avoids a lock by using a cache of recently seen hashes.
   767  // Each cache entry is N-way set-associative: h can appear in any of the slots.
   768  // If h does not appear in any of them, then it is inserted into a random slot,
   769  // overwriting whatever was there before.
   770  func (d *dedup) seenLossy(h uint64) bool {
   771  	cache := &d.recent[uint(h)%uint(len(d.recent))]
   772  	for i := 0; i < len(cache); i++ {
   773  		if atomic.LoadUint64(&cache[i]) == h {
   774  			return true
   775  		}
   776  	}
   777  
   778  	// Compute index in set to evict as hash of current set.
   779  	ch := offset64
   780  	for _, x := range cache {
   781  		ch = fnvUint64(ch, x)
   782  	}
   783  	atomic.StoreUint64(&cache[uint(ch)%uint(len(cache))], h)
   784  	return false
   785  }