proc/variables: support NaN/Inf float values (#706)
Unfortunately go/constant does not support NaN and Inf float values so we need to store this information alongside. Fixes #705
This commit is contained in:
parent
449b276fe1
commit
d89d115ef9
@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/constant"
|
"go/constant"
|
||||||
|
"math"
|
||||||
"runtime"
|
"runtime"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
@ -207,6 +208,10 @@ func main() {
|
|||||||
ni16 := int16(-5)
|
ni16 := int16(-5)
|
||||||
ni32 := int32(-5)
|
ni32 := int32(-5)
|
||||||
|
|
||||||
|
pinf := math.Inf(+1)
|
||||||
|
ninf := math.Inf(-1)
|
||||||
|
nan := math.NaN()
|
||||||
|
|
||||||
var amb1 = 1
|
var amb1 = 1
|
||||||
runtime.Breakpoint()
|
runtime.Breakpoint()
|
||||||
for amb1 := 0; amb1 < 10; amb1++ {
|
for amb1 := 0; amb1 < 10; amb1++ {
|
||||||
@ -214,5 +219,5 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
runtime.Breakpoint()
|
runtime.Breakpoint()
|
||||||
fmt.Println(i1, i2, i3, p1, amb1, s1, s3, a1, p2, p3, s2, as1, str1, f1, fn1, fn2, nilslice, nilptr, ch1, chnil, m1, mnil, m2, m3, up1, i4, i5, i6, err1, err2, errnil, iface1, iface2, ifacenil, arr1, parr, cpx1, const1, iface3, iface4, recursive1, recursive1.x, iface5, iface2fn1, iface2fn2, bencharr, benchparr, mapinf, mainMenu, b, b2, sd, anonstruct1, anonstruct2, anoniface1, anonfunc, mapanonstruct1, ifacearr, efacearr, ni8, ni16, ni32)
|
fmt.Println(i1, i2, i3, p1, amb1, s1, s3, a1, p2, p3, s2, as1, str1, f1, fn1, fn2, nilslice, nilptr, ch1, chnil, m1, mnil, m2, m3, up1, i4, i5, i6, err1, err2, errnil, iface1, iface2, ifacenil, arr1, parr, cpx1, const1, iface3, iface4, recursive1, recursive1.x, iface5, iface2fn1, iface2fn2, bencharr, benchparr, mapinf, mainMenu, b, b2, sd, anonstruct1, anonstruct2, anoniface1, anonfunc, mapanonstruct1, ifacearr, efacearr, ni8, ni16, ni32, pinf, ninf, nan)
|
||||||
}
|
}
|
||||||
|
|||||||
10
proc/eval.go
10
proc/eval.go
@ -3,6 +3,7 @@ package proc
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/ast"
|
"go/ast"
|
||||||
"go/constant"
|
"go/constant"
|
||||||
@ -15,6 +16,8 @@ import (
|
|||||||
"golang.org/x/debug/dwarf"
|
"golang.org/x/debug/dwarf"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var OperationOnSpecialFloatError = errors.New("operations on non-finite floats not implemented")
|
||||||
|
|
||||||
// EvalExpression returns the value of the given expression.
|
// EvalExpression returns the value of the given expression.
|
||||||
func (scope *EvalScope) EvalExpression(expr string, cfg LoadConfig) (*Variable, error) {
|
func (scope *EvalScope) EvalExpression(expr string, cfg LoadConfig) (*Variable, error) {
|
||||||
t, err := parser.ParseExpr(expr)
|
t, err := parser.ParseExpr(expr)
|
||||||
@ -688,6 +691,9 @@ func (scope *EvalScope) evalUnary(node *ast.UnaryExpr) (*Variable, error) {
|
|||||||
if xv.Unreadable != nil {
|
if xv.Unreadable != nil {
|
||||||
return nil, xv.Unreadable
|
return nil, xv.Unreadable
|
||||||
}
|
}
|
||||||
|
if xv.FloatSpecial != 0 {
|
||||||
|
return nil, OperationOnSpecialFloatError
|
||||||
|
}
|
||||||
if xv.Value == nil {
|
if xv.Value == nil {
|
||||||
return nil, fmt.Errorf("operator %s can not be applied to \"%s\"", node.Op.String(), exprToString(node.X))
|
return nil, fmt.Errorf("operator %s can not be applied to \"%s\"", node.Op.String(), exprToString(node.X))
|
||||||
}
|
}
|
||||||
@ -794,6 +800,10 @@ func (scope *EvalScope) evalBinary(node *ast.BinaryExpr) (*Variable, error) {
|
|||||||
return nil, yv.Unreadable
|
return nil, yv.Unreadable
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if xv.FloatSpecial != 0 || yv.FloatSpecial != 0 {
|
||||||
|
return nil, OperationOnSpecialFloatError
|
||||||
|
}
|
||||||
|
|
||||||
typ, err := negotiateType(node.Op, xv, yv)
|
typ, err := negotiateType(node.Op, xv, yv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import (
|
|||||||
"go/constant"
|
"go/constant"
|
||||||
"go/parser"
|
"go/parser"
|
||||||
"go/token"
|
"go/token"
|
||||||
|
"math"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
@ -29,6 +30,15 @@ const (
|
|||||||
hashMinTopHash = 4 // used by map reading code, indicates minimum value of tophash that isn't empty or evacuated
|
hashMinTopHash = 4 // used by map reading code, indicates minimum value of tophash that isn't empty or evacuated
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type FloatSpecial uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
FloatIsNormal FloatSpecial = iota
|
||||||
|
FloatIsNaN
|
||||||
|
FloatIsPosInf
|
||||||
|
FloatIsNegInf
|
||||||
|
)
|
||||||
|
|
||||||
// Variable represents a variable. It contains the address, name,
|
// Variable represents a variable. It contains the address, name,
|
||||||
// type and other information parsed from both the Dwarf information
|
// type and other information parsed from both the Dwarf information
|
||||||
// and the memory of the debugged process.
|
// and the memory of the debugged process.
|
||||||
@ -43,7 +53,8 @@ type Variable struct {
|
|||||||
mem memoryReadWriter
|
mem memoryReadWriter
|
||||||
dbp *Process
|
dbp *Process
|
||||||
|
|
||||||
Value constant.Value
|
Value constant.Value
|
||||||
|
FloatSpecial FloatSpecial
|
||||||
|
|
||||||
Len int64
|
Len int64
|
||||||
Cap int64
|
Cap int64
|
||||||
@ -808,6 +819,14 @@ func (v *Variable) loadValueInternal(recurseLevel int, cfg LoadConfig) {
|
|||||||
var val float64
|
var val float64
|
||||||
val, v.Unreadable = v.readFloatRaw(v.RealType.(*dwarf.FloatType).ByteSize)
|
val, v.Unreadable = v.readFloatRaw(v.RealType.(*dwarf.FloatType).ByteSize)
|
||||||
v.Value = constant.MakeFloat64(val)
|
v.Value = constant.MakeFloat64(val)
|
||||||
|
switch {
|
||||||
|
case math.IsInf(val, +1):
|
||||||
|
v.FloatSpecial = FloatIsPosInf
|
||||||
|
case math.IsInf(val, -1):
|
||||||
|
v.FloatSpecial = FloatIsNegInf
|
||||||
|
case math.IsNaN(val):
|
||||||
|
v.FloatSpecial = FloatIsNaN
|
||||||
|
}
|
||||||
case reflect.Func:
|
case reflect.Func:
|
||||||
v.readFunctionPtr()
|
v.readFunctionPtr()
|
||||||
default:
|
default:
|
||||||
|
|||||||
@ -98,6 +98,19 @@ func prettyTypeName(typ dwarf.Type) string {
|
|||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func convertFloatValue(v *proc.Variable, sz int) string {
|
||||||
|
switch v.FloatSpecial {
|
||||||
|
case proc.FloatIsPosInf:
|
||||||
|
return "+Inf"
|
||||||
|
case proc.FloatIsNegInf:
|
||||||
|
return "-Inf"
|
||||||
|
case proc.FloatIsNaN:
|
||||||
|
return "NaN"
|
||||||
|
}
|
||||||
|
f, _ := constant.Float64Val(v.Value)
|
||||||
|
return strconv.FormatFloat(f, 'f', -1, sz)
|
||||||
|
}
|
||||||
|
|
||||||
// ConvertVar converts from proc.Variable to api.Variable.
|
// ConvertVar converts from proc.Variable to api.Variable.
|
||||||
func ConvertVar(v *proc.Variable) *Variable {
|
func ConvertVar(v *proc.Variable) *Variable {
|
||||||
r := Variable{
|
r := Variable{
|
||||||
@ -119,11 +132,9 @@ func ConvertVar(v *proc.Variable) *Variable {
|
|||||||
if v.Value != nil {
|
if v.Value != nil {
|
||||||
switch v.Kind {
|
switch v.Kind {
|
||||||
case reflect.Float32:
|
case reflect.Float32:
|
||||||
f, _ := constant.Float64Val(v.Value)
|
r.Value = convertFloatValue(v, 32)
|
||||||
r.Value = strconv.FormatFloat(f, 'f', -1, 32)
|
|
||||||
case reflect.Float64:
|
case reflect.Float64:
|
||||||
f, _ := constant.Float64Val(v.Value)
|
r.Value = convertFloatValue(v, 64)
|
||||||
r.Value = strconv.FormatFloat(f, 'f', -1, 64)
|
|
||||||
case reflect.String, reflect.Func:
|
case reflect.String, reflect.Func:
|
||||||
r.Value = constant.StringVal(v.Value)
|
r.Value = constant.StringVal(v.Value)
|
||||||
default:
|
default:
|
||||||
|
|||||||
@ -448,6 +448,11 @@ func TestEvalExpression(t *testing.T) {
|
|||||||
{"str1[0:12]", false, "", "", "string", fmt.Errorf("index out of bounds")},
|
{"str1[0:12]", false, "", "", "string", fmt.Errorf("index out of bounds")},
|
||||||
{"str1[5:3]", false, "", "", "string", fmt.Errorf("index out of bounds")},
|
{"str1[5:3]", false, "", "", "string", fmt.Errorf("index out of bounds")},
|
||||||
|
|
||||||
|
// NaN and Inf floats
|
||||||
|
{"pinf", false, "+Inf", "+Inf", "float64", nil},
|
||||||
|
{"ninf", false, "-Inf", "-Inf", "float64", nil},
|
||||||
|
{"nan", false, "NaN", "NaN", "float64", nil},
|
||||||
|
|
||||||
// pointers
|
// pointers
|
||||||
{"*p2", false, "5", "5", "int", nil},
|
{"*p2", false, "5", "5", "int", nil},
|
||||||
{"p2", true, "*5", "(*int)(0x…", "*int", nil},
|
{"p2", true, "*5", "(*int)(0x…", "*int", nil},
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user