Skip to content

Commit 454a6d1

Browse files
committed
proc,service: pretty print time.Time variables
Fixes #999
1 parent e3f4c40 commit 454a6d1

6 files changed

Lines changed: 81 additions & 9 deletions

File tree

_fixtures/testvariables2.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"go/constant"
66
"math"
77
"runtime"
8+
"time"
89
"unsafe"
910
)
1011

@@ -359,6 +360,10 @@ func main() {
359360
w5 := &W5{nil}
360361
w5.W5 = w5
361362

363+
tim1 := time.Unix(233431200, 0)
364+
loc, _ := time.LoadLocation("Mexico/BajaSur")
365+
tim2, _ := time.ParseInLocation("2006-01-02 15:04:05", "2022-06-07 02:03:04", loc)
366+
362367
var amb1 = 1
363368
runtime.Breakpoint()
364369
for amb1 := 0; amb1 < 10; amb1++ {
@@ -369,5 +374,5 @@ func main() {
369374
longslice := make([]int, 100, 100)
370375

371376
runtime.Breakpoint()
372-
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)
377+
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)
373378
}

pkg/proc/variables.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"sort"
1414
"strconv"
1515
"strings"
16+
"time"
1617
"unsafe"
1718

1819
"github.com/go-delve/delve/pkg/dwarf/godwarf"
@@ -1304,6 +1305,9 @@ func (v *Variable) loadValueInternal(recurseLevel int, cfg LoadConfig) {
13041305
v.Children[i].loadValueInternal(recurseLevel+1, cfg)
13051306
}
13061307
}
1308+
if t.Name == "time.Time" {
1309+
v.formatTime()
1310+
}
13071311

13081312
case reflect.Interface:
13091313
v.loadInterface(recurseLevel, true, cfg)
@@ -2454,3 +2458,63 @@ type constantValuesByValue []constantValue
24542458
func (v constantValuesByValue) Len() int { return len(v) }
24552459
func (v constantValuesByValue) Less(i int, j int) bool { return v[i].value < v[j].value }
24562460
func (v constantValuesByValue) Swap(i int, j int) { v[i], v[j] = v[j], v[i] }
2461+
2462+
const (
2463+
timeTimeWallHasMonotonicBit uint64 = (1 << 63) // hasMonotonic bit of time.Time.wall
2464+
2465+
//lint:ignore ST1011 addSeconds is the name of the relevant function
2466+
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
2467+
2468+
wallNsecShift = 30 // size of the nanoseconds field of time.Time.wall
2469+
2470+
unixTimestampOfWallEpoch = -2682288000 // number of seconds between the unix epoch and the epoch for time.Time.wall (1 jan 1885)
2471+
)
2472+
2473+
// formatTime writes formatted value of a time.Time to v.Value.
2474+
// See $GOROOT/src/time/time.go for a description of time.Time internals.
2475+
func (v *Variable) formatTime() {
2476+
wallv := v.fieldVariable("wall")
2477+
extv := v.fieldVariable("ext")
2478+
if wallv == nil || extv == nil || wallv.Unreadable != nil || extv.Unreadable != nil || wallv.Value == nil || extv.Value == nil {
2479+
return
2480+
}
2481+
2482+
var loc *time.Location
2483+
2484+
locv := v.fieldVariable("loc")
2485+
if locv != nil && locv.Unreadable == nil {
2486+
namev := locv.loadFieldNamed("name")
2487+
if namev != nil && namev.Unreadable == nil {
2488+
name := constant.StringVal(namev.Value)
2489+
loc, _ = time.LoadLocation(name)
2490+
}
2491+
}
2492+
2493+
wall, _ := constant.Uint64Val(wallv.Value)
2494+
ext, _ := constant.Int64Val(extv.Value)
2495+
2496+
hasMonotonic := (wall & timeTimeWallHasMonotonicBit) != 0
2497+
if hasMonotonic {
2498+
// the 33-bit field of wall holds a 33-bit unsigned wall
2499+
// seconds since Jan 1 year 1885, and ext holds a signed 64-bit monotonic
2500+
// clock reading, nanoseconds since process start
2501+
sec := int64(wall << 1 >> (wallNsecShift + 1)) // seconds since 1 Jan 1885
2502+
t := time.Unix(sec+unixTimestampOfWallEpoch, 0).UTC()
2503+
if loc != nil {
2504+
t = t.In(loc)
2505+
}
2506+
v.Value = constant.MakeString(fmt.Sprintf("%s, %+d", t.Format(time.RFC3339), ext))
2507+
} else {
2508+
// the full signed 64-bit wall seconds since Jan 1 year 1 is stored in ext
2509+
var t time.Time
2510+
for ext > int64(maxAddSeconds/time.Second) {
2511+
t = t.Add(maxAddSeconds)
2512+
ext -= int64(maxAddSeconds / time.Second)
2513+
}
2514+
t = t.Add(time.Duration(ext) * time.Second)
2515+
if loc != nil {
2516+
t = t.In(loc)
2517+
}
2518+
v.Value = constant.MakeString(t.Format(time.RFC3339))
2519+
}
2520+
}

service/api/conversions.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ func VariableValueAsString(v *proc.Variable) string {
261261
return convertFloatValue(v, 32)
262262
case reflect.Float64:
263263
return convertFloatValue(v, 64)
264-
case reflect.String, reflect.Func:
264+
case reflect.String, reflect.Func, reflect.Struct:
265265
return constant.StringVal(v.Value)
266266
default:
267267
if cd := v.ConstDescr(); cd != "" {

service/api/prettyprint.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,10 @@ func (v *Variable) writeTo(buf io.Writer, top, newlines, includeType bool, inden
8989
}
9090
}
9191
case reflect.Struct:
92+
if v.Value != "" {
93+
fmt.Fprintf(buf, "%s(%s)", v.Type, v.Value)
94+
includeType = false
95+
}
9296
v.writeStructTo(buf, newlines, includeType, indent, fmtstr)
9397
case reflect.Interface:
9498
if v.Addr == 0 {

service/dap/server_test.go

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2290,7 +2290,7 @@ func TestVariablesMetadata(t *testing.T) {
22902290
disconnect: false,
22912291
}, {
22922292
execute: func() {
2293-
checkStop(t, client, 1, "main.main", 372)
2293+
checkStop(t, client, 1, "main.main", -1)
22942294

22952295
client.VariablesRequest(localsScope)
22962296
locals := client.ExpectVariablesResponse(t)
@@ -5963,12 +5963,7 @@ func TestSetVariable(t *testing.T) {
59635963
execute: func() {
59645964
tester := &helperForSetVariable{t, client}
59655965

5966-
startLineno := 364 // after runtime.Breakpoint
5967-
if runtime.GOOS == "windows" && goversion.VersionAfterOrEqual(runtime.Version(), 1, 15) {
5968-
startLineno = -1
5969-
}
5970-
5971-
checkStop(t, client, 1, "main.main", startLineno)
5966+
checkStop(t, client, 1, "main.main", -1)
59725967
locals := tester.variables(localsScope)
59735968

59745969
// channel

service/test/variables_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -840,6 +840,10 @@ func TestEvalExpression(t *testing.T) {
840840
{"ni8 << 8", false, "0", "0", "int8", nil},
841841
{"ni8 >> 1", false, "-3", "-3", "int8", nil},
842842
{"bytearray[0] * bytearray[0]", false, "144", "144", "uint8", nil},
843+
844+
// pretty printing special types
845+
{"tim1", false, `time.Time(1977-05-25T18:00:00Z)…`, `time.Time(1977-05-25T18:00:00Z)…`, "time.Time", nil},
846+
{"tim2", false, `time.Time(2022-06-07T02:03:04-06:00)…`, `time.Time(2022-06-07T02:03:04-06:00)…`, "time.Time", nil},
843847
}
844848

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

0 commit comments

Comments
 (0)