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 }