gitlab.com/Raven-IO/raven-delve@v1.22.4/service/api/prettyprint.go (about)

     1  package api
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  	"math"
     8  	"reflect"
     9  	"strconv"
    10  	"strings"
    11  	"text/tabwriter"
    12  )
    13  
    14  const (
    15  	// strings longer than this will cause slices, arrays and structs to be printed on multiple lines when newlines is enabled
    16  	maxShortStringLen = 7
    17  	// string used for one indentation level (when printing on multiple lines)
    18  	indentString = "\t"
    19  )
    20  
    21  type prettyFlags uint8
    22  
    23  const (
    24  	prettyTop prettyFlags = 1 << iota
    25  	prettyNewlines
    26  	prettyIncludeType
    27  	prettyShortenType
    28  )
    29  
    30  func (flags prettyFlags) top() bool         { return flags&prettyTop != 0 }
    31  func (flags prettyFlags) includeType() bool { return flags&prettyIncludeType != 0 }
    32  func (flags prettyFlags) newlines() bool    { return flags&prettyNewlines != 0 }
    33  func (flags prettyFlags) shortenType() bool { return flags&prettyShortenType != 0 }
    34  
    35  func (flags prettyFlags) set(flag prettyFlags, v bool) prettyFlags {
    36  	if v {
    37  		return flags | flag
    38  	} else {
    39  		return flags &^ flag
    40  	}
    41  }
    42  
    43  // SinglelineString returns a representation of v on a single line.
    44  func (v *Variable) SinglelineString() string {
    45  	var buf bytes.Buffer
    46  	v.writeTo(&buf, prettyTop|prettyIncludeType, "", "")
    47  	return buf.String()
    48  }
    49  
    50  // SinglelineStringWithShortTypes returns a representation of v on a single line, with types shortened.
    51  func (v *Variable) SinglelineStringWithShortTypes() string {
    52  	var buf bytes.Buffer
    53  	v.writeTo(&buf, prettyTop|prettyIncludeType|prettyShortenType, "", "")
    54  	return buf.String()
    55  }
    56  
    57  // SinglelineStringFormatted returns a representation of v on a single line, using the format specified by fmtstr.
    58  func (v *Variable) SinglelineStringFormatted(fmtstr string) string {
    59  	var buf bytes.Buffer
    60  	v.writeTo(&buf, prettyTop|prettyIncludeType, "", fmtstr)
    61  	return buf.String()
    62  }
    63  
    64  // MultilineString returns a representation of v on multiple lines.
    65  func (v *Variable) MultilineString(indent, fmtstr string) string {
    66  	var buf bytes.Buffer
    67  	v.writeTo(&buf, prettyTop|prettyNewlines|prettyIncludeType, indent, fmtstr)
    68  	return buf.String()
    69  }
    70  
    71  func (v *Variable) typeStr(flags prettyFlags) string {
    72  	if flags.shortenType() {
    73  		return ShortenType(v.Type)
    74  	}
    75  
    76  	return v.Type
    77  }
    78  
    79  func (v *Variable) writeTo(buf io.Writer, flags prettyFlags, indent, fmtstr string) {
    80  	if v.Unreadable != "" {
    81  		fmt.Fprintf(buf, "(unreadable %s)", v.Unreadable)
    82  		return
    83  	}
    84  
    85  	if !flags.top() && v.Addr == 0 && v.Value == "" {
    86  		if flags.includeType() && v.Type != "void" {
    87  			fmt.Fprintf(buf, "%s nil", v.typeStr(flags))
    88  		} else {
    89  			fmt.Fprint(buf, "nil")
    90  		}
    91  		return
    92  	}
    93  
    94  	switch v.Kind {
    95  	case reflect.Slice:
    96  		v.writeSliceTo(buf, flags, indent, fmtstr)
    97  	case reflect.Array:
    98  		v.writeArrayTo(buf, flags, indent, fmtstr)
    99  	case reflect.Ptr:
   100  		if v.Type == "" || len(v.Children) == 0 {
   101  			fmt.Fprint(buf, "nil")
   102  		} else if v.Children[0].OnlyAddr && v.Children[0].Addr != 0 {
   103  			v.writePointerTo(buf, flags)
   104  		} else {
   105  			if flags.top() && flags.newlines() && v.Children[0].Addr != 0 {
   106  				v.writePointerTo(buf, flags)
   107  				fmt.Fprint(buf, "\n")
   108  			}
   109  			fmt.Fprint(buf, "*")
   110  			v.Children[0].writeTo(buf, flags.set(prettyTop, false), indent, fmtstr)
   111  		}
   112  	case reflect.UnsafePointer:
   113  		if len(v.Children) == 0 {
   114  			fmt.Fprintf(buf, "unsafe.Pointer(nil)")
   115  		} else {
   116  			fmt.Fprintf(buf, "unsafe.Pointer(%#x)", v.Children[0].Addr)
   117  		}
   118  	case reflect.Chan:
   119  		if flags.newlines() {
   120  			v.writeStructTo(buf, flags, indent, fmtstr)
   121  		} else {
   122  			if len(v.Children) == 0 {
   123  				fmt.Fprintf(buf, "%s nil", v.typeStr(flags))
   124  			} else {
   125  				fmt.Fprintf(buf, "%s %s/%s", v.typeStr(flags), v.Children[0].Value, v.Children[1].Value)
   126  			}
   127  		}
   128  	case reflect.Struct:
   129  		if v.Value != "" {
   130  			fmt.Fprintf(buf, "%s(%s)", v.typeStr(flags), v.Value)
   131  			flags = flags.set(prettyIncludeType, false)
   132  		}
   133  		v.writeStructTo(buf, flags, indent, fmtstr)
   134  	case reflect.Interface:
   135  		if v.Addr == 0 {
   136  			// an escaped interface variable that points to nil, this shouldn't
   137  			// happen in normal code but can happen if the variable is out of scope.
   138  			fmt.Fprintf(buf, "nil")
   139  			return
   140  		}
   141  		if flags.includeType() {
   142  			if v.Children[0].Kind == reflect.Invalid {
   143  				fmt.Fprintf(buf, "%s ", v.typeStr(flags))
   144  				if v.Children[0].Addr == 0 {
   145  					fmt.Fprint(buf, "nil")
   146  					return
   147  				}
   148  			} else {
   149  				fmt.Fprintf(buf, "%s(%s) ", v.typeStr(flags), v.Children[0].Type)
   150  			}
   151  		}
   152  		data := v.Children[0]
   153  		if data.Kind == reflect.Ptr {
   154  			if len(data.Children) == 0 {
   155  				fmt.Fprint(buf, "...")
   156  			} else if data.Children[0].Addr == 0 {
   157  				fmt.Fprint(buf, "nil")
   158  			} else if data.Children[0].OnlyAddr {
   159  				fmt.Fprintf(buf, "0x%x", v.Children[0].Addr)
   160  			} else {
   161  				v.Children[0].writeTo(buf, flags.set(prettyTop, false).set(prettyIncludeType, !flags.includeType()), indent, fmtstr)
   162  			}
   163  		} else if data.OnlyAddr {
   164  			if strings.Contains(v.Type, "/") {
   165  				fmt.Fprintf(buf, "*(*%q)(%#x)", v.typeStr(flags), v.Addr)
   166  			} else {
   167  				fmt.Fprintf(buf, "*(*%s)(%#x)", v.typeStr(flags), v.Addr)
   168  			}
   169  		} else {
   170  			v.Children[0].writeTo(buf, flags.set(prettyTop, false).set(prettyIncludeType, !flags.includeType()), indent, fmtstr)
   171  		}
   172  	case reflect.Map:
   173  		v.writeMapTo(buf, flags, indent, fmtstr)
   174  	case reflect.Func:
   175  		if v.Value == "" {
   176  			fmt.Fprint(buf, "nil")
   177  		} else {
   178  			fmt.Fprintf(buf, "%s", v.Value)
   179  			if flags.newlines() && len(v.Children) > 0 {
   180  				fmt.Fprintf(buf, " {\n")
   181  				for i := range v.Children {
   182  					fmt.Fprintf(buf, "%s%s%s %s = ", indent, indentString, v.Children[i].Name, v.Children[i].typeStr(flags))
   183  					v.Children[i].writeTo(buf, flags.set(prettyTop, false).set(prettyIncludeType, false), indent+indentString, fmtstr)
   184  					fmt.Fprintf(buf, "\n")
   185  				}
   186  				fmt.Fprintf(buf, "%s}", indent)
   187  			}
   188  		}
   189  	default:
   190  		v.writeBasicType(buf, fmtstr)
   191  	}
   192  }
   193  
   194  func (v *Variable) writePointerTo(buf io.Writer, flags prettyFlags) {
   195  	if strings.Contains(v.Type, "/") {
   196  		fmt.Fprintf(buf, "(%q)(%#x)", v.typeStr(flags), v.Children[0].Addr)
   197  	} else {
   198  		fmt.Fprintf(buf, "(%s)(%#x)", v.typeStr(flags), v.Children[0].Addr)
   199  	}
   200  }
   201  
   202  func (v *Variable) writeBasicType(buf io.Writer, fmtstr string) {
   203  	if v.Value == "" && v.Kind != reflect.String {
   204  		fmt.Fprintf(buf, "(unknown %s)", v.Kind)
   205  		return
   206  	}
   207  
   208  	switch v.Kind {
   209  	case reflect.Bool:
   210  		if fmtstr == "" {
   211  			buf.Write([]byte(v.Value))
   212  			return
   213  		}
   214  		var b bool = v.Value == "true"
   215  		fmt.Fprintf(buf, fmtstr, b)
   216  
   217  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
   218  		if fmtstr == "" {
   219  			buf.Write([]byte(v.Value))
   220  			return
   221  		}
   222  		n, _ := strconv.ParseInt(ExtractIntValue(v.Value), 10, 64)
   223  		fmt.Fprintf(buf, fmtstr, n)
   224  
   225  	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
   226  		if fmtstr == "" {
   227  			buf.Write([]byte(v.Value))
   228  			return
   229  		}
   230  		n, _ := strconv.ParseUint(ExtractIntValue(v.Value), 10, 64)
   231  		fmt.Fprintf(buf, fmtstr, n)
   232  
   233  	case reflect.Float32, reflect.Float64:
   234  		if fmtstr == "" {
   235  			buf.Write([]byte(v.Value))
   236  			return
   237  		}
   238  		x, _ := strconv.ParseFloat(v.Value, 64)
   239  		fmt.Fprintf(buf, fmtstr, x)
   240  
   241  	case reflect.Complex64, reflect.Complex128:
   242  		if fmtstr == "" {
   243  			fmt.Fprintf(buf, "(%s + %si)", v.Children[0].Value, v.Children[1].Value)
   244  			return
   245  		}
   246  		real, _ := strconv.ParseFloat(v.Children[0].Value, 64)
   247  		imag, _ := strconv.ParseFloat(v.Children[1].Value, 64)
   248  		var x complex128 = complex(real, imag)
   249  		fmt.Fprintf(buf, fmtstr, x)
   250  
   251  	case reflect.String:
   252  		if fmtstr == "" {
   253  			s := v.Value
   254  			if len(s) != int(v.Len) {
   255  				s = fmt.Sprintf("%s...+%d more", s, int(v.Len)-len(s))
   256  			}
   257  			fmt.Fprintf(buf, "%q", s)
   258  			return
   259  		}
   260  		fmt.Fprintf(buf, fmtstr, v.Value)
   261  	}
   262  }
   263  
   264  func ExtractIntValue(s string) string {
   265  	if s == "" || s[len(s)-1] != ')' {
   266  		return s
   267  	}
   268  	open := strings.LastIndex(s, "(")
   269  	if open < 0 {
   270  		return s
   271  	}
   272  	return s[open+1 : len(s)-1]
   273  }
   274  
   275  func (v *Variable) writeSliceTo(buf io.Writer, flags prettyFlags, indent, fmtstr string) {
   276  	if flags.includeType() {
   277  		fmt.Fprintf(buf, "%s len: %d, cap: %d, ", v.typeStr(flags), v.Len, v.Cap)
   278  	}
   279  	if v.Base == 0 && len(v.Children) == 0 {
   280  		fmt.Fprintf(buf, "nil")
   281  		return
   282  	}
   283  	v.writeSliceOrArrayTo(buf, flags, indent, fmtstr)
   284  }
   285  
   286  func (v *Variable) writeArrayTo(buf io.Writer, flags prettyFlags, indent, fmtstr string) {
   287  	if flags.includeType() {
   288  		fmt.Fprintf(buf, "%s ", v.typeStr(flags))
   289  	}
   290  	v.writeSliceOrArrayTo(buf, flags, indent, fmtstr)
   291  }
   292  
   293  func (v *Variable) writeStructTo(buf io.Writer, flags prettyFlags, indent, fmtstr string) {
   294  	if int(v.Len) != len(v.Children) && len(v.Children) == 0 {
   295  		if strings.Contains(v.Type, "/") {
   296  			fmt.Fprintf(buf, "(*%q)(%#x)", v.typeStr(flags), v.Addr)
   297  		} else {
   298  			fmt.Fprintf(buf, "(*%s)(%#x)", v.typeStr(flags), v.Addr)
   299  		}
   300  		return
   301  	}
   302  
   303  	if flags.includeType() {
   304  		fmt.Fprintf(buf, "%s ", v.typeStr(flags))
   305  	}
   306  
   307  	nl := v.shouldNewlineStruct(flags.newlines())
   308  
   309  	fmt.Fprint(buf, "{")
   310  
   311  	for i := range v.Children {
   312  		if nl {
   313  			fmt.Fprintf(buf, "\n%s%s", indent, indentString)
   314  		}
   315  		fmt.Fprintf(buf, "%s: ", v.Children[i].Name)
   316  		v.Children[i].writeTo(buf, prettyIncludeType.set(prettyNewlines, nl), indent+indentString, fmtstr)
   317  		if i != len(v.Children)-1 || nl {
   318  			fmt.Fprint(buf, ",")
   319  			if !nl {
   320  				fmt.Fprint(buf, " ")
   321  			}
   322  		}
   323  	}
   324  
   325  	if len(v.Children) != int(v.Len) {
   326  		if nl {
   327  			fmt.Fprintf(buf, "\n%s%s", indent, indentString)
   328  		} else {
   329  			fmt.Fprint(buf, ",")
   330  		}
   331  		fmt.Fprintf(buf, "...+%d more", int(v.Len)-len(v.Children))
   332  	}
   333  
   334  	fmt.Fprint(buf, "}")
   335  }
   336  
   337  func (v *Variable) writeMapTo(buf io.Writer, flags prettyFlags, indent, fmtstr string) {
   338  	if flags.includeType() {
   339  		fmt.Fprintf(buf, "%s ", v.typeStr(flags))
   340  	}
   341  	if v.Base == 0 && len(v.Children) == 0 {
   342  		fmt.Fprintf(buf, "nil")
   343  		return
   344  	}
   345  
   346  	nl := flags.newlines() && (len(v.Children) > 0)
   347  
   348  	fmt.Fprint(buf, "[")
   349  
   350  	for i := 0; i < len(v.Children); i += 2 {
   351  		key := &v.Children[i]
   352  		value := &v.Children[i+1]
   353  
   354  		if nl {
   355  			fmt.Fprintf(buf, "\n%s%s", indent, indentString)
   356  		}
   357  
   358  		key.writeTo(buf, 0, indent+indentString, fmtstr)
   359  		fmt.Fprint(buf, ": ")
   360  		value.writeTo(buf, prettyFlags(0).set(prettyNewlines, nl), indent+indentString, fmtstr)
   361  		if i != len(v.Children)-1 || nl {
   362  			fmt.Fprint(buf, ", ")
   363  		}
   364  	}
   365  
   366  	if len(v.Children)/2 != int(v.Len) {
   367  		if len(v.Children) != 0 {
   368  			if nl {
   369  				fmt.Fprintf(buf, "\n%s%s", indent, indentString)
   370  			} else {
   371  				fmt.Fprint(buf, ",")
   372  			}
   373  			fmt.Fprintf(buf, "...+%d more", int(v.Len)-(len(v.Children)/2))
   374  		} else {
   375  			fmt.Fprint(buf, "...")
   376  		}
   377  	}
   378  
   379  	if nl {
   380  		fmt.Fprintf(buf, "\n%s", indent)
   381  	}
   382  	fmt.Fprint(buf, "]")
   383  }
   384  
   385  func (v *Variable) shouldNewlineArray(newlines bool) bool {
   386  	if !newlines || len(v.Children) == 0 {
   387  		return false
   388  	}
   389  
   390  	kind, hasptr := (&v.Children[0]).recursiveKind()
   391  
   392  	switch kind {
   393  	case reflect.Slice, reflect.Array, reflect.Struct, reflect.Map, reflect.Interface:
   394  		return true
   395  	case reflect.String:
   396  		if hasptr {
   397  			return true
   398  		}
   399  		for i := range v.Children {
   400  			if len(v.Children[i].Value) > maxShortStringLen {
   401  				return true
   402  			}
   403  		}
   404  		return false
   405  	default:
   406  		return false
   407  	}
   408  }
   409  
   410  func (v *Variable) recursiveKind() (reflect.Kind, bool) {
   411  	hasptr := false
   412  	var kind reflect.Kind
   413  	for {
   414  		kind = v.Kind
   415  		if kind == reflect.Ptr {
   416  			hasptr = true
   417  			if len(v.Children) == 0 {
   418  				return kind, hasptr
   419  			}
   420  			v = &(v.Children[0])
   421  		} else {
   422  			break
   423  		}
   424  	}
   425  	return kind, hasptr
   426  }
   427  
   428  func (v *Variable) shouldNewlineStruct(newlines bool) bool {
   429  	if !newlines || len(v.Children) == 0 {
   430  		return false
   431  	}
   432  
   433  	for i := range v.Children {
   434  		kind, hasptr := (&v.Children[i]).recursiveKind()
   435  
   436  		switch kind {
   437  		case reflect.Slice, reflect.Array, reflect.Struct, reflect.Map, reflect.Interface:
   438  			return true
   439  		case reflect.String:
   440  			if hasptr {
   441  				return true
   442  			}
   443  			if len(v.Children[i].Value) > maxShortStringLen {
   444  				return true
   445  			}
   446  		}
   447  	}
   448  
   449  	return false
   450  }
   451  
   452  func (v *Variable) writeSliceOrArrayTo(buf io.Writer, flags prettyFlags, indent, fmtstr string) {
   453  	nl := v.shouldNewlineArray(flags.newlines())
   454  	fmt.Fprint(buf, "[")
   455  
   456  	for i := range v.Children {
   457  		if nl {
   458  			fmt.Fprintf(buf, "\n%s%s", indent, indentString)
   459  		}
   460  		v.Children[i].writeTo(buf, prettyFlags(0).set(prettyNewlines, nl), indent+indentString, fmtstr)
   461  		if i != len(v.Children)-1 || nl {
   462  			fmt.Fprint(buf, ",")
   463  		}
   464  	}
   465  
   466  	if len(v.Children) != int(v.Len) {
   467  		if len(v.Children) != 0 {
   468  			if nl {
   469  				fmt.Fprintf(buf, "\n%s%s", indent, indentString)
   470  			} else {
   471  				fmt.Fprint(buf, ",")
   472  			}
   473  			fmt.Fprintf(buf, "...+%d more", int(v.Len)-len(v.Children))
   474  		} else {
   475  			fmt.Fprint(buf, "...")
   476  		}
   477  	}
   478  
   479  	if nl {
   480  		fmt.Fprintf(buf, "\n%s", indent)
   481  	}
   482  
   483  	fmt.Fprint(buf, "]")
   484  }
   485  
   486  // PrettyExamineMemory examine the memory and format data
   487  //
   488  // `format` specifies the data format (or data type), `size` specifies size of each data,
   489  // like 4byte integer, 1byte character, etc. `count` specifies the number of values.
   490  func PrettyExamineMemory(address uintptr, memArea []byte, isLittleEndian bool, format byte, size int) string {
   491  
   492  	var (
   493  		cols      int
   494  		colFormat string
   495  		colBytes  = size
   496  
   497  		addrLen int
   498  		addrFmt string
   499  	)
   500  
   501  	// Different versions of golang output differently about '#'.
   502  	// See https://ci.appveyor.com/project/derekparker/delve-facy3/builds/30179356.
   503  	switch format {
   504  	case 'b':
   505  		cols = 4 // Avoid emitting rows that are too long when using binary format
   506  		colFormat = fmt.Sprintf("%%0%db", colBytes*8)
   507  	case 'o':
   508  		cols = 8
   509  		colFormat = fmt.Sprintf("0%%0%do", colBytes*3) // Always keep one leading zero for octal.
   510  	case 'd':
   511  		cols = 8
   512  		colFormat = fmt.Sprintf("%%0%dd", colBytes*3)
   513  	case 'x':
   514  		cols = 8
   515  		colFormat = fmt.Sprintf("0x%%0%dx", colBytes*2) // Always keep one leading '0x' for hex.
   516  	default:
   517  		return fmt.Sprintf("not supported format %q\n", string(format))
   518  	}
   519  	colFormat += "\t"
   520  
   521  	l := len(memArea)
   522  	rows := l / (cols * colBytes)
   523  	if l%(cols*colBytes) != 0 {
   524  		rows++
   525  	}
   526  
   527  	// Avoid the lens of two adjacent address are different, so always use the last addr's len to format.
   528  	if l != 0 {
   529  		addrLen = len(fmt.Sprintf("%x", uint64(address)+uint64(l)))
   530  	}
   531  	addrFmt = "0x%0" + strconv.Itoa(addrLen) + "x:\t"
   532  
   533  	var b strings.Builder
   534  	w := tabwriter.NewWriter(&b, 0, 0, 3, ' ', 0)
   535  
   536  	for i := 0; i < rows; i++ {
   537  		fmt.Fprintf(w, addrFmt, address)
   538  
   539  		for j := 0; j < cols; j++ {
   540  			offset := i*(cols*colBytes) + j*colBytes
   541  			if offset+colBytes <= len(memArea) {
   542  				n := byteArrayToUInt64(memArea[offset:offset+colBytes], isLittleEndian)
   543  				fmt.Fprintf(w, colFormat, n)
   544  			}
   545  		}
   546  		fmt.Fprintln(w, "")
   547  		address += uintptr(cols * colBytes)
   548  	}
   549  	w.Flush()
   550  	return b.String()
   551  }
   552  
   553  func byteArrayToUInt64(buf []byte, isLittleEndian bool) uint64 {
   554  	var n uint64
   555  	if isLittleEndian {
   556  		for i := len(buf) - 1; i >= 0; i-- {
   557  			n = n<<8 + uint64(buf[i])
   558  		}
   559  	} else {
   560  		for i := 0; i < len(buf); i++ {
   561  			n = n<<8 + uint64(buf[i])
   562  		}
   563  	}
   564  	return n
   565  }
   566  
   567  const stacktraceTruncatedMessage = "(truncated)"
   568  
   569  func digits(n int) int {
   570  	if n <= 0 {
   571  		return 1
   572  	}
   573  	return int(math.Floor(math.Log10(float64(n)))) + 1
   574  }
   575  
   576  type StackTraceColors struct {
   577  	FunctionColor string
   578  	BasenameColor string
   579  	NormalColor   string
   580  }
   581  
   582  func PrintStack(formatPath func(string) string, out io.Writer, stack []Stackframe, ind string, offsets bool, stc StackTraceColors, include func(Stackframe) bool) {
   583  	if len(stack) == 0 {
   584  		return
   585  	}
   586  
   587  	extranl := offsets
   588  	for i := range stack {
   589  		if extranl {
   590  			break
   591  		}
   592  		extranl = extranl || (len(stack[i].Defers) > 0) || (len(stack[i].Arguments) > 0) || (len(stack[i].Locals) > 0)
   593  	}
   594  
   595  	fileLine := func(file string, line int) string {
   596  		file = formatPath(file)
   597  		if stc.BasenameColor == "" {
   598  			return fmt.Sprintf("%s:%d", file, line)
   599  		}
   600  		slash := strings.LastIndex(file, "/")
   601  		if slash < 0 {
   602  			slash = 0
   603  		} else {
   604  			slash++
   605  		}
   606  		return fmt.Sprintf("%s%s%s:%d%s", file[:slash], stc.BasenameColor, file[slash:], line, stc.NormalColor)
   607  	}
   608  
   609  	d := digits(len(stack) - 1)
   610  	fmtstr := "%s%" + strconv.Itoa(d) + "d  0x%016x in " + stc.FunctionColor + "%s" + stc.NormalColor + "\n"
   611  	s := ind + strings.Repeat(" ", d+2+len(ind))
   612  
   613  	for i := range stack {
   614  		if !include(stack[i]) {
   615  			continue
   616  		}
   617  		if stack[i].Err != "" {
   618  			fmt.Fprintf(out, "%serror: %s\n", s, stack[i].Err)
   619  			continue
   620  		}
   621  		fmt.Fprintf(out, fmtstr, ind, i, stack[i].PC, stack[i].Function.Name())
   622  		fmt.Fprintf(out, "%sat %s\n", s, fileLine(stack[i].File, stack[i].Line))
   623  
   624  		if offsets {
   625  			fmt.Fprintf(out, "%sframe: %+#x frame pointer %+#x\n", s, stack[i].FrameOffset, stack[i].FramePointerOffset)
   626  		}
   627  
   628  		for j, d := range stack[i].Defers {
   629  			deferHeader := fmt.Sprintf("%s    defer %d: ", s, j+1)
   630  			s2 := strings.Repeat(" ", len(deferHeader))
   631  			if d.Unreadable != "" {
   632  				fmt.Fprintf(out, "%s(unreadable defer: %s)\n", deferHeader, d.Unreadable)
   633  				continue
   634  			}
   635  			fmt.Fprintf(out, "%s%#016x in %s%s%s\n", deferHeader, d.DeferredLoc.PC, stc.FunctionColor, d.DeferredLoc.Function.Name(), stc.NormalColor)
   636  			fmt.Fprintf(out, "%sat %s:%d\n", s2, formatPath(d.DeferredLoc.File), d.DeferredLoc.Line)
   637  			fmt.Fprintf(out, "%sdeferred by %s at %s\n", s2, d.DeferLoc.Function.Name(), fileLine(d.DeferLoc.File, d.DeferLoc.Line))
   638  		}
   639  
   640  		for j := range stack[i].Arguments {
   641  			fmt.Fprintf(out, "%s    %s = %s\n", s, stack[i].Arguments[j].Name, stack[i].Arguments[j].SinglelineString())
   642  		}
   643  		for j := range stack[i].Locals {
   644  			fmt.Fprintf(out, "%s    %s = %s\n", s, stack[i].Locals[j].Name, stack[i].Locals[j].SinglelineString())
   645  		}
   646  
   647  		if extranl {
   648  			fmt.Fprintln(out)
   649  		}
   650  	}
   651  
   652  	if len(stack) > 0 && !stack[len(stack)-1].Bottom {
   653  		fmt.Fprintf(out, "%s"+stacktraceTruncatedMessage+"\n", ind)
   654  	}
   655  }