提交 d89d115e 编写于 作者: A Alessandro Arzilli 提交者: Derek Parker

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
上级 449b276f
...@@ -3,6 +3,7 @@ package main ...@@ -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() { ...@@ -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() { ...@@ -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)
} }
...@@ -3,6 +3,7 @@ package proc ...@@ -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 ( ...@@ -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) { ...@@ -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) { ...@@ -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 ( ...@@ -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 ( ...@@ -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 { ...@@ -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) { ...@@ -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 { ...@@ -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 { ...@@ -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) { ...@@ -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},
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册