V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
wqgogogo
V2EX  ›  程序员

想到一个有趣的问题,为什么 debug 模式可以进断点而 run 模式不行?

  •  1
     
  •   wqgogogo · 9 天前 · 3223 次点击
    39 条回复    2020-11-21 16:57:50 +08:00
    magiclx
        1
    magiclx   9 天前   ❤️ 23
    因为当在 debug 模式下一个断点时,IDE 将断点处程序的二进制替换为了 0xCC,即 INT 3 指令,当程序执行到 INT 3 时,再替换回原指令执行,而 run 模式并未做这个事。
    ZSeptember
        2
    ZSeptember   9 天前
    debug 模式,编译器什么的会给生成的可执行文件加入很多调试相关的信息
    Guaidaodl
        3
    Guaidaodl   9 天前
    不同的语言采用的方式略有区别. 建议明确一下语言
    LuckyKoala
        4
    LuckyKoala   9 天前   ❤️ 1
    调试模式运行的时候,会有类似 DWARF 的调试文件生成,包含运行的指令与源文件行号、函数等信息,然后运行的时候会执行 ptrace 类似的函数,如 #1 所说,替换断点处的机器指令,可以查询当时各寄存器的值等信息。

    如果有源文件的话,IDE 支持在程序运行之后再把调试器挂载到指定进程。

    而普通模式运行的时候是很少与源文件相关的信息,不方便你对着源码去调试。
    wqgogogo
        5
    wqgogogo   9 天前
    @Guaidaodl Java 语言,idea 调试 Java 程序的时候
    luckyrayyy
        6
    luckyrayyy   9 天前   ❤️ 13
    为什么你们连这个都懂...
    wqgogogo
        7
    wqgogogo   9 天前
    @magiclx
    @LuckyKoala
    这个都完全没接触过,又增加了新的知识点🙇‍
    atonku
        8
    atonku   9 天前
    一拍脑门,为什么我就没想到呢
    wqgogogo
        9
    wqgogogo   9 天前
    @atonku 哈哈,有时候有很多奇怪的想法,过一会就忘了,赶紧发出来分享下 ^_^
    msaionyc
        10
    msaionyc   9 天前
    因为 run 模式不需要进断点
    scar263
        11
    scar263   9 天前
    楼主你这是想得太多学得太少,建议多看看基础知识的书籍。
    ju5t4fun
        12
    ju5t4fun   9 天前
    硬件支持,操作系统提供 api,编译器提供符号,调试器提供功能,想要理解调试的原理需要很多知识
    《软件调试》张银奎,这本书很不错,楼主有兴趣的话可以读一下
    yangJunKing
        13
    yangJunKing   9 天前
    GrayXu
        14
    GrayXu   9 天前
    正经科班都学过编译原理这门课吧。。
    cheng6563
        15
    cheng6563   9 天前 via Android
    Java 其实就是开了个特殊端口给调试器调用,其他没区别了
    warush
        16
    warush   9 天前 via iPhone
    @GrayXu 你看看人楼怎么回复的
    warush
        17
    warush   9 天前 via iPhone
    @GrayXu 言语中暗贬楼主 不正经、非科班 呗
    chaleaoch
        18
    chaleaoch   9 天前
    C 语言的话 GCC 编译参数不一样.
    生成的.o 文件也不一样.
    譬如 需要将机器码和代码做映射.

    另外执行方式也不同. 参考 ptrace.
    Team
        19
    Team   9 天前   ❤️ 3
    @warush #17
    同意,装的自己很牛,啥信息也没提供.
    msg7086
        20
    msg7086   9 天前
    除了加入调试相关信息外,debug 模式通常还会禁用代码优化,保证你写的什么就生成什么。
    release 下代码会经过大量优化,有可能你洋洋洒洒写了一大段的计算,最后被优化成一个常量什么的。
    像是 C 系的还会做 unroll 或者 vectorize,不关优化的话根本没法调试的。
    raaaaaar
        21
    raaaaaar   9 天前 via Android
    好强。。
    fl2d
        22
    fl2d   9 天前
    matlab 随时可停🤔
    no1xsyzy
        23
    no1xsyzy   9 天前
    以前 NOIP 用 Free Pascal 的时候看到文章说简单花指令 asm push pop 一下
    然后看到 FP 有 asm,顺便玩了下,然后发现编译输出 debug 的话仅仅 asm push xxx; pop xxx; end; fc 一看,差了非常非常多的部分。而如果编译输出为 release,就只多了几个字节。
    atempcode
        25
    atempcode   9 天前
    Java 的 debug 不是加 int 3 的,是 JVM 处理的。
    Guaidaodl
        26
    Guaidaodl   9 天前
    @wqgogogo JPDA 了解一下.
    GrayXu
        27
    GrayXu   9 天前
    @warush @Team 这么敏感吗,这个问题的最简单的解答,就是 1L 说的 INT 3,学了编译原理或者汇编什么的就能理解了。这种东西都能装逼那门槛也太低了…建议大家以及在读的本科学生好好学习 CS 基础核心课程。
    akira
        28
    akira   9 天前
    release 模式的话,可以用 ollydb 之类的来设置断点,唔。。。。
    xuboying
        29
    xuboying   9 天前
    @GrayXu #27 忽然想起一个梗,程序员分为 10 类,一类是折腾过硬件的,一类是没折腾过硬件的。
    xiangyuecn
        30
    xiangyuecn   9 天前
    @magiclx #1 我胡乱猜测的:目测你说的这个应该是执行的时候发生的替换(几乎不可能是修改的编译后的文件),那么非 debug 模式,按道理也能进行替换,甚至插入指令。意思就是不管是不是 debug 模式都能 debug 。

    唯一想到的可能就是:非 debug 模式下不好去定位到源文件,也不好去处理变量名(也许都丢失了),然后调试器的开发者懒得去支持非 debug 模式调试😂 任何语言通用,瞎猜的,无非就是为了避免使用者来找麻烦。
    vantis
        31
    vantis   9 天前 via Android
    @xuboying 应该说 0 类是没折腾过硬件的 高电平是折腾过硬件的 233
    luhe
        32
    luhe   9 天前
    软件工程专业...没学过编译原理...我确定学校没有安排这门课程...
    afx
        33
    afx   9 天前
    @GrayXu 编译原理真的讲这个吗
    irytu
        34
    irytu   9 天前 via iPhone
    @magiclx 是的 再补充一个小细节,就是替换回原来的指令之后还需要把用户态 eip 往前挪一个字节,因为 trap 到内核后,内核栈存的 eip 是被替换指令字节的下一条指令,不然再次执行被替换指令就被 skipped 了
    lujie2012
        35
    lujie2012   9 天前
    前面有楼主说到位了,建议看一下 LLVM 编译原理这本书。简单说,run 的编译过程,做了指令集的优化处理,而 debug 模式可以不做指令集的优化,另外会在编译的代码中加入调试代码,只有这样才能实时的 IDE 中看到内存和执行位置。
    release 模式直接是汇编或者说是到了机器码,无调试,所以为了找到那一段代码 crash,会生成其他的文件来定位。

    debug 过程生产的代码未必到了汇编或者机器码,而只是编译器自己解析的逻辑代码,当然可以 debug 。
    MineDog
        36
    MineDog   8 天前
    java 的话 debug 是 jvm 层面实现的,ide 只是在调用接口,具体没深入了解过
    amimo
        37
    amimo   8 天前
    如果是 native 程序的话,这个问题的主要跟“调试器”相关,debug 模式下运行的程序受调试器控制。native 调试器实现主要依赖硬件和操作系统提供的调试机制,跟编译器,编译原理无关。
    lewis89
        38
    lewis89   8 天前
    @MineDog #36 道理是差不多的,都是在二进制指令 生成了中断指令,或者使用 mprotect 这种系统调用 触发一个软中断调用
    mingl0280
        39
    mingl0280   8 天前 via Android
    归根结底不是什么 INT3 的问题(说这个的你用过 od/IDA 么),是 release 模式有优化,编译器会打乱代码行序并且清除大量的编译相关信息以提升运行性能。这样你最多能看到调用了啥函数(某些 stripped 了的程序你连调用栈信息都看不到,里面只剩各种指针了),内部有什么指令偏移多少的信息没了,调试器看到你的源文件的时候不知道去哪里找对应的指令下断点。
    为啥 vs 的 release 模式就支持下断点呢,人家有 pdb 文件啊!
    debug 模式下编译器会在程序中插入调试用的符号表,以及使用未优化的代码(确保每行源代码都能对应上特定的二进制指令),所以下断点容易得多。
    关于   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   1047 人在线   最高记录 5268   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 19:39 · PVG 03:39 · LAX 11:39 · JFK 14:39
    ♥ Do have faith in what you're doing.