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 }