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