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 }