github.com/ks888/tgo@v0.0.0-20190130135156-80bf89407292/tracee/process.go (about) 1 package tracee 2 3 import ( 4 "debug/dwarf" 5 "encoding/binary" 6 "fmt" 7 "sort" 8 "strings" 9 10 "github.com/ks888/tgo/debugapi" 11 "github.com/ks888/tgo/log" 12 "golang.org/x/arch/x86/x86asm" 13 ) 14 15 var breakpointInsts = []byte{0xcc} 16 17 type breakpoint struct { 18 addr uint64 19 orgInsts []byte 20 } 21 22 // Process represents the tracee process launched by or attached to this tracer. 23 type Process struct { 24 debugapiClient *debugapi.Client 25 breakpoints map[uint64]breakpoint 26 Binary BinaryFile 27 GoVersion GoVersion 28 moduleDataList []*moduleData 29 valueParser valueParser 30 } 31 32 const countDisabled = -1 33 34 // StackFrame describes the data in the stack frame and its associated function. 35 type StackFrame struct { 36 Function *Function 37 InputArguments []Argument 38 OutputArguments []Argument 39 ReturnAddress uint64 40 } 41 42 // Attributes specifies the set of tracee's attributes. 43 type Attributes struct { 44 ProgramPath string 45 CompiledGoVersion string 46 FirstModuleDataAddr uint64 47 } 48 49 // LaunchProcess launches new tracee process. 50 func LaunchProcess(name string, arg []string, attrs Attributes) (*Process, error) { 51 debugapiClient := debugapi.NewClient() 52 if err := debugapiClient.LaunchProcess(name, arg...); err != nil { 53 return nil, err 54 } 55 56 if attrs.ProgramPath == "" { 57 attrs.ProgramPath = name 58 } 59 proc, err := newProcess(debugapiClient, attrs) 60 if err != nil { 61 debugapiClient.DetachProcess() 62 } 63 return proc, err 64 } 65 66 // AttachProcess attaches to the existing tracee process. 67 func AttachProcess(pid int, attrs Attributes) (*Process, error) { 68 debugapiClient := debugapi.NewClient() 69 err := debugapiClient.AttachProcess(pid) 70 if err != nil { 71 return nil, err 72 } 73 74 proc, err := newProcess(debugapiClient, attrs) 75 if err != nil { 76 debugapiClient.DetachProcess() // keep the attached process running 77 } 78 return proc, err 79 } 80 81 func newProcess(debugapiClient *debugapi.Client, attrs Attributes) (*Process, error) { 82 proc := &Process{debugapiClient: debugapiClient, breakpoints: make(map[uint64]breakpoint)} 83 84 proc.GoVersion = ParseGoVersion(attrs.CompiledGoVersion) 85 var err error 86 proc.Binary, err = OpenBinaryFile(attrs.ProgramPath, proc.GoVersion) 87 if err != nil { 88 return nil, err 89 } 90 proc.moduleDataList = parseModuleDataList(attrs.FirstModuleDataAddr, proc.Binary.moduleDataType(), debugapiClient) 91 proc.valueParser = valueParser{reader: debugapiClient, mapRuntimeType: proc.mapRuntimeType} 92 return proc, nil 93 } 94 95 func parseModuleDataList(firstModuleDataAddr uint64, moduleDataType dwarf.Type, reader memoryReader) (moduleDataList []*moduleData) { 96 moduleDataAddr := firstModuleDataAddr 97 for moduleDataAddr != 0 { 98 md := newModuleData(moduleDataAddr, moduleDataType) 99 moduleDataList = append(moduleDataList, md) 100 101 moduleDataAddr = md.next(reader) 102 } 103 return 104 } 105 106 func (p *Process) mapRuntimeType(runtimeTypeAddr uint64) (dwarf.Type, error) { 107 var md *moduleData 108 var reader memoryReader = p.debugapiClient 109 for _, candidate := range p.moduleDataList { 110 if candidate.types(reader) <= runtimeTypeAddr && runtimeTypeAddr < candidate.etypes(reader) { 111 md = candidate 112 break 113 } 114 } 115 116 return p.Binary.findDwarfTypeByAddr(runtimeTypeAddr - md.types(reader)) 117 } 118 119 // Detach detaches from the tracee process. All breakpoints are cleared. 120 func (p *Process) Detach() error { 121 for breakpointAddr := range p.breakpoints { 122 if err := p.ClearBreakpoint(breakpointAddr); err != nil { 123 // the process may have exited already 124 log.Debugf("failed to clear breakpoint at %#x: %v", breakpointAddr, err) 125 } 126 } 127 128 if err := p.debugapiClient.DetachProcess(); err != nil { 129 return err 130 } 131 132 return p.close() 133 } 134 135 func (p *Process) close() error { 136 return p.Binary.Close() 137 } 138 139 // ContinueAndWait continues the execution and waits until an event happens. 140 // Note that the id of the stopped thread may be different from the id of the continued thread. 141 func (p *Process) ContinueAndWait() (debugapi.Event, error) { 142 event, err := p.debugapiClient.ContinueAndWait() 143 if debugapi.IsExitEvent(event.Type) { 144 err = p.close() 145 } 146 return event, err 147 } 148 149 // SingleStep executes one instruction while clearing and setting breakpoints. 150 // If not all the threads are stopped, there is some possibility that another thread 151 // passes through the breakpoint while single-stepping. 152 func (p *Process) SingleStep(threadID int, trappedAddr uint64) error { 153 if err := p.setPC(threadID, trappedAddr); err != nil { 154 return err 155 } 156 157 bp, bpSet := p.breakpoints[trappedAddr] 158 if bpSet { 159 if err := p.debugapiClient.WriteMemory(trappedAddr, bp.orgInsts); err != nil { 160 return err 161 } 162 } 163 164 if _, err := p.stepAndWait(threadID); err != nil { 165 unspecifiedError, ok := err.(debugapi.UnspecifiedThreadError) 166 if !ok { 167 return err 168 } 169 170 if err := p.singleStepUnspecifiedThreads(threadID, unspecifiedError); err != nil { 171 return err 172 } 173 return p.SingleStep(threadID, trappedAddr) 174 } 175 176 if bpSet { 177 return p.debugapiClient.WriteMemory(trappedAddr, breakpointInsts) 178 } 179 return nil 180 } 181 182 func (p *Process) setPC(threadID int, addr uint64) error { 183 regs, err := p.debugapiClient.ReadRegisters(threadID) 184 if err != nil { 185 return err 186 } 187 188 regs.Rip = addr 189 return p.debugapiClient.WriteRegisters(threadID, regs) 190 } 191 192 func (p *Process) stepAndWait(threadID int) (event debugapi.Event, err error) { 193 event, err = p.debugapiClient.StepAndWait(threadID) 194 if debugapi.IsExitEvent(event.Type) { 195 err = p.close() 196 } 197 return event, err 198 } 199 200 // SetBreakpoint sets the breakpoint at the specified address. 201 func (p *Process) SetBreakpoint(addr uint64) error { 202 _, ok := p.breakpoints[addr] 203 if ok { 204 return nil 205 } 206 207 originalInsts := make([]byte, len(breakpointInsts)) 208 if err := p.debugapiClient.ReadMemory(addr, originalInsts); err != nil { 209 return err 210 } 211 if err := p.debugapiClient.WriteMemory(addr, breakpointInsts); err != nil { 212 return err 213 } 214 215 p.breakpoints[addr] = breakpoint{addr, originalInsts} 216 return nil 217 } 218 219 // ClearBreakpoint clears the breakpoint at the specified address. 220 func (p *Process) ClearBreakpoint(addr uint64) error { 221 bp, ok := p.breakpoints[addr] 222 if !ok { 223 return nil 224 } 225 226 if err := p.debugapiClient.WriteMemory(addr, bp.orgInsts); err != nil { 227 return err 228 } 229 230 delete(p.breakpoints, addr) 231 return nil 232 } 233 234 // ExistBreakpoint returns true if the the breakpoint is already set at the specified address. 235 func (p *Process) ExistBreakpoint(addr uint64) bool { 236 _, ok := p.breakpoints[addr] 237 return ok 238 } 239 240 // StackFrameAt returns the stack frame to which the given rbp specified. 241 // To get the correct stack frame, it assumes: 242 // * rsp points to the return address. 243 // * rsp+8 points to the beginning of the args list. 244 // 245 // To be accurate, we need to check the .debug_frame section to find the CFA and return address. 246 // But we omit the check here because this function is called at only the beginning or end of the tracee's function call. 247 func (p *Process) StackFrameAt(rsp, rip uint64) (*StackFrame, error) { 248 function, err := p.FindFunction(rip) 249 if err != nil { 250 return nil, err 251 } 252 253 buff := make([]byte, 8) 254 if err := p.debugapiClient.ReadMemory(rsp, buff); err != nil { 255 return nil, err 256 } 257 retAddr := binary.LittleEndian.Uint64(buff) 258 259 inputArgs, outputArgs, err := p.currentArgs(function.Parameters, rsp+8) 260 if err != nil { 261 return nil, err 262 } 263 264 return &StackFrame{ 265 Function: function, 266 ReturnAddress: retAddr, 267 InputArguments: inputArgs, 268 OutputArguments: outputArgs, 269 }, nil 270 } 271 272 // FindFunction finds the function to which pc specifies. 273 func (p *Process) FindFunction(pc uint64) (*Function, error) { 274 function, err := p.Binary.FindFunction(pc) 275 if err == nil { 276 p.fillInOutputParameters(pc, function.Parameters) 277 p.fillInUnknownParameter(pc, function.Parameters) 278 return function, err 279 } 280 281 return p.findFunctionByModuleData(pc) 282 } 283 284 func (p *Process) fillInOutputParameters(pc uint64, params []Parameter) { 285 if !p.canFillInOutputParameters(pc, params) { 286 return 287 } 288 289 p.doFillInOutputParameters(pc, params) 290 291 sort.Slice(params, func(i, j int) bool { return params[i].Offset < params[j].Offset }) 292 return 293 } 294 295 func (p *Process) canFillInOutputParameters(pc uint64, params []Parameter) bool { 296 for _, param := range params { 297 if param.IsOutput { 298 if param.Exist || !strings.HasPrefix(param.Name, "~r") { 299 return false 300 } 301 } 302 } 303 304 if !p.noPadding(pc, params) { 305 // It may be dangerous to fill in the parameter's location due to the alignment. 306 return false 307 } 308 return true 309 } 310 311 func (p *Process) doFillInOutputParameters(pc uint64, params []Parameter) { 312 var outputIndexes []int 313 var totalSize, totalOutputSize int 314 for i, param := range params { 315 if param.IsOutput { 316 outputIndexes = append(outputIndexes, i) 317 totalOutputSize += int(param.Typ.Size()) 318 } 319 totalSize += int(param.Typ.Size()) 320 } 321 322 sort.Slice(outputIndexes, func(i, j int) bool { return params[outputIndexes[i]].Name < params[outputIndexes[j]].Name }) 323 324 currOffset := totalSize - totalOutputSize 325 for _, outputIndex := range outputIndexes { 326 params[outputIndex].Exist = true 327 params[outputIndex].Offset = currOffset 328 currOffset += int(params[outputIndex].Typ.Size()) 329 } 330 return 331 } 332 333 func (p *Process) fillInUnknownParameter(pc uint64, params []Parameter) { 334 if !p.canFillInUnknownParameter(pc, params) { 335 return 336 } 337 338 unknownParamIndex := -1 339 for i, param := range params { 340 if !param.Exist { 341 unknownParamIndex = i 342 break 343 } 344 } 345 346 offset := p.calculateUnknownParameterOffset(params) 347 params[unknownParamIndex].Exist = true 348 params[unknownParamIndex].Offset = offset 349 350 sort.Slice(params, func(i, j int) bool { return params[i].Offset < params[j].Offset }) 351 return 352 } 353 354 func (p *Process) canFillInUnknownParameter(pc uint64, params []Parameter) bool { 355 numNonExistParams := 0 356 for _, param := range params { 357 if !param.Exist { 358 numNonExistParams++ 359 } 360 } 361 if numNonExistParams != 1 { 362 // 0: no need to fill in. 363 // 1>: we can not fill in the parameter's location in decisive way. 364 return false 365 } 366 367 if !p.noPadding(pc, params) { 368 // It may be dangerous to fill in the parameter's location due to the alignment. 369 return false 370 } 371 return true 372 } 373 374 func (p *Process) noPadding(pc uint64, params []Parameter) bool { 375 expectedArgsSize, err := p.findFunctionArgsSize(pc) 376 if err != nil { 377 log.Debugf("failed to find function args size: %v", err) 378 return false 379 } 380 381 actualArgsSize := 0 382 for _, param := range params { 383 actualArgsSize += int(param.Typ.Size()) 384 } 385 return actualArgsSize == expectedArgsSize 386 } 387 388 func (p *Process) findFunctionArgsSize(pc uint64) (int, error) { 389 md := p.findModuleDataByPC(pc) 390 if md == nil { 391 return 0, fmt.Errorf("no moduledata found for pc %#x", pc) 392 } 393 394 funcTypeVal, _, err := p.findFuncType(md, pc) 395 if err != nil { 396 return 0, err 397 } 398 399 for _, field := range _funcType.Field { 400 if field.Name == "args" { 401 rawData := funcTypeVal[field.ByteOffset : field.ByteOffset+field.Type.Size()] 402 return int(binary.LittleEndian.Uint32(rawData)), nil 403 } 404 } 405 return 0, fmt.Errorf("failed to find args size at %#x", pc) 406 } 407 408 func (p *Process) calculateUnknownParameterOffset(params []Parameter) int { 409 argsSize := 0 410 for _, param := range params { 411 argsSize += int(param.Typ.Size()) 412 } 413 414 params = append(params, Parameter{Offset: argsSize, Exist: true} /* sentinel */) 415 for i := 0; i < len(params)-1; i++ { 416 if !params[i].Exist { 417 continue 418 } 419 j := i + 1 420 if !params[j].Exist { 421 j++ 422 } 423 424 nextOffset := params[i].Offset + int(params[i].Typ.Size()) 425 if nextOffset != params[j].Offset { 426 return nextOffset 427 } 428 } 429 return 0 430 } 431 432 var findfuncbucketType = &dwarf.StructType{ 433 CommonType: dwarf.CommonType{ByteSize: 20}, 434 StructName: "runtime.findfuncbucket", 435 Field: []*dwarf.StructField{ 436 &dwarf.StructField{ 437 Name: "idx", 438 Type: &dwarf.UintType{BasicType: dwarf.BasicType{CommonType: dwarf.CommonType{ByteSize: 4}}}, 439 ByteOffset: 0, 440 }, 441 &dwarf.StructField{ 442 Name: "subbuckets", 443 Type: &dwarf.ArrayType{ 444 CommonType: dwarf.CommonType{ByteSize: 16}, 445 Type: &dwarf.UintType{BasicType: dwarf.BasicType{CommonType: dwarf.CommonType{ByteSize: 1}}}, 446 StrideBitSize: 0, 447 Count: 16, 448 }, 449 ByteOffset: 4, 450 }, 451 }, 452 } 453 454 // Assume this dwarf.Type represents a subset of the _func type in the case DWARF is not available. 455 var _funcType = &dwarf.StructType{ 456 StructName: "runtime._func", 457 CommonType: dwarf.CommonType{ByteSize: 40}, 458 Field: []*dwarf.StructField{ 459 &dwarf.StructField{ 460 Name: "entry", 461 Type: &dwarf.UintType{BasicType: dwarf.BasicType{CommonType: dwarf.CommonType{ByteSize: 8}}}, 462 ByteOffset: 0, 463 }, 464 &dwarf.StructField{ 465 Name: "nameoff", 466 Type: &dwarf.IntType{BasicType: dwarf.BasicType{CommonType: dwarf.CommonType{ByteSize: 4}}}, 467 ByteOffset: 8, 468 }, 469 &dwarf.StructField{ 470 Name: "args", 471 Type: &dwarf.IntType{BasicType: dwarf.BasicType{CommonType: dwarf.CommonType{ByteSize: 4}}}, 472 ByteOffset: 12, 473 }, 474 }, 475 } 476 477 // findFunctionByModuleData has the same logic as the runtime.findfunc. 478 func (p *Process) findFunctionByModuleData(pc uint64) (*Function, error) { 479 md := p.findModuleDataByPC(pc) 480 if md == nil { 481 return nil, fmt.Errorf("no moduledata found for pc %#x", pc) 482 } 483 484 funcTypeVal, endAddr, err := p.findFuncType(md, pc) 485 if err != nil { 486 return nil, err 487 } 488 489 var entry uint64 490 var nameoff int32 491 var args int32 492 for _, field := range _funcType.Field { 493 rawData := funcTypeVal[field.ByteOffset : field.ByteOffset+field.Type.Size()] 494 switch field.Name { 495 case "entry": 496 entry = binary.LittleEndian.Uint64(rawData) 497 case "nameoff": 498 nameoff = int32(binary.LittleEndian.Uint32(rawData)) 499 case "args": 500 args = int32(binary.LittleEndian.Uint32(rawData)) 501 if args < 0 { 502 // In Go's Assembler, the args size declared in the TEXT directive can be omitted. 503 // In that case, `args` here may be negative. 504 args = 0 505 } 506 } 507 } 508 509 funcName, err := p.resolveNameoff(md, int(nameoff)) 510 if err != nil { 511 return nil, err 512 } 513 514 numParams := int(args) / 8 // the actual number of params is unknown. Assumes the each parameter has 1 ptr size. 515 params := make([]Parameter, 0, numParams) 516 for i := 0; i < numParams; i++ { 517 param := Parameter{ 518 Typ: &dwarf.PtrType{CommonType: dwarf.CommonType{ByteSize: 8}, Type: &dwarf.VoidType{}}, 519 Offset: i * 8, 520 Exist: true, 521 } 522 params = append(params, param) 523 } 524 525 return &Function{Name: funcName, StartAddr: entry, EndAddr: endAddr, Parameters: params}, nil 526 } 527 528 func (p *Process) findModuleDataByPC(pc uint64) *moduleData { 529 for _, moduleData := range p.moduleDataList { 530 if moduleData.minpc(p.debugapiClient) <= pc && pc < moduleData.maxpc(p.debugapiClient) { 531 return moduleData 532 } 533 } 534 return nil 535 } 536 537 const ( 538 // must be same as the values defined in runtime package 539 minfunc = 16 // minimum function size 540 pcbucketsize = 256 * minfunc // size of bucket in the pc->func lookup table 541 ) 542 543 // findFuncType implements the core logic to find the func type using pc. 544 // The logic is essentially same as the one used in the runtime.findfunc(). 545 // It involves 2 tables and linear search and has 4 steps (if the only 1 table is there, it must be huge!). 546 // (1) Find the bucket. `findfunctab` points to the array of the buckets. 547 // The index is pc / (1 bucket region, typically 4096 bytes), so it uses the first 20 bits of the pc 548 // (assuming the pc can be represented in 32 bits). 549 // (2) Find the subbucket. Each bucket contains the 16 subbuckets. 550 // The index is pc % 1 bucket region / (1 subbucket region, typically 256), so it uses the 551 // next 4 bits of the pc. 552 // (3) Find the functab. `functab` points to the array of the functabs. 553 // We can find out the rough index using the index the bucket holds + sub-index the subbucket holds. 554 // But it may not be correct, because 1 subbucket region is typically 256 and may contain multiple functions. 555 // So do the linear search to find the correct index. 556 // (4) Finally, get the func type using the funcoff field in functab, the pointer to the func type embedded in the pcln table. 557 // Note that the pcln table contains not only func type, but other data like function name. 558 func (p *Process) findFuncType(md *moduleData, pc uint64) ([]byte, uint64, error) { 559 ftabIdx, err := p.findFtabIndex(md, pc) 560 if err != nil { 561 return nil, 0, err 562 } 563 564 ftabIdx = p.adjustFtabIndex(md, pc, ftabIdx) 565 endAddr := p.findEndAddr(md, ftabIdx) 566 _, funcoff := md.functab(p.debugapiClient, ftabIdx) 567 568 funcTypePtr := md.pclntable(p.debugapiClient, int(funcoff)) 569 buff := make([]byte, _funcType.Size()) 570 if err := p.debugapiClient.ReadMemory(funcTypePtr, buff); err != nil { 571 return nil, 0, err 572 } 573 574 return buff, endAddr, nil 575 } 576 577 func (p *Process) findFtabIndex(md *moduleData, pc uint64) (int, error) { 578 var idxField, subbucketsField *dwarf.StructField 579 for _, field := range findfuncbucketType.Field { 580 switch field.Name { 581 case "idx": 582 idxField = field 583 case "subbuckets": 584 subbucketsField = field 585 } 586 } 587 588 x := pc - md.minpc(p.debugapiClient) 589 bucketIndex := x / pcbucketsize 590 subbucketIndex := int(x % pcbucketsize / (pcbucketsize / uint64(subbucketsField.Type.Size()))) 591 592 ptrToFindFuncBucket := md.findfunctab(p.debugapiClient) + bucketIndex*uint64(findfuncbucketType.Size()) 593 buff := make([]byte, findfuncbucketType.Size()) 594 if err := p.debugapiClient.ReadMemory(ptrToFindFuncBucket, buff); err != nil { 595 return 0, err 596 } 597 598 ftabIdx := int(binary.LittleEndian.Uint32(buff[idxField.ByteOffset : idxField.ByteOffset+idxField.Type.Size()])) 599 ftabIdx += int(buff[int(subbucketsField.ByteOffset)+subbucketIndex]) 600 return ftabIdx, nil 601 } 602 603 func (p *Process) adjustFtabIndex(md *moduleData, pc uint64, ftabIdx int) int { 604 ftabLen := md.ftabLen(p.debugapiClient) 605 if ftabIdx >= ftabLen { 606 ftabIdx = ftabLen - 1 607 } 608 609 entry, _ := md.functab(p.debugapiClient, ftabIdx) 610 if pc < entry { 611 for entry > pc && ftabIdx > 0 { 612 ftabIdx-- 613 entry, _ = md.functab(p.debugapiClient, ftabIdx) 614 } 615 if ftabIdx == 0 { 616 panic("bad findfunctab entry idx") 617 } 618 } else { 619 // linear search to find func with pc >= entry. 620 nextEntry, _ := md.functab(p.debugapiClient, ftabIdx+1) 621 for nextEntry <= pc { 622 ftabIdx++ 623 nextEntry, _ = md.functab(p.debugapiClient, ftabIdx+1) 624 } 625 } 626 return ftabIdx 627 } 628 629 func (p *Process) findEndAddr(md *moduleData, ftabIdx int) uint64 { 630 ftabLen := md.ftabLen(p.debugapiClient) 631 if ftabIdx+1 >= ftabLen { 632 return 0 633 } 634 entry, _ := md.functab(p.debugapiClient, ftabIdx+1) 635 return entry 636 } 637 638 func (p *Process) resolveNameoff(md *moduleData, nameoff int) (string, error) { 639 ptrToFuncname := md.pclntable(p.debugapiClient, nameoff) 640 var rawFuncname []byte 641 for { 642 buff := make([]byte, 16) 643 if err := p.debugapiClient.ReadMemory(ptrToFuncname, buff); err != nil { 644 return "", err 645 } 646 647 for i := 0; i < len(buff); i++ { 648 if buff[i] == 0 { 649 return string(append(rawFuncname, buff[0:i]...)), nil 650 } 651 } 652 653 rawFuncname = append(rawFuncname, buff...) 654 ptrToFuncname += uint64(len(buff)) 655 } 656 } 657 658 func (p *Process) currentArgs(params []Parameter, addrBeginningOfArgs uint64) (inputArgs []Argument, outputArgs []Argument, err error) { 659 for _, param := range params { 660 param := param // without this, all the closures point to the last param. 661 parseValue := func(depth int) value { 662 if !param.Exist { 663 return nil 664 } 665 666 size := param.Typ.Size() 667 buff := make([]byte, size) 668 if err = p.debugapiClient.ReadMemory(addrBeginningOfArgs+uint64(param.Offset), buff); err != nil { 669 log.Debugf("failed to read the '%s' value: %v", param.Name, err) 670 return nil 671 } 672 return p.valueParser.parseValue(param.Typ, buff, depth) 673 } 674 675 arg := Argument{Name: param.Name, Typ: param.Typ, parseValue: parseValue} 676 if param.IsOutput { 677 outputArgs = append(outputArgs, arg) 678 } else { 679 inputArgs = append(inputArgs, arg) 680 } 681 } 682 return 683 } 684 685 // ReadInstructions reads the instructions of the specified function from memory. 686 func (p *Process) ReadInstructions(f *Function) ([]x86asm.Inst, error) { 687 if f.EndAddr == 0 { 688 return nil, fmt.Errorf("the end address of the function %s is unknown", f.Name) 689 } 690 691 buff := make([]byte, f.EndAddr-f.StartAddr) 692 if err := p.debugapiClient.ReadMemory(f.StartAddr, buff); err != nil { 693 return nil, err 694 } 695 696 for addr, bp := range p.breakpoints { 697 if f.StartAddr <= addr && addr < f.EndAddr { 698 copy(buff[addr-f.StartAddr:], bp.orgInsts) 699 } 700 } 701 702 var pos int 703 var insts []x86asm.Inst 704 for pos < len(buff) { 705 inst, err := x86asm.Decode(buff[pos:len(buff)], 64) 706 if err != nil { 707 log.Debugf("decode error at %#x: %v", pos, err) 708 } else { 709 insts = append(insts, inst) 710 } 711 712 pos += inst.Len 713 } 714 715 return insts, nil 716 } 717 718 // GoRoutineInfo describes the various info of the go routine like pc. 719 type GoRoutineInfo struct { 720 ID int64 721 UsedStackSize uint64 722 CurrentPC uint64 723 CurrentStackAddr uint64 724 NextDeferFuncAddr uint64 725 Panicking bool 726 PanicHandler *PanicHandler 727 } 728 729 // PanicHandler holds the function info which (will) handles panic. 730 type PanicHandler struct { 731 // UsedStackSizeAtDefer and PCAtDefer are the function info which register this handler by 'defer'. 732 UsedStackSizeAtDefer uint64 733 PCAtDefer uint64 734 } 735 736 // CurrentGoRoutineInfo returns the go routine info associated with the go routine which hits the breakpoint. 737 func (p *Process) CurrentGoRoutineInfo(threadID int) (GoRoutineInfo, error) { 738 gAddr, err := p.debugapiClient.ReadTLS(threadID, p.offsetToG()) 739 if err != nil { 740 unspecifiedError, ok := err.(debugapi.UnspecifiedThreadError) 741 if !ok { 742 return GoRoutineInfo{}, err 743 } 744 745 if err := p.singleStepUnspecifiedThreads(threadID, unspecifiedError); err != nil { 746 return GoRoutineInfo{}, err 747 } 748 return p.CurrentGoRoutineInfo(threadID) 749 } 750 751 _, idRawVal, err := p.findFieldInStruct(gAddr, p.Binary.runtimeGType(), "goid") 752 if err != nil { 753 return GoRoutineInfo{}, err 754 } 755 id := int64(binary.LittleEndian.Uint64(idRawVal)) 756 757 stackType, stackRawVal, err := p.findFieldInStruct(gAddr, p.Binary.runtimeGType(), "stack") 758 if err != nil { 759 return GoRoutineInfo{}, err 760 } 761 stackVal := p.valueParser.parseValue(stackType, stackRawVal, 1) 762 stackHi := stackVal.(structValue).fields["hi"].(uint64Value).val 763 764 regs, err := p.debugapiClient.ReadRegisters(threadID) 765 if err != nil { 766 return GoRoutineInfo{}, err 767 } 768 usedStackSize := stackHi - regs.Rsp 769 770 _, panicRawVal, err := p.findFieldInStruct(gAddr, p.Binary.runtimeGType(), "_panic") 771 if err != nil { 772 return GoRoutineInfo{}, err 773 } 774 panicAddr := binary.LittleEndian.Uint64(panicRawVal) 775 panicking := panicAddr != 0 776 777 panicHandler, err := p.findPanicHandler(gAddr, panicAddr, stackHi) 778 if err != nil { 779 return GoRoutineInfo{}, err 780 } 781 782 nextDeferFuncAddr, err := p.findNextDeferFuncAddr(gAddr) 783 if err != nil { 784 return GoRoutineInfo{}, err 785 } 786 787 return GoRoutineInfo{ID: id, UsedStackSize: usedStackSize, CurrentPC: regs.Rip, CurrentStackAddr: regs.Rsp, NextDeferFuncAddr: nextDeferFuncAddr, Panicking: panicking, PanicHandler: panicHandler}, nil 788 } 789 790 func (p *Process) singleStepUnspecifiedThreads(threadID int, err debugapi.UnspecifiedThreadError) error { 791 for _, unspecifiedThread := range err.ThreadIDs { 792 if unspecifiedThread == threadID { 793 continue 794 } 795 796 regs, err := p.debugapiClient.ReadRegisters(unspecifiedThread) 797 if err != nil { 798 return err 799 } 800 if err := p.SingleStep(unspecifiedThread, regs.Rip-1); err != nil { 801 return err 802 } 803 } 804 return nil 805 } 806 807 func (p *Process) findNextDeferFuncAddr(gAddr uint64) (uint64, error) { 808 ptrToDeferType, rawVal, err := p.findFieldInStruct(gAddr, p.Binary.runtimeGType(), "_defer") 809 if err != nil { 810 return 0, err 811 } 812 deferAddr := binary.LittleEndian.Uint64(rawVal) 813 if deferAddr == 0x0 { 814 return 0x0, nil 815 } 816 817 deferType := ptrToDeferType.(*dwarf.PtrType).Type 818 _, rawVal, err = p.findFieldInStruct(deferAddr, deferType, "fn") 819 if err != nil { 820 return 0, err 821 } 822 ptrToFuncAddr := binary.LittleEndian.Uint64(rawVal) 823 824 buff := make([]byte, 8) 825 if err := p.debugapiClient.ReadMemory(ptrToFuncAddr, buff); err != nil { 826 return 0, fmt.Errorf("failed to read memory at %#x: %v", ptrToFuncAddr, err) 827 } 828 return binary.LittleEndian.Uint64(buff), nil 829 } 830 831 func (p *Process) findFieldInStruct(structAddr uint64, structType dwarf.Type, fieldName string) (dwarf.Type, []byte, error) { 832 for { 833 typedefType, ok := structType.(*dwarf.TypedefType) 834 if !ok { 835 break 836 } 837 structType = typedefType.Type 838 } 839 840 for _, field := range structType.(*dwarf.StructType).Field { 841 if field.Name != fieldName { 842 continue 843 } 844 845 buff := make([]byte, field.Type.Size()) 846 addr := structAddr + uint64(field.ByteOffset) 847 if err := p.debugapiClient.ReadMemory(addr, buff); err != nil { 848 return nil, nil, fmt.Errorf("failed to read memory at %#x: %v", addr, err) 849 } 850 return field.Type, buff, nil 851 } 852 return nil, nil, fmt.Errorf("field %s not found", fieldName) 853 } 854 855 func (p *Process) findPanicHandler(gAddr, panicAddr, stackHi uint64) (*PanicHandler, error) { 856 ptrToDeferType, rawVal, err := p.findFieldInStruct(gAddr, p.Binary.runtimeGType(), "_defer") 857 if err != nil { 858 return nil, err 859 } 860 deferAddr := binary.LittleEndian.Uint64(rawVal) 861 deferType := ptrToDeferType.(*dwarf.PtrType).Type 862 863 for deferAddr != 0 { 864 _, rawVal, err := p.findFieldInStruct(deferAddr, deferType, "_panic") 865 if err != nil { 866 return nil, err 867 } 868 panicInDefer := binary.LittleEndian.Uint64(rawVal) 869 if panicInDefer == panicAddr { 870 break 871 } 872 873 _, rawVal, err = p.findFieldInStruct(deferAddr, deferType, "link") 874 if err != nil { 875 return nil, err 876 } 877 deferAddr = binary.LittleEndian.Uint64(rawVal) 878 } 879 880 if deferAddr == 0 { 881 return nil, nil 882 } 883 884 _, rawVal, err = p.findFieldInStruct(deferAddr, deferType, "sp") 885 if err != nil { 886 return nil, err 887 } 888 stackAddress := binary.LittleEndian.Uint64(rawVal) 889 usedStackSizeAtDefer := stackHi - stackAddress 890 891 _, rawVal, err = p.findFieldInStruct(deferAddr, deferType, "pc") 892 if err != nil { 893 return nil, err 894 } 895 pc := binary.LittleEndian.Uint64(rawVal) 896 897 return &PanicHandler{UsedStackSizeAtDefer: usedStackSizeAtDefer, PCAtDefer: pc}, nil 898 } 899 900 // ThreadInfo describes the various info of thread. 901 type ThreadInfo struct { 902 ID int 903 CurrentPC uint64 904 CurrentStackAddr uint64 905 } 906 907 // CurrentThreadInfo returns the thread info of the specified thread ID. 908 func (p *Process) CurrentThreadInfo(threadID int) (ThreadInfo, error) { 909 regs, err := p.debugapiClient.ReadRegisters(threadID) 910 if err != nil { 911 return ThreadInfo{}, err 912 } 913 return ThreadInfo{ID: threadID, CurrentPC: regs.Rip, CurrentStackAddr: regs.Rsp}, nil 914 } 915 916 // Argument represents the value passed to the function. 917 type Argument struct { 918 Name string 919 Typ dwarf.Type 920 // parseValue lazily parses the value. The parsing every time is not only wasting resource, but the value may not be initialized yet. 921 parseValue func(int) value 922 } 923 924 // ParseValue parses the arg value and returns string representation. 925 // The `depth` option specifies to the depth of the parsing. 926 func (arg Argument) ParseValue(depth int) string { 927 val := arg.parseValue(depth) 928 var valStr string 929 if val == nil { 930 valStr = "-" 931 } else { 932 valStr = val.String() 933 } 934 935 if arg.Name == "" { 936 return valStr 937 } 938 return fmt.Sprintf("%s = %s", arg.Name, valStr) 939 }