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