github.com/neilgarb/delve@v1.9.2-nobreaks/service/api/conversions.go (about)

     1  package api
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"go/constant"
     7  	"go/printer"
     8  	"go/token"
     9  	"reflect"
    10  	"sort"
    11  	"strconv"
    12  	"strings"
    13  
    14  	"github.com/go-delve/delve/pkg/dwarf/godwarf"
    15  	"github.com/go-delve/delve/pkg/dwarf/op"
    16  	"github.com/go-delve/delve/pkg/proc"
    17  )
    18  
    19  // ConvertLogicalBreakpoint converts a proc.LogicalBreakpoint into an API breakpoint.
    20  func ConvertLogicalBreakpoint(lbp *proc.LogicalBreakpoint) *Breakpoint {
    21  	b := &Breakpoint{
    22  		ID:            lbp.LogicalID,
    23  		FunctionName:  lbp.FunctionName,
    24  		File:          lbp.File,
    25  		Line:          lbp.Line,
    26  		Name:          lbp.Name,
    27  		Tracepoint:    lbp.Tracepoint,
    28  		TraceReturn:   lbp.TraceReturn,
    29  		Stacktrace:    lbp.Stacktrace,
    30  		Goroutine:     lbp.Goroutine,
    31  		Variables:     lbp.Variables,
    32  		LoadArgs:      LoadConfigFromProc(lbp.LoadArgs),
    33  		LoadLocals:    LoadConfigFromProc(lbp.LoadLocals),
    34  		TotalHitCount: lbp.TotalHitCount,
    35  		Disabled:      !lbp.Enabled,
    36  		UserData:      lbp.UserData,
    37  	}
    38  
    39  	b.HitCount = map[string]uint64{}
    40  	for idx := range lbp.HitCount {
    41  		b.HitCount[strconv.Itoa(idx)] = lbp.HitCount[idx]
    42  	}
    43  
    44  	if lbp.HitCond != nil {
    45  		b.HitCond = fmt.Sprintf("%s %d", lbp.HitCond.Op.String(), lbp.HitCond.Val)
    46  	}
    47  
    48  	var buf bytes.Buffer
    49  	printer.Fprint(&buf, token.NewFileSet(), lbp.Cond)
    50  	b.Cond = buf.String()
    51  
    52  	return b
    53  }
    54  
    55  // ConvertPhysicalBreakpoints adds informations from physical breakpoints to an API breakpoint.
    56  func ConvertPhysicalBreakpoints(b *Breakpoint, bps []*proc.Breakpoint) {
    57  	if len(bps) == 0 {
    58  		return
    59  	}
    60  
    61  	b.WatchExpr = bps[0].WatchExpr
    62  	b.WatchType = WatchType(bps[0].WatchType)
    63  
    64  	lg := false
    65  	for _, bp := range bps {
    66  		b.Addrs = append(b.Addrs, bp.Addr)
    67  		if b.FunctionName != bp.FunctionName && b.FunctionName != "" {
    68  			if !lg {
    69  				b.FunctionName = removeTypeParams(b.FunctionName)
    70  				lg = true
    71  			}
    72  			fn := removeTypeParams(bp.FunctionName)
    73  			if b.FunctionName != fn {
    74  				b.FunctionName = "(multiple functions)"
    75  			}
    76  		}
    77  	}
    78  
    79  	if len(b.Addrs) > 0 {
    80  		b.Addr = b.Addrs[0]
    81  	}
    82  }
    83  
    84  func removeTypeParams(name string) string {
    85  	fn := proc.Function{Name: name}
    86  	return fn.NameWithoutTypeParams()
    87  }
    88  
    89  // ConvertThread converts a proc.Thread into an
    90  // api thread.
    91  func ConvertThread(th proc.Thread, bp *Breakpoint) *Thread {
    92  	var (
    93  		function *Function
    94  		file     string
    95  		line     int
    96  		pc       uint64
    97  		gid      int
    98  	)
    99  
   100  	loc, err := th.Location()
   101  	if err == nil {
   102  		pc = loc.PC
   103  		file = loc.File
   104  		line = loc.Line
   105  		function = ConvertFunction(loc.Fn)
   106  	}
   107  
   108  	if g, _ := proc.GetG(th); g != nil {
   109  		gid = g.ID
   110  	}
   111  
   112  	return &Thread{
   113  		ID:          th.ThreadID(),
   114  		PC:          pc,
   115  		File:        file,
   116  		Line:        line,
   117  		Function:    function,
   118  		GoroutineID: gid,
   119  		Breakpoint:  bp,
   120  	}
   121  }
   122  
   123  // ConvertThreads converts a slice of proc.Thread into a slice of api.Thread.
   124  func ConvertThreads(threads []proc.Thread, convertBreakpoint func(proc.Thread) *Breakpoint) []*Thread {
   125  	r := make([]*Thread, len(threads))
   126  	for i := range threads {
   127  		r[i] = ConvertThread(threads[i], convertBreakpoint(threads[i]))
   128  	}
   129  	return r
   130  }
   131  
   132  func PrettyTypeName(typ godwarf.Type) string {
   133  	if typ == nil {
   134  		return ""
   135  	}
   136  	if typ.Common().Name != "" {
   137  		return typ.Common().Name
   138  	}
   139  	r := typ.String()
   140  	if r == "*void" {
   141  		return "unsafe.Pointer"
   142  	}
   143  	return r
   144  }
   145  
   146  func convertFloatValue(v *proc.Variable, sz int) string {
   147  	switch v.FloatSpecial {
   148  	case proc.FloatIsPosInf:
   149  		return "+Inf"
   150  	case proc.FloatIsNegInf:
   151  		return "-Inf"
   152  	case proc.FloatIsNaN:
   153  		return "NaN"
   154  	}
   155  	f, _ := constant.Float64Val(v.Value)
   156  	return strconv.FormatFloat(f, 'f', -1, sz)
   157  }
   158  
   159  // ConvertVar converts from proc.Variable to api.Variable.
   160  func ConvertVar(v *proc.Variable) *Variable {
   161  	r := Variable{
   162  		Addr:     v.Addr,
   163  		OnlyAddr: v.OnlyAddr,
   164  		Name:     v.Name,
   165  		Kind:     v.Kind,
   166  		Len:      v.Len,
   167  		Cap:      v.Cap,
   168  		Flags:    VariableFlags(v.Flags),
   169  		Base:     v.Base,
   170  
   171  		LocationExpr: v.LocationExpr.String(),
   172  		DeclLine:     v.DeclLine,
   173  	}
   174  
   175  	r.Type = PrettyTypeName(v.DwarfType)
   176  	r.RealType = PrettyTypeName(v.RealType)
   177  
   178  	if v.Unreadable != nil {
   179  		r.Unreadable = v.Unreadable.Error()
   180  	}
   181  
   182  	r.Value = VariableValueAsString(v)
   183  
   184  	switch v.Kind {
   185  	case reflect.Complex64:
   186  		r.Children = make([]Variable, 2)
   187  		r.Len = 2
   188  
   189  		r.Children[0].Name = "real"
   190  		r.Children[0].Kind = reflect.Float32
   191  
   192  		r.Children[1].Name = "imaginary"
   193  		r.Children[1].Kind = reflect.Float32
   194  
   195  		if v.Value != nil {
   196  			real, _ := constant.Float64Val(constant.Real(v.Value))
   197  			r.Children[0].Value = strconv.FormatFloat(real, 'f', -1, 32)
   198  
   199  			imag, _ := constant.Float64Val(constant.Imag(v.Value))
   200  			r.Children[1].Value = strconv.FormatFloat(imag, 'f', -1, 32)
   201  		} else {
   202  			r.Children[0].Value = "nil"
   203  			r.Children[1].Value = "nil"
   204  		}
   205  
   206  	case reflect.Complex128:
   207  		r.Children = make([]Variable, 2)
   208  		r.Len = 2
   209  
   210  		r.Children[0].Name = "real"
   211  		r.Children[0].Kind = reflect.Float64
   212  
   213  		r.Children[1].Name = "imaginary"
   214  		r.Children[1].Kind = reflect.Float64
   215  
   216  		if v.Value != nil {
   217  			real, _ := constant.Float64Val(constant.Real(v.Value))
   218  			r.Children[0].Value = strconv.FormatFloat(real, 'f', -1, 64)
   219  
   220  			imag, _ := constant.Float64Val(constant.Imag(v.Value))
   221  			r.Children[1].Value = strconv.FormatFloat(imag, 'f', -1, 64)
   222  		} else {
   223  			r.Children[0].Value = "nil"
   224  			r.Children[1].Value = "nil"
   225  		}
   226  
   227  	default:
   228  		r.Children = make([]Variable, len(v.Children))
   229  
   230  		for i := range v.Children {
   231  			r.Children[i] = *ConvertVar(&v.Children[i])
   232  		}
   233  	}
   234  
   235  	return &r
   236  }
   237  
   238  func VariableValueAsString(v *proc.Variable) string {
   239  	if v.Value == nil {
   240  		return ""
   241  	}
   242  	switch v.Kind {
   243  	case reflect.Float32:
   244  		return convertFloatValue(v, 32)
   245  	case reflect.Float64:
   246  		return convertFloatValue(v, 64)
   247  	case reflect.String, reflect.Func, reflect.Struct:
   248  		return constant.StringVal(v.Value)
   249  	default:
   250  		if cd := v.ConstDescr(); cd != "" {
   251  			return fmt.Sprintf("%s (%s)", cd, v.Value.String())
   252  		} else {
   253  			return v.Value.String()
   254  		}
   255  	}
   256  }
   257  
   258  // ConvertVars converts from []*proc.Variable to []api.Variable.
   259  func ConvertVars(pv []*proc.Variable) []Variable {
   260  	if pv == nil {
   261  		return nil
   262  	}
   263  	vars := make([]Variable, 0, len(pv))
   264  	for _, v := range pv {
   265  		vars = append(vars, *ConvertVar(v))
   266  	}
   267  	return vars
   268  }
   269  
   270  // ConvertFunction converts from gosym.Func to
   271  // api.Function.
   272  func ConvertFunction(fn *proc.Function) *Function {
   273  	if fn == nil {
   274  		return nil
   275  	}
   276  
   277  	// fn here used to be a *gosym.Func, the fields Type and GoType below
   278  	// corresponded to the homonymous field of gosym.Func. Since the contents of
   279  	// those fields is not documented their value was replaced with 0 when
   280  	// gosym.Func was replaced by debug_info entries.
   281  	return &Function{
   282  		Name_:     fn.Name,
   283  		Type:      0,
   284  		Value:     fn.Entry,
   285  		GoType:    0,
   286  		Optimized: fn.Optimized(),
   287  	}
   288  }
   289  
   290  // ConvertGoroutine converts from proc.G to api.Goroutine.
   291  func ConvertGoroutine(tgt *proc.Target, g *proc.G) *Goroutine {
   292  	th := g.Thread
   293  	tid := 0
   294  	if th != nil {
   295  		tid = th.ThreadID()
   296  	}
   297  	if g.Unreadable != nil {
   298  		return &Goroutine{Unreadable: g.Unreadable.Error()}
   299  	}
   300  	return &Goroutine{
   301  		ID:             g.ID,
   302  		CurrentLoc:     ConvertLocation(g.CurrentLoc),
   303  		UserCurrentLoc: ConvertLocation(g.UserCurrent()),
   304  		GoStatementLoc: ConvertLocation(g.Go()),
   305  		StartLoc:       ConvertLocation(g.StartLoc(tgt)),
   306  		ThreadID:       tid,
   307  		WaitSince:      g.WaitSince,
   308  		WaitReason:     g.WaitReason,
   309  		Labels:         g.Labels(),
   310  		Status:         g.Status,
   311  	}
   312  }
   313  
   314  // ConvertGoroutines converts from []*proc.G to []*api.Goroutine.
   315  func ConvertGoroutines(tgt *proc.Target, gs []*proc.G) []*Goroutine {
   316  	goroutines := make([]*Goroutine, len(gs))
   317  	for i := range gs {
   318  		goroutines[i] = ConvertGoroutine(tgt, gs[i])
   319  	}
   320  	return goroutines
   321  }
   322  
   323  // ConvertLocation converts from proc.Location to api.Location.
   324  func ConvertLocation(loc proc.Location) Location {
   325  	return Location{
   326  		PC:       loc.PC,
   327  		File:     loc.File,
   328  		Line:     loc.Line,
   329  		Function: ConvertFunction(loc.Fn),
   330  	}
   331  }
   332  
   333  // ConvertAsmInstruction converts from proc.AsmInstruction to api.AsmInstruction.
   334  func ConvertAsmInstruction(inst proc.AsmInstruction, text string) AsmInstruction {
   335  	var destloc *Location
   336  	if inst.DestLoc != nil {
   337  		r := ConvertLocation(*inst.DestLoc)
   338  		destloc = &r
   339  	}
   340  	return AsmInstruction{
   341  		Loc:        ConvertLocation(inst.Loc),
   342  		DestLoc:    destloc,
   343  		Text:       text,
   344  		Bytes:      inst.Bytes,
   345  		Breakpoint: inst.Breakpoint,
   346  		AtPC:       inst.AtPC,
   347  	}
   348  }
   349  
   350  // LoadConfigToProc converts an api.LoadConfig to proc.LoadConfig.
   351  func LoadConfigToProc(cfg *LoadConfig) *proc.LoadConfig {
   352  	if cfg == nil {
   353  		return nil
   354  	}
   355  	return &proc.LoadConfig{
   356  		FollowPointers:     cfg.FollowPointers,
   357  		MaxVariableRecurse: cfg.MaxVariableRecurse,
   358  		MaxStringLen:       cfg.MaxStringLen,
   359  		MaxArrayValues:     cfg.MaxArrayValues,
   360  		MaxStructFields:    cfg.MaxStructFields,
   361  		MaxMapBuckets:      0, // MaxMapBuckets is set internally by pkg/proc, read its documentation for an explanation.
   362  	}
   363  }
   364  
   365  // LoadConfigFromProc converts a proc.LoadConfig to api.LoadConfig.
   366  func LoadConfigFromProc(cfg *proc.LoadConfig) *LoadConfig {
   367  	if cfg == nil {
   368  		return nil
   369  	}
   370  	return &LoadConfig{
   371  		FollowPointers:     cfg.FollowPointers,
   372  		MaxVariableRecurse: cfg.MaxVariableRecurse,
   373  		MaxStringLen:       cfg.MaxStringLen,
   374  		MaxArrayValues:     cfg.MaxArrayValues,
   375  		MaxStructFields:    cfg.MaxStructFields,
   376  	}
   377  }
   378  
   379  var canonicalRegisterOrder = map[string]int{
   380  	// amd64
   381  	"rip": 0,
   382  	"rsp": 1,
   383  	"rax": 2,
   384  	"rbx": 3,
   385  	"rcx": 4,
   386  	"rdx": 5,
   387  
   388  	// arm64
   389  	"pc": 0,
   390  	"sp": 1,
   391  }
   392  
   393  // ConvertRegisters converts proc.Register to api.Register for a slice.
   394  func ConvertRegisters(in *op.DwarfRegisters, dwarfRegisterToString func(int, *op.DwarfRegister) (string, bool, string), floatingPoint bool) (out []Register) {
   395  	out = make([]Register, 0, in.CurrentSize())
   396  	for i := 0; i < in.CurrentSize(); i++ {
   397  		reg := in.Reg(uint64(i))
   398  		if reg == nil {
   399  			continue
   400  		}
   401  		name, fp, repr := dwarfRegisterToString(i, reg)
   402  		if !floatingPoint && fp {
   403  			continue
   404  		}
   405  		out = append(out, Register{name, repr, i})
   406  	}
   407  	// Sort the registers in a canonical order we prefer, this is mostly
   408  	// because the DWARF register numbering for AMD64 is weird.
   409  	sort.Slice(out, func(i, j int) bool {
   410  		a, b := out[i], out[j]
   411  		an, aok := canonicalRegisterOrder[strings.ToLower(a.Name)]
   412  		bn, bok := canonicalRegisterOrder[strings.ToLower(b.Name)]
   413  		// Registers that don't appear in canonicalRegisterOrder sort after registers that do.
   414  		if !aok {
   415  			an = 1000
   416  		}
   417  		if !bok {
   418  			bn = 1000
   419  		}
   420  		if an == bn {
   421  			// keep registers that don't appear in canonicalRegisterOrder in DWARF order
   422  			return a.DwarfNumber < b.DwarfNumber
   423  		}
   424  		return an < bn
   425  
   426  	})
   427  	return
   428  }
   429  
   430  func ConvertImage(image *proc.Image) Image {
   431  	return Image{Path: image.Path, Address: image.StaticBase}
   432  }
   433  
   434  func ConvertDumpState(dumpState *proc.DumpState) *DumpState {
   435  	dumpState.Mutex.Lock()
   436  	defer dumpState.Mutex.Unlock()
   437  	r := &DumpState{
   438  		Dumping:      dumpState.Dumping,
   439  		AllDone:      dumpState.AllDone,
   440  		ThreadsDone:  dumpState.ThreadsDone,
   441  		ThreadsTotal: dumpState.ThreadsTotal,
   442  		MemDone:      dumpState.MemDone,
   443  		MemTotal:     dumpState.MemTotal,
   444  	}
   445  	if dumpState.Err != nil {
   446  		r.Err = dumpState.Err.Error()
   447  	}
   448  	return r
   449  }