• 请不要在回答技术问题时复制粘贴 AI 生成的内容
wqgogogo
V2EX  ›  程序员

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

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

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

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

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

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

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