There are two things that make hacking deepequals difficult
1) You cant just use print statements.
Because deepequals is used alot, you can't import a few important go libraries like "fmt" into it. This means you cant just chop it up with print statements.
2) It isnt recompiled automatically!
When you run something like "go test", we're used to being able to modify source on the fly. However, the GOROOT libraries *arent* recompiled by default!
So, here how I hacked deep equals to see what was going on in a recent kube patch.
1) Write DeepEquals2, with return value "(bool, string)"
2) call DeepEquals2 instead of DeepEquals wherever I'm interested in debugging
3) Return string debugging statements along the boolean value in (1).
4) Run go install -a std after hacking up DeepEquals2. This causes golang to rebuild the binaries, so that the changes are compiled into your code.
Not that hard to do, but took me a while to figure this out :). FYI, this is what the diff to deep equals winds up looking like. The salient parts are.
- adding DeepEqual2 (bool, string)
- adding deepvalueEqual2 (bool string) as the underlying implementation
- adding debug strings to the end of each return in the above functions.
12c12
< // in progress. The comparison algorithm assumes that all
---
> // in progress. The comparison algorithm assumes that all
131,264d130
< // Tests for deep equality using reflected types. The map argument tracks
< // comparisons that have already been seen, which allows short circuiting on
< // recursive types.
< func deepValueEqual2(v1, v2 Value, visited map[visit]bool, depth int) (bool, string) {
< if !v1.IsValid() || !v2.IsValid() {
< return v1.IsValid() == v2.IsValid(),"both are invalid"
< }
< if v1.Type() != v2.Type() {
< return false,"not same type"
< }
<
< // if depth > 10 { panic("deepValueEqual") } // for debugging
< hard := func(k Kind) (bool,string) {
< switch k {
< case Array, Map, Slice, Struct:
< return true,"array map sl str"
< }
< return false,"not simple type"
< }
<
< isHard , _ := hard(v1.Kind())
< if v1.CanAddr() && v2.CanAddr() && isHard {
< addr1 := unsafe.Pointer(v1.UnsafeAddr())
< addr2 := unsafe.Pointer(v2.UnsafeAddr())
< if uintptr(addr1) > uintptr(addr2) {
< // Canonicalize order to reduce number of entries in visited.
< // Assumes non-moving garbage collector.
< addr1, addr2 = addr2, addr1
< }
<
< // Short circuit if references are already seen.
< typ := v1.Type()
< v := visit{addr1, addr2, typ}
< if visited[v] {
< return true, "visted already"
< }
<
< // Remember for later.
< visited[v] = true
< }
<
< switch v1.Kind() {
< case Array:
< for i := 0; i < v1.Len(); i++ {
< if !deepValueEqual(v1.Index(i), v2.Index(i), visited, depth+1) {
< return false,"deep eq fail array"
< }
< }
< return true,"array tr"
< case Slice:
< if v1.IsNil() != v2.IsNil() {
< return false,"nil is diff"
< }
< if v1.Len() != v2.Len() {
< return false,"len is diff"
< }
< if v1.Pointer() == v2.Pointer() {
< return true, " pointers are eq"
< }
< for i := 0; i < v1.Len(); i++ {
< if !deepValueEqual(v1.Index(i), v2.Index(i), visited, depth+1) {
< return false,"found a dif value"
< }
< }
< return true, "blah"
< case Interface:
< if v1.IsNil() || v2.IsNil() {
< return v1.IsNil() == v2.IsNil(), "not both sma nil val"
< }
< return deepValueEqual(v1.Elem(), v2.Elem(), visited, depth+1), "interf deep eq"
< case Ptr:
< if v1.Pointer() == v2.Pointer() {
< return true,"ptr are sm"
< }
< return deepValueEqual(v1.Elem(), v2.Elem(), visited, depth+1),"pointer comparison"
< case Struct:
< for i, n := 0, v1.NumField(); i < n; i++ {
< if !deepValueEqual(v1.Field(i), v2.Field(i), visited, depth+1) {
< return false,"! deep eq struct"
< }
< }
< return true,"str"
< case Map:
< if v1.IsNil() != v2.IsNil() {
< if v1.IsNil(){
< return false,"map nillity 1 is nil, 2 is not"
< } else {
< return false,"map nillity 2 is nil, 1 is not"
< }
< }
< if v1.Len() != v2.Len() {
< return false,"len dif"
< }
< if v1.Pointer() == v2.Pointer() {
< return true,"ptr same"
< }
< for _, k := range v1.MapKeys() {
< val1 := v1.MapIndex(k)
< val2 := v2.MapIndex(k)
< if !val1.IsValid() || !val2.IsValid() || !deepValueEqual(v1.MapIndex(k), v2.MapIndex(k), visited, depth+1) {
< return false, " map differences "
< }
< }
< return true, "map eq"
< case Func:
< if v1.IsNil() && v2.IsNil() {
< return true, "func nillness "
< }
< // Can't do better than this:
< return false,"func"
< default:
< // Normal equality suffices
< return valueInterface(v1, false) == valueInterface(v2, false), "regular equality"
< }
< }
<
< func DeepEqual2(x, y interface{}) ( bool, string ) {
< if x == nil || y == nil {
< if x == y {
< return true, "de2 nilxy"
< } else {
< return false, "de2 nilxy"
< }
<
< return x == y, "d2 nility "
< }
< v1 := ValueOf(x)
< v2 := ValueOf(y)
< if v1.Type() != v2.Type() {
< return false,"types"
< }
< return deepValueEqual2(v1, v2, make(map[visit]bool), 0)
< }
<
312,317d177
< if x == y {
< return true
< } else {
< return false
< }
<
323c183
< return false
---
> return false
327d186
<
No comments:
Post a Comment