Skip to content
This repository was archived by the owner on Feb 2, 2022. It is now read-only.

Commit 78a771c

Browse files
committed
update chapter 11
1 parent 9cf9db0 commit 78a771c

File tree

8 files changed

+92
-93
lines changed

8 files changed

+92
-93
lines changed

content/3/chapter11/0.tex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
LLVM附带了一组工具,可以帮助您查找应用程序中的某些错误。所有这些工具都会使用LLVM和Clang的库。\par
22

3-
在本章中,您将学习如何使用sanitizer来检测应用程序,如何使用最常见的sanitizer来识别各种各样的错误,以及如何为您的应用程序实现模糊测试(fuzz testing)。这将帮助您识别单元测试通常无法发现的bug。您还将学习如何在应用程序中识别性能瓶颈,运行静态分析程序来识别编译器通常没有发现的问题,以及创建您自己的(基于Clang)的工具,您可以使用它来扩展Clang的新功能。\par
3+
本章中,您将学习如何使用sanitizer来检测应用程序,如何使用最常见的sanitizer来识别各种各样的错误,以及如何为您的应用程序实现模糊测试(fuzz testing)。这将帮助您识别单元测试通常无法发现的bug。您还将学习如何在应用程序中识别性能瓶颈,运行静态分析程序来识别编译器通常没有发现的问题,以及创建您自己的(基于Clang)的工具,您可以使用它来扩展Clang的新功能。\par
44

55
本章将涵盖以下内容:\par
66

@@ -12,7 +12,7 @@
1212
\item 创建基于Clang的工具
1313
\end{itemize}
1414

15-
在本章结束时,您将了解如何使用各种LLVM和Clang工具来识别应用程序中的错误。您还将获得使用新功能扩展Clang的知识,例如:执行命名约定或添加新的源分析\par
15+
本章结束时,您将了解如何使用各种LLVM和Clang工具来识别应用程序中的错误。您还将获得使用新功能扩展Clang的知识,例如:执行命名约定或添加新的源码分析工具\par
1616

1717

1818

content/3/chapter11/1.tex

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
要在使用XRay的性能分析部分创建火焰图,你需要从\url{https://github.com/brendangregg/FlameGraph}安装脚本。有些系统,如Fedora和FreeBSD,为这些脚本提供了一个安装包,也可以直接使用安装包\par
1+
要在使用XRay的性能分析部分创建火焰图,需要从\url{https://github.com/brendangregg/FlameGraph}安装脚本。有些系统,如Fedora和FreeBSD,为这些脚本提供了安装包,可以直接使用安装包安装\par
22

3-
要查看Chrome可视化,您需要安装Chrome浏览器。你可以从\url{https://www.google.com/chrome/}下载浏览器,或者使用系统的包管理器来安装Chrome浏览器。本章的代码文件可以在\url{https://github.com/PacktPublishing/Learn-LLVM-12/tree/master/Chapter11}上获取。
3+
要查看Chrome可视化,需要安装Chrome浏览器。可以从\url{https://www.google.com/chrome/}下载浏览器,或者使用系统的包管理器来安装Chrome浏览器。本章的代码文件可以在\url{https://github.com/PacktPublishing/Learn-LLVM-12/tree/master/Chapter11}获取。\par
44

5-
你可以在视频中找到代码 \url{https://bit.ly/3nllhED}
5+
可以在视频中找到代码\url{https://bit.ly/3nllhED}\par

content/3/chapter11/2.tex

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11

2-
LLVM自带一些sanitizer。这些Pass以某种方式检测中间表示(IR),以检查应用程序的某些不当行为。通常,它们需要库支持,这是compiler-rt项目的一部分。可以在Clang中启用sanitizer,这让它们使用起来更加方便。在下面的小节中,我们将介绍可用的sanitizer,即地址、内存和线程。我们先来看看地址sanitizer。\par
2+
LLVM自带一些sanitizer。这些Pass以某种方式检测中间表示(IR),以检查应用程序的某些不当行为。通常,需要库支持,这是compiler-rt项目的一部分。可以在Clang中启用sanitizer,这让它们使用起来更加方便。下面的小节中,我们将介绍可用的sanitizer,即地址、内存和线程。我们先来看看地址sanitizer。\par
33

44
\hspace*{\fill} \par %插入空行
55
\textbf{用地址sanitizer检测内存访问问题}
66

7-
您可以使用地址sanitizer来检测应用程序中的两个内存访问错误。这包括一些常见的错误,比如:在释放动态分配的内存后使用它,或者在已分配内存的边界之外写入动态分配的内存。\par
7+
可以使用地址sanitizer来检测应用程序中的两个内存访问错误。这包括一些常见的错误,比如:在释放动态分配的内存后使用它,或者在已分配内存的边界之外写入动态分配的内存。\par
88

9-
当启用地址sanitizer时,地址sanitizer将用它自己的版本替换对malloc()和free()函数的调用,并使用检查保护程序检测所有内存访问。当然,这给应用程序增加了很多开销,您将只在应用程序的测试阶段使用地址消毒剂。如果您对实现细节感兴趣,那么您可以在llvm/lib/Transforms/\allowbreak Instrumentation/AddressSanitzer.cpp文件中找到Pass源,并在\url{https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm}中找到算法描述。\par
9+
当启用地址sanitizer时,地址sanitizer将用它自己的版本替换对malloc()和free()函数的调用,并使用检查保护程序检测所有内存访问。当然,这给应用程序增加了很多开销,您将只在应用程序的测试阶段使用地址消毒剂。如果对实现细节感兴趣,可以在llvm/lib/Transforms/Instrumentation/AddressSanitzer.cpp文件中找到Pass源,并在\url{https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm}中找到算法描述。\par
1010

1111
让我们运行一个简短的示例来演示地址sanitizer的功能。下面的示例应用程序outfbounds.c分配了12字节的内存,但初始化了14字节:\par
1212

@@ -29,7 +29,7 @@
2929
\$ clang -fsanitize=address -g outofbounds.c -o outofbounds
3030
\end{tcolorbox}
3131

32-
现在,当运行应用程序时,你会得到一个冗长的错误报告:\par
32+
现在,当运行应用程序时,会得到一个冗长的错误报告:\par
3333

3434
\begin{tcolorbox}[colback=white,colframe=black]
3535
\$ ./outofbounds \\
@@ -45,16 +45,16 @@
4545
\hspace*{1cm}\#2 0x23331f in \underline{~}start /usr/src/lib/csu/amd64/crt1.c:76:7
4646
\end{tcolorbox}
4747

48-
报告还包含关于内存内容的详细信息。重要的信息是错误的类型(在本例中是堆缓冲区溢出)和出错的源行。要找到源码行,可以查看位置\#1的堆栈跟踪,这是地址sanitizer拦截应用程序执行之前的最后一个位置。它显示了outfbounds.c文件中的第6行,这一行包含了对memset()的调用——实际上,这就是发生内存溢出的确切位置\par
48+
报告还包含关于内存内容的详细信息。重要的信息是错误的类型(在本例中是堆缓冲区溢出)和出错的源行。要找到源码行,可以查看位置\#1的堆栈跟踪,这是地址sanitizer拦截应用程序执行之前的最后一个位置。它显示了outfbounds.c文件中的第6行,这一行包含了对memset()的调用——实际上,这就是发生内存溢出的位置\par
4949

50-
替换包含memset(p, 0, 14);在outfbounds.c文件中使用以下代码,然后在释放内存后引入对内存的访问。并将源代码存储在useafterfree.c文件中:\par
50+
替换包含memset(p, 0, 14);在outfbounds.c文件中使用以下代码,然后在释放内存后引入对内存的访问。并将源代码存储在useafterfree.c中:\par
5151

5252
\begin{lstlisting}[caption={}]
5353
memset(p, 0, 12);
5454
free(p);
5555
\end{lstlisting}
5656

57-
同样,如果你编译并运行它,会检测到内存释放后指针的使用情况:\par
57+
同样,如果编译并运行,会检测到内存释放后指针的使用情况:\par
5858

5959
\begin{tcolorbox}[colback=white,colframe=black]
6060
\$ clang -fsanitize=address -g useafterfree.c -o useafterfree \\
@@ -72,7 +72,7 @@
7272

7373
这一次,报告指向第8行,其中包含p指针的释放。\par
7474

75-
在x86\underline{~}64 Linux和macOS上,您也可以启用泄漏检测器。如果在运行应用程序之前将ASAN\underline{~}OPTIONS环境变量设置为detect\underline{~}leaks=1,那么还会得到一个关于内存泄漏的报告。可以这样做:\par
75+
在x86\underline{~}64 Linux和macOS上,也可以启用泄漏检测器。如果在运行应用程序之前将ASAN\underline{~}OPTIONS环境变量设置为detect\underline{~}leaks=1,还会得到一个关于内存泄漏的报告。可以这样做:\par
7676

7777
\begin{tcolorbox}[colback=white,colframe=black]
7878
\$ ASAN\underline{~}OPTIONS=detect\underline{~}leaks=1 ./useafterfree
@@ -83,9 +83,9 @@
8383
\hspace*{\fill} \par %插入空行
8484
\textbf{使用内存sanitizer查找未初始化的内存访问}
8585

86-
使用未初始化的内存是另一类难以发现的错误。在C和C++中,一般的内存分配例程不会用默认值初始化内存缓冲区对于堆栈上的变量也是如此。\par
86+
使用未初始化的内存是另一类难以发现的错误。在C和C++中,一般的内存分配例程不会用默认值初始化内存缓冲区对于堆栈上的变量也是如此。\par
8787

88-
出现错误的机会很多,而内存sanitizer有助于找到错误。如果您对实现细节感兴趣,可以在llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp文件中找到内存sanitizer Pass的源文件。文件顶部的注释解释了实现思想。\par
88+
出现错误的机会很多,而内存sanitizer有助于找到错误。如果对实现细节感兴趣,可以在llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp中找到内存sanitizer Pass的源文件。文件顶部的注释解释了实现思想。\par
8989

9090
让我们运行一个小示例,并将下面的源代码保存为memory.c文件。你应该注意到x变量没有初始化,而是用作返回值:\par
9191

@@ -117,9 +117,9 @@
117117
\hspace*{\fill} \par %插入空行
118118
\textbf{用线程sanitizer指出数据竞争}
119119

120-
为了充分利用现代CPU的功能,应用程序现在使用多线程。这是一项强大的技术,但它也引入了新的错误来源。多线程应用程序中一个常见的问题是,对全局数据的访问没有保护,例如:没有使用互斥锁或信号量。这样的问题称为数据竞争。线程sanitizer可以检测基于pthread的应用程序和使用LLVM libc++实现的应用程序中的数据竞争。可以在llvm/lib/Transforms/Instrumentation/\allowbreak ThreadSanitize.cpp文件中找到实现。\par
120+
为了充分利用现代CPU的功能,应用程序现在使用多线程。这是一项强大的技术,但它也引入了新的错误来源。多线程应用程序中一个常见的问题是,对全局数据的访问没有保护,例如:没有使用互斥锁或信号量。这样的问题称为数据竞争。线程sanitizer可以检测基于pthread的应用程序和使用LLVM libc++实现的应用程序中的数据竞争。可以在llvm/lib/Transforms/Instrumentation/ThreadSanitize.cpp文件中找到实现。\par
121121

122-
为了演示线程sanitizer的功能,我们将创建一个非常简单的生产者/消费者的应用程序。生产者线程增加一个全局变量,而消费者线程减少同一个变量。对全局变量的访问不受保护,因此这显然是一场数据竞争。在thread.c文件中保存以下源代码:\par
122+
为了演示线程sanitizer的功能,我们将创建一个简单的生产者/消费者的应用程序。生产者线程增加一个全局变量,而消费者线程减少同一个变量。对全局变量的访问不受保护,因此这显然是一场数据竞争。在thread.c文件中保存以下源代码:\par
123123

124124
\begin{lstlisting}[caption={}]
125125
#include <pthread.h>
@@ -150,7 +150,7 @@
150150

151151
producer()函数只增加数据变量,而consumer()函数减少数据变量。没有实现访问保护,因此这构成了一场数据竞争。main()函数使用pthread\underline{~}create()函数启动两个线程,使用pthread\underline{~}join()函数等待线程结束,并返回数据变量的当前值。\par
152152

153-
如果编译并运行此应用程序,则不会注意到任何错误,返回值总是0。如果执行的循环次数增加100倍,则会出现一个错误。在本例中,返回值不等于0,您将看到显示其他值。\par
153+
如果编译并运行此应用程序,则不会注意到任何错误,返回值总是0。如果执行的循环次数增加100倍,则会出现一个错误。本例中,返回值不等于0,您将看到显示其他值。\par
154154

155155
您可以使用线程sanitizer来标识数据竞争。要在启用了线程sanitizer的情况下进行编译,需要将-fsanitize=thread选项传递给Clang。使用-g选项添加调试符号可以在报告中提供行号,这很有帮助。注意,还需要链接pthread库:\par
156156

@@ -193,7 +193,7 @@
193193

194194
报告指向源文件的第6行和第11行,其中访问全局变量。它还显示了两个名为T1和T2的线程访问了该变量,以及分别调用pthread\underline{~}create()函数的文件和行号。\par
195195

196-
在本节中,我们学习了如何使用三种sanitizer来查找应用程序中的常见问题。地址sanitizer帮助我们识别常见的内存访问错误,例如:越界访问或在释放后使用内存。使用内存sanitizer,可以找到对未初始化内存的访问,线程sanitizer帮助我们查找数据竞争。\par
196+
本节中,我们学习了如何使用三种sanitizer来查找应用程序中的常见问题。地址sanitizer帮助我们识别常见的内存访问错误,例如:越界访问或在释放后使用内存。使用内存sanitizer,可以找到对未初始化内存的访问,线程sanitizer帮助我们查找数据竞争。\par
197197

198198
下一节中,我们将尝试通过在随机数据上运行应用程序(称为模糊测试)来触发sanitizer。\par
199199

content/3/chapter11/3.tex

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
LLVM自带了自己的模糊测试库。libFuzzer实现最初是LLVM核心库的一部分,后来移到了compiler-rt上。该库旨在测试小而快速的函数。\par
77

8-
让我们运行一个小示例。需要提供LLVMFuzzerTestOneInput()函数。这个函数由fuzzer驱动程序调用,并为您提供一些输入。下面的函数计算输入中的连续ASCII数字,然后我们将随机输入输入给它。你需要将示例保存在fuzzer.c文件中:\par
8+
让我们运行一个小示例。需要提供LLVMFuzzerTestOneInput()函数。这个函数由fuzzer驱动程序调用,并为您提供一些输入。下面的函数计算输入中的连续ASCII数字,然后我们将随机输入输入给它。需要将示例保存在fuzzer.c文件中:\par
99

1010
\begin{lstlisting}[caption={}]
1111
#include <stdint.h>
@@ -27,9 +27,9 @@
2727

2828
代码中,count()函数计算Data变量所指向的内存中的位数。只检查数据的大小以确定是否有可用的字节。在while循环中,不检查数据长度。\par
2929

30-
与普通C字符串一起使用时,不会出现错误,因为C字符串总是以0字节结束。LLVMFuzzerTest\allowbreak OneInput()函数就是所谓的fuzz目标,它是libFuzzer调用的函数。调用我们想要测试的函数并返回0,这是目前唯一允许的值。\par
30+
与普通C字符串一起使用时,不会出现错误,因为C字符串总是以0字节结束。LLVMFuzzerTestOneInput()函数就是fuzz目标,它是libFuzzer调用的函数。调用我们想要测试的函数并返回0,这是目前唯一允许的值。\par
3131

32-
要使用libFuzzer编译文件,需要添加-fsanitize=fuzzer选项。建议还启用地址sanitizer和调试符号的生成。使用以下命令编译文件:\par
32+
要使用libFuzzer编译文件,需要添加-fsanitize=fuzzer选项。建议还启用地址sanitizer和调试符号的生成:\par
3333

3434
\begin{tcolorbox}[colback=white,colframe=black]
3535
\$ clang -fsanitize=fuzzer,address -g fuzzer.c -o fuzzer
@@ -38,18 +38,18 @@
3838
当您运行测试时,会产生一个冗长的报告。报告包含比堆栈跟踪更多的信息,所以让我们仔细来看看:\par
3939

4040
\begin{enumerate}
41-
\item 第一行告诉您用于初始化随机数生成器的种子。你可以使用-seed =选项来重复执行:
41+
\item 第一行告诉您用于初始化随机数生成器的种子。可以使用-seed=来重复执行:
4242
\begin{tcolorbox}[colback=white,colframe=black]
4343
INFO: Seed: 1297394926
4444
\end{tcolorbox}
4545

46-
\item 默认情况下,libFuzzer将输入限制为最多4,096字节。可以使用-max \underline{~}len=选项来更改默认值:
46+
\item 默认情况下,libFuzzer将输入限制为最多4,096字节。可以使用-max \underline{~}len=来更改默认值:
4747
\begin{tcolorbox}[colback=white,colframe=black]
4848
INFO: -max\underline{~}len is not provided; libFuzzer will not \\
4949
generate inputs larger than 4096 bytes
5050
\end{tcolorbox}
5151

52-
\item 现在,我们在不提供示例输入的情况下运行测试。所有样本输入的集合称为语料库,在这次运行中它是空的:
52+
\item 现在,在不提供示例输入的情况下运行测试。所有样本输入的集合称为语料库,在这次运行中它是空的:
5353
\begin{tcolorbox}[colback=white,colframe=black]
5454
INFO: A corpus is not provided, starting from an empty corpus
5555
\end{tcolorbox}
@@ -61,7 +61,7 @@
6161
ChangeByte- DE: "1$\setminus$x00"-
6262
\end{tcolorbox}
6363

64-
\item 之后,检测到内存溢出,它跟随而来的就是地址sanitizer的信息。最后,报告显示导致内存溢出的输入在哪里:
64+
\item 之后,检测到内存溢出,然后就是地址sanitizer的信息。最后,报告显示导致内存溢出的输入在哪里:
6565
\begin{tcolorbox}[colback=white,colframe=black]
6666
artifact\underline{~}prefix='./'; Test unit written to ./crash-17ba0791499db908433b80f37c5fbc89b87\allowbreak 0084b
6767
\end{tcolorbox}
@@ -98,13 +98,13 @@
9898
INFO: seed corpus: files: 2 min: 4b max: 7b total: 11b rss: 29Mb
9999
\end{tcolorbox}
100100

101-
如果您正在测试一个作用于令牌或其他魔数值(如编程语言)的函数,那么可以通过提供一个带有令牌的字典来加快这个过程。对于编程语言,字典将包含该语言中使用的所有关键字和特殊符号。字典定义遵循简单的键-值样式,例如:要在字典中定义if关键字,您可以添加以下内容:\par
101+
如果您正在测试一个作用于令牌或其他魔数值(如编程语言)的函数,那么可以通过提供一个带有令牌的字典来加快这个过程。对于编程语言,字典将包含该语言中使用的所有关键字和特殊符号。字典定义遵循简单的键-值样式,例如:要在字典中定义if关键字,可以添加以下内容:\par
102102

103103
\begin{tcolorbox}[colback=white,colframe=black]
104104
kw1="if"
105105
\end{tcolorbox}
106106

107-
但是,这个键是可选的,可以省略。然后可以使用-dict=选项 在命令行上指定字典文件。下一节中,我们将了解libFuzzer实现的限制和替代方案。\par
107+
但是,这个键是可选的,可以省略。然后可以使用-dict=在命令行上指定字典文件。下一节中,我们将了解libFuzzer实现的限制和替代方案。\par
108108

109109
\hspace*{\fill} \par %插入空行
110110
\textbf{限制和替代}
@@ -128,7 +128,7 @@
128128

129129
有很多方法可以影响libFuzzer的工作方式,可以在\url{https://llvm.org/docs/LibFuzzer.html}了解更多细节。\par
130130

131-
在下一节中,我们将讨论应用程序可能存在的一个完全不同的问题,试图查找性能瓶颈。\par
131+
下一节中,我们将讨论应用程序可能存在的一个完全不同的问题,试图查找性能瓶颈。\par
132132

133133

134134

0 commit comments

Comments
 (0)