Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion _fixtures/testvariables2.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"go/constant"
"math"
"runtime"
"time"
"unsafe"
)

Expand Down Expand Up @@ -359,6 +360,10 @@ func main() {
w5 := &W5{nil}
w5.W5 = w5

tim1 := time.Unix(233431200, 0)
loc, _ := time.LoadLocation("Mexico/BajaSur")
tim2, _ := time.ParseInLocation("2006-01-02 15:04:05", "2022-06-07 02:03:04", loc)

var amb1 = 1
runtime.Breakpoint()
for amb1 := 0; amb1 < 10; amb1++ {
Expand All @@ -369,5 +374,5 @@ func main() {
longslice := make([]int, 100, 100)

runtime.Breakpoint()
fmt.Println(i1, i2, i3, p1, pp1, amb1, s1, s3, a0, a1, p2, p3, s2, as1, str1, f1, fn1, fn2, nilslice, nilptr, ch1, chnil, m1, mnil, m2, m3, m4, m5, upnil, 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, ni64, pinf, ninf, nan, zsvmap, zsslice, zsvar, tm, rettm, errtypednil, emptyslice, emptymap, byteslice, bytestypeslice, runeslice, bytearray, bytetypearray, runearray, longstr, nilstruct, as2, as2.NonPointerRecieverMethod, s4, iface2map, issue1578, ll, unread, w2, w3, w4, w5, longarr, longslice, val, m6, m7, cl)
fmt.Println(i1, i2, i3, p1, pp1, amb1, s1, s3, a0, a1, p2, p3, s2, as1, str1, f1, fn1, fn2, nilslice, nilptr, ch1, chnil, m1, mnil, m2, m3, m4, m5, upnil, 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, ni64, pinf, ninf, nan, zsvmap, zsslice, zsvar, tm, rettm, errtypednil, emptyslice, emptymap, byteslice, bytestypeslice, runeslice, bytearray, bytetypearray, runearray, longstr, nilstruct, as2, as2.NonPointerRecieverMethod, s4, iface2map, issue1578, ll, unread, w2, w3, w4, w5, longarr, longslice, val, m6, m7, cl, tim1, tim2)
}
64 changes: 64 additions & 0 deletions pkg/proc/variables.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"sort"
"strconv"
"strings"
"time"
"unsafe"

"github.com/go-delve/delve/pkg/dwarf/godwarf"
Expand Down Expand Up @@ -1304,6 +1305,9 @@ func (v *Variable) loadValueInternal(recurseLevel int, cfg LoadConfig) {
v.Children[i].loadValueInternal(recurseLevel+1, cfg)
}
}
if t.Name == "time.Time" {
v.formatTime()
}

case reflect.Interface:
v.loadInterface(recurseLevel, true, cfg)
Expand Down Expand Up @@ -2454,3 +2458,63 @@ type constantValuesByValue []constantValue
func (v constantValuesByValue) Len() int { return len(v) }
func (v constantValuesByValue) Less(i int, j int) bool { return v[i].value < v[j].value }
func (v constantValuesByValue) Swap(i int, j int) { v[i], v[j] = v[j], v[i] }

const (
timeTimeWallHasMonotonicBit uint64 = (1 << 63) // hasMonotonic bit of time.Time.wall

//lint:ignore ST1011 addSeconds is the name of the relevant function
maxAddSeconds time.Duration = (time.Duration(^uint64(0)>>1) / time.Second) * time.Second // maximum number of seconds that can be added with (time.Time).Add, measured in nanoseconds

wallNsecShift = 30 // size of the nanoseconds field of time.Time.wall

unixTimestampOfWallEpoch = -2682288000 // number of seconds between the unix epoch and the epoch for time.Time.wall (1 jan 1885)
)

// formatTime writes formatted value of a time.Time to v.Value.
// See $GOROOT/src/time/time.go for a description of time.Time internals.
func (v *Variable) formatTime() {
wallv := v.fieldVariable("wall")
extv := v.fieldVariable("ext")
if wallv == nil || extv == nil || wallv.Unreadable != nil || extv.Unreadable != nil || wallv.Value == nil || extv.Value == nil {
return
}

var loc *time.Location

locv := v.fieldVariable("loc")
if locv != nil && locv.Unreadable == nil {
namev := locv.loadFieldNamed("name")
if namev != nil && namev.Unreadable == nil {
name := constant.StringVal(namev.Value)
loc, _ = time.LoadLocation(name)
}
}

wall, _ := constant.Uint64Val(wallv.Value)
ext, _ := constant.Int64Val(extv.Value)

hasMonotonic := (wall & timeTimeWallHasMonotonicBit) != 0
if hasMonotonic {
// the 33-bit field of wall holds a 33-bit unsigned wall
// seconds since Jan 1 year 1885, and ext holds a signed 64-bit monotonic
// clock reading, nanoseconds since process start
sec := int64(wall << 1 >> (wallNsecShift + 1)) // seconds since 1 Jan 1885
t := time.Unix(sec+unixTimestampOfWallEpoch, 0).UTC()
if loc != nil {
t = t.In(loc)
}
v.Value = constant.MakeString(fmt.Sprintf("%s, %+d", t.Format(time.RFC3339), ext))
} else {
// the full signed 64-bit wall seconds since Jan 1 year 1 is stored in ext
var t time.Time
for ext > int64(maxAddSeconds/time.Second) {
t = t.Add(maxAddSeconds)
ext -= int64(maxAddSeconds / time.Second)
}
t = t.Add(time.Duration(ext) * time.Second)
if loc != nil {
t = t.In(loc)
}
v.Value = constant.MakeString(t.Format(time.RFC3339))
}
}
2 changes: 1 addition & 1 deletion service/api/conversions.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ func VariableValueAsString(v *proc.Variable) string {
return convertFloatValue(v, 32)
case reflect.Float64:
return convertFloatValue(v, 64)
case reflect.String, reflect.Func:
case reflect.String, reflect.Func, reflect.Struct:
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All structs?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there's a reason to be more specific.

return constant.StringVal(v.Value)
default:
if cd := v.ConstDescr(); cd != "" {
Expand Down
4 changes: 4 additions & 0 deletions service/api/prettyprint.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ func (v *Variable) writeTo(buf io.Writer, top, newlines, includeType bool, inden
}
}
case reflect.Struct:
if v.Value != "" {
fmt.Fprintf(buf, "%s(%s)", v.Type, v.Value)
includeType = false
}
v.writeStructTo(buf, newlines, includeType, indent, fmtstr)
case reflect.Interface:
if v.Addr == 0 {
Expand Down
9 changes: 2 additions & 7 deletions service/dap/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2290,7 +2290,7 @@ func TestVariablesMetadata(t *testing.T) {
disconnect: false,
}, {
execute: func() {
checkStop(t, client, 1, "main.main", 372)
checkStop(t, client, 1, "main.main", -1)

client.VariablesRequest(localsScope)
locals := client.ExpectVariablesResponse(t)
Expand Down Expand Up @@ -5963,12 +5963,7 @@ func TestSetVariable(t *testing.T) {
execute: func() {
tester := &helperForSetVariable{t, client}

startLineno := 364 // after runtime.Breakpoint
if runtime.GOOS == "windows" && goversion.VersionAfterOrEqual(runtime.Version(), 1, 15) {
startLineno = -1
}

checkStop(t, client, 1, "main.main", startLineno)
checkStop(t, client, 1, "main.main", -1)
locals := tester.variables(localsScope)

// channel
Expand Down
4 changes: 4 additions & 0 deletions service/test/variables_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -850,6 +850,10 @@ func TestEvalExpression(t *testing.T) {
{"unknownthing(2)", false, "", "", "", errors.New("could not evaluate function or type unknownthing: could not find symbol value for unknownthing")},
{"(*unknownthing)(2)", false, "", "", "", errors.New("could not evaluate function or type (*unknownthing): could not find symbol value for unknownthing")},
{"(*strings.Split)(2)", false, "", "", "", errors.New("could not evaluate function or type (*strings.Split): could not find symbol value for strings")},

// pretty printing special types
{"tim1", false, `time.Time(1977-05-25T18:00:00Z)…`, `time.Time(1977-05-25T18:00:00Z)…`, "time.Time", nil},
Comment thread
derekparker marked this conversation as resolved.
{"tim2", false, `time.Time(2022-06-07T02:03:04-06:00)…`, `time.Time(2022-06-07T02:03:04-06:00)…`, "time.Time", nil},
}

ver, _ := goversion.Parse(runtime.Version())
Expand Down