• 善用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 '	', align = 8 '', fieldAlign = 8 '', kind = 54 '6', equal = {void (void *, void *, bool *)} 0x4baf00, 
      gcdata = 0x4d8b08 "010203040506a	
    vf
    16172022242526303133343637"%&(,-256<BUXx216231330335345377", str = 7300, ptrToThis = 0}
    (gdb) p *r.tab
    $17 = {inter = 0x4a9400, _type = 0x4baf00, hash = 871609668, _ = "000000", fun = {4749008}}
    (gdb) p *r.tab.inter
    $18 = {typ = {size = 16, ptrdata = 16, hash = 3769182245, tflag = 7 'a', align = 8 '', fieldAlign = 8 '', kind = 20 '24', equal = {void (void *, void *, bool *)} 0x4a9400, 
        gcdata = 0x4d8b09 "0203040506a	
    vf
    16172022242526303133343637"%&(,-256<BUXx216231330335345377", 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
    ", e)
                    }  
            }()
            if s == nil {
                    fmt.Printf("judge1:s(%v) == nil
    ", s)
                    return
            }  
            if reflect.ValueOf(s).IsNil() {
                    fmt.Printf("judge2:s(%v) reflect nil
    ", s)
                    return
            }  
            fmt.Printf("s(%v) pass
    ", 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 '', 
        fieldAlign = 8 '', 
        kind = 20 '24', 
        equal = {void (void *, void *, bool *)} 0x4a9400, 
        gcdata = 0x4d8b09 "0203040506a	
    vf
    16172022242526303133343637"%&(,-256<BUXx216231330335345377", 
        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

    一个没有高级趣味的人。 email:hushui502@gmail.com
  • 相关阅读:
    ubuntu16.04设置开机自启服务
    Flask架构管理及特点(重要)
    Django开发框架知识点
    Django进行数据迁移时,报错:(1064, "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '(6) NOT NULL)' at line 1")
    Java 网络编程(二) 两类传输协议:TCP UDP
    Java 网络编程(一) 网络基础知识
    .NET开源项目
    手写ArrayList、LinkedList
    命令查看java的class字节码文件
    dubbo总结
  • 原文地址:https://www.cnblogs.com/CherryTab/p/12818706.html
Copyright © 2020-2023  润新知