DRAFT

提醒

课程作业

  • Research Proposal (draft)
    • 本月底截止
  • Programming Assignments
    • 在学期后半部分截止
    • 请安排好时间

英文 paper presentation session (10.23, 下次课)

  • (崔子寒) Learning from mistakes — A comprehensive study on real world concurrency bug characteristics.
  • (付毅科) Understanding integer overflow in C/C++.
  • (王通) On the naturalness of software.
  • (张明亚) Why we refactor? Confessions of GitHub contributors.

本讲主题

静态分析

  • 从源代码中推断程序的性质
    • “总结性” 地概括了所有执行的行为

动态分析

  • 观察一次程序执行中发生的事件
    • 关注程序一次执行的行为

状态机与动态分析

计算机系统 = 程序 = 状态机

这是对 “计算机” 的一个非常 fundamental 的理解。

以多线程程序 (多处理器系统) 为例

  • 状态 = 共享内存 + 每个线程寄存器现场的数值
  • 转移 = 选择一个线程执行一条指令

动态分析 = 检查状态机转移序列

动态程序分析:通过执行程序、观测其行为从而得到程序各方面信息 (bugs、性能、需求实现情况等) 的程序分析技术。


简单说,就是检查状态机产生的状态序列

  • 推导出关于本次执行的性质
    • 与静态分析 (sound) 相反,一般没有误报

实现最基础的动态分析:调试器

The GNU Project Debugger (GDB)

GDB, the GNU Project debugger, allows you to see what is going on “inside” another program while it executes -- or what another program was doing at the moment it crashed.

  • Start your program, specifying anything that might affect its behavior.
  • Make your program stop on specified conditions.
  • Examine what has happened, when your program has stopped.
  • Change things in your program, so you can experiment with correcting the effects of one bug and go on to learn about another.

GDB 命令

命令行交互

  • 执行类 r, c, f, s, si,...
  • 打印类 p, x, i, bt, ...
  • 断点类 b, hb, wa, ...
  • 修改类 set, ...
  • 黑科技类 rc, rn, rsi, ...

使用 GDB

  • 内置 layouts
  • 可以作为其他程序的后端 (例如 vscode)

GDB: 实现

最重要的功能是指令级的断点

  • 指令是状态转移的最小单位
    • 语句级断点:编译器提供语句到指令的对应 (dwarf 标准)
    • 单步调试:在下一条指令打上断点

实现指令级的断点:Program Instrumentation (插桩)

  • 将断点处的指令替换为 debug_trap(),跳转后把原指令恢复
  • x86 提供了 INT3 指令
    • 0xcc,仅一个字节,专门用于调试
    • 其他 trap 指令都需要两个字节 0xcd $trap
  • 但要求指令内存可写 (不可写时可借助硬件断点)

INT3:不妨试一试

int main() {
  asm volatile ("int $3");
}

$ ./a.out
Trace/breakpoint trap (core dumped)

ptrace 系统调用可以将被追踪进程的 trap 通知追踪进程 (gdb)

  • 如果单步 (stepi),将会直接 “越过” int $3

调试器:动态分析的概念基础

任何动态分析都是 “简化的” 调试器。

调试器实现了

  • 精确的程序执行流控制
    • info inferiors; thread tid
    • 能记录程序执行的每条指令
  • 在程序运行过程中修改程序的状态
    • set var = value

当你试图理解动态分析技术时

  • 想象动态分析就在 “调试” 程序就好了

动态分析技术

从调试器到动态分析

调试器是好,但性能太差、记录太多

  • 1000$\times$ slowdown
  • 换算到原程序执行,每秒几个 GB 的指令 log
    • (有一些硬件的解决方案,例如 Intel PT)
  • 在日志上的 log 也会运行非常多时间

核心问题:如何针对特定软件工程问题实现轻量级的记录高效的分析算法

问题分析

问题空间

  • 分析什么性质?
    • follow 已有的工作
    • 开脑洞/经验/
    • 从实际中找新问题

设计空间

  • 记录什么信息 (系统设计)
  • 如何高效记录 (系统 hacking)
  • 怎样进行分析 (算法设计)

软件工程中的动态分析:精简调试日志

我们并不需要指令级的完整 log

  • 可以对调试信息做某一方面的精简!
  • 大家思考怎样记录、可以做什么分析

profile 问题: 统计同一类代码的执行信息

  • 记录覆盖率 (执行与否)
  • 记录时间 (执行时间总和)

trace 问题: 打印事件日志

  • 记录系统调用
  • 记录函数调用
  • 记录内存访问 (PA2)

案例 (1): 性能监视器

我的程序的性能尚不能满足要求

  • 是系统某部分需要调优,还是我引入了性能 bug?不要瞎猜!
    • premature optimization is the root of all evil (D. E. Knuth)
    • 让 profiler 告诉你 (gprof, perf/systemtap, VisualVM, ...)

案例 (2): 程序理解/性质推导

Invariant Mining

  • Daikon
    • 我看到的是应该发生的
    • 我看不到的是不应该发生的

在各种场合都很好用

  • 一般程序
  • CPS 程序
  • 并发程序
  • 分布式系统
  • 开脑洞:maybe smart contracts?

案例 (3): Bug 检测

我们已知一些 bug patterns,在运行时检测就好了

  • AddressSanitizer (ASan)
    • use-after-free, use-after-return, stack/heap/buffer overflow 等内存问题
    • 本学期课程中包含了 ASan 的 ATC'12 论文
  • ThreadSanitizer (TSan)
    • 检查数据竞争 (data race)
  • UndefinedBehaviorSanitizer (UBSan)
    • 检查 (整数溢出、对象越界、溢出等一系列问题)

案例 (3): Bug 检测 (cont'd)

Mini UBSan

  • 我们知道 signed integer overflow 是 UB,十分危险和有害
    • 在运行时,能否对每个加法都进行溢出检查?
    • 例子:foo(i++, j++) + (bar() / 2)

例子:AST 改写

  • 把表达式 (E1) + (E2) 替换成 CallExpr ubsan::add_chk(E1, E2)

案例 (4): 自适应软件系统

在动态分析时,我们不仅可以观测,还可以改变程序的执行

  • reliable concurrency
    • 既然我们不能找到所有的并发 bug,那我们不让那些 “没见过” 的调度发生就好了

甚至在程序出现问题以后,还可以恢复

  • Word 出 Bug 了,怎么办?
    • 等一等 → 关掉再开 → 重启机器 → 重装 Word → 重装系统
  • 这体现了一种错误恢复的哲学:local rollback
    • 本学期课程中包含了 micro-reboot
    • 但还有很多有趣的工作实现故障恢复
      • txbegin/txend 快照 → setjmp/longjmp 快照 → fork 快照

总结

动态分析:观测/改变程序的执行

本质上,调试器都可以搞定

  • 但 naive 的实现时间/空间消耗可能过大

实现方法

  • 静态插桩:修改 AST/IR
    • 例子:clang, LLVM pass
  • 动态插桩:运行时二进制补丁
    • 例子:gdb, PIN
  • 借助硬件/系统的机制
    • watch point, VM exit, PMU, PT, ...

一个有趣的讨论

动态分析 (tracer/profiler) 虽然不改变程序的语义,但实际改变了程序的运行时间

  • 本来存在的 bug,增加了 printf 就没有了
  • 本来有性能问题的,profile 以后察觉不出来了
  • 那动态分析还有用吗???

End.

(这两次课信息量很大,但不必惊慌)