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 }