善用debugger学习

学习自曹大

 

之前曹大和雨痕大神的书都涉及过一些debugger的高级用法,但是自己还是没用起来,这次借助整理再熟悉下,以后就用起来。

 

 

var a = new(T) 和 var a = &T{} 这两种语法有区别么?

type T struct {
}

func main() {
    var a = new(T)
    var b = &T{}
    println(a, b)
}

 

推荐是使用第二种,但这不是本随笔的重点。我们可以在gdb里看一下,其实不如go tool objdump/compile 清晰,这里只是展示有这个功能,至于分析,明显这两个就不是一个方式实现的,但是开启优化后就会成一样的了。

,(gdb) disassemble main.main
Dump of assembler code for function main.main:
=> 0x00000000004581d0 <+0>:    mov    %fs:0xfffffffffffffff8,%rcx
   0x00000000004581d9 <+9>:    cmp    0x10(%rcx),%rsp
   0x00000000004581dd <+13>:    jbe    0x458240 <main.main+112>
   0x00000000004581df <+15>:    sub    $0x30,%rsp
   0x00000000004581e3 <+19>:    mov    %rbp,0x28(%rsp)
   0x00000000004581e8 <+24>:    lea    0x28(%rsp),%rbp
   0x00000000004581ed <+29>:    lea    0xf(%rsp),%rax
  // new(T)
0x00000000004581f2 <+34>: mov %rax,0x18(%rsp) 0x00000000004581f7 <+39>: lea 0xf(%rsp),%rax
  // &T{}
0x00000000004581fc <+44>: mov %rax,0x20(%rsp) 0x0000000000458201 <+49>: mov %rax,0x10(%rsp)
0x0000000000458206 <+54>: callq 0x42b010 <runtime.printlock> 0x000000000045820b <+59>: mov 0x18(%rsp),%rax 0x0000000000458210 <+64>: mov %rax,(%rsp) 0x0000000000458214 <+68>: callq 0x42b910 <runtime.printpointer> 0x0000000000458219 <+73>: callq 0x42b250 <runtime.printsp> 0x000000000045821e <+78>: mov 0x10(%rsp),%rax 0x0000000000458223 <+83>: mov %rax,(%rsp) 0x0000000000458227 <+87>: callq 0x42b910 <runtime.printpointer> 0x000000000045822c <+92>: callq 0x42b2a0 <runtime.printnl> 0x0000000000458231 <+97>: callq 0x42b090 <runtime.printunlock> 0x0000000000458236 <+102>: mov 0x28(%rsp),%rbp 0x000000000045823b <+107>: add $0x30,%rsp 0x000000000045823f <+111>: retq 0x0000000000458240 <+112>: callq 0x4519d0 <runtime.morestack_noctxt> 0x0000000000458245 <+117>: jmp 0x4581d0 <main.main> End of assembler dump.

 

 

查看 go 的 interface 的数据结构

package main

import (
    "fmt"
    "io"
    "os"
)

var (
    v  interface{}
    r  io.Reader
    f  *os.File
    fn os.File
)

func main() {
    fmt.Println(v == nil)
    fmt.Println(r == nil)
    fmt.Println(f == nil)
    v = r
    fmt.Println(v == nil)
    v = fn
    fmt.Println(v == nil)
    v = f
    fmt.Println(v == nil)
    r = f
    fmt.Println(r == nil)
}
true
true
true
true
false
false
false

是不是很奇怪,为什么都是”nil“接口却最后不等于nil了,解释可以解释接口有两个指针不一样,但是这好像不足以服众,还是用debugger看一下吧。

 

(gdb) b main.main
Breakpoint 1 at 0x491c70: file /root/main.go, line 16.
(gdb) r
Starting program: /root/main 

Breakpoint 1, main.main () at /root/main.go:16
16    func main() {
(gdb) n
17        fmt.Println(v == nil)
(gdb) n
true
18        fmt.Println(r == nil)
(gdb) n
true
19        fmt.Println(f == nil)
(gdb) n
true
20        v = r
(gdb) p r
$1 = {tab = 0x0, data = 0x0}
(gdb) p v
$2 = {_type = 0x0, data = 0x0}

(gdb) p f

  $3 = (os.File *) 0x0

在 golang 中空 interface 和非空 interface 在数据结构上是不同的。空 interface 就只有 runtime._type 和 void* 指针组成。而非空 interface 则是 runtime.itab 和 void* 指针组成。

 

26        r = f
(gdb) n
27        fmt.Println(r == nil)
(gdb) n
false
28    }(gdb) p r
$14 = {tab = 0x4dc480 <File,io.Reader>, data = 0x0}
(gdb) p v
$15 = {_type = 0x4baf00, data = 0x0}
(gdb) p *v._type
$16 = {size = 8, ptrdata = 8, hash = 871609668, tflag = 9 '\t', align = 8 '\b', fieldAlign = 8 '\b', kind = 54 '6', equal = {void (void *, void *, bool *)} 0x4baf00, 
  gcdata = 0x4d8b08 "\001\002\003\004\005\006\a\b\t\n\v\f\r\016\017\020\022\024\025\026\030\031\033\034\036\037\"%&(,-256<BUXx\216\231\330\335\345\377", str = 7300, ptrToThis = 0}
(gdb) p *r.tab
$17 = {inter = 0x4a9400, _type = 0x4baf00, hash = 871609668, _ = "\000\000\000", fun = {4749008}}
(gdb) p *r.tab.inter
$18 = {typ = {size = 16, ptrdata = 16, hash = 3769182245, tflag = 7 '\a', align = 8 '\b', fieldAlign = 8 '\b', kind = 20 '\024', equal = {void (void *, void *, bool *)} 0x4a9400, 
    gcdata = 0x4d8b09 "\002\003\004\005\006\a\b\t\n\v\f\r\016\017\020\022\024\025\026\030\031\033\034\036\037\"%&(,-256<BUXx\216\231\330\335\345\377", str = 11039, ptrToThis = 36672}, 
  pkgpath = {bytes = 0x493064 ""}, mhdr = {array = 0x4a9460, len = 1, cap = 1}}
(gdb) 

如果你把一个非空 interface 类型的 nil 值的 interface 变量赋值给一个空 interface 类型的变量,那么就会得到一个非空类型的非空 interface 变量。

当然有一个区分的手段

func isNil(s Stringer) {
        defer func() {
                if e := recover(); e != nil {
                        fmt.Printf("panic in isNil(), err:%v\n", e)
                }  
        }()
        if s == nil {
                fmt.Printf("judge1:s(%v) == nil\n", s)
                return
        }  
        if reflect.ValueOf(s).IsNil() {
                fmt.Printf("judge2:s(%v) reflect nil\n", s)
                return
        }  
        fmt.Printf("s(%v) pass\n", s)
}
————————————————
版权声明:本文为CSDN博主「xiaohu50」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/xiaohu50/article/details/50318963

 

 

 

 

大致就是这么个debugger思路,当然还有很多玩法,直接左拐到曹大那学,我再重复没太大意义(虽然也重复了很多orz)。

https://xargin.com/debugger/

 

最后一个小tip,如何让gdb输出的按一个结构体字段来换行,不是很懂为什么这不是默认的。

set print pretty on
(gdb) set print pretty on
(gdb) p *r.tab.inter
$19 = {
  typ = {
    size = 16, 
    ptrdata = 16, 
    hash = 3769182245, 
    tflag = 7 '\a', 
    align = 8 '\b', 
    fieldAlign = 8 '\b', 
    kind = 20 '\024', 
    equal = {void (void *, void *, bool *)} 0x4a9400, 
    gcdata = 0x4d8b09 "\002\003\004\005\006\a\b\t\n\v\f\r\016\017\020\022\024\025\026\030\031\033\034\036\037\"%&(,-256<BUXx\216\231\330\335\345\377", 
    str = 11039, 
    ptrToThis = 36672
  }, 
  pkgpath = {
    bytes = 0x493064 ""
  }, 
  mhdr = {
    array = 0x4a9460, 
    len = 1, 
    cap = 1
  }
}

 详情

https://wizardforcel.gitbooks.io/100-gdb-tips/set-print-pretty-on.html

 

end

posted @ 2020-05-02 17:11  zhangyu63  阅读(245)  评论(0编辑  收藏  举报