diff --git a/ReadMe.md b/ReadMe.md index 1d38efc..80aeaed 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -1,9 +1,14 @@ + # 更新 -2023-12-20 +2024-07-05 本书已经出版 ![image](https://github.com/Celthi/effective-debugging-zh/assets/5187962/29b04963-5535-432c-b56f-8a2d5dbc2ec6) - -书里的序言二说明了书和这个repo的关系 +由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 +如果你想阅读更加完善的版本,推荐购买正版书籍。 +# 更新 +2023-12-20 +本书已经出版 +书里的序言二说明了书和这个repo的关系,也可见[高效C/C++调试](https://zhuanlan.zhihu.com/p/675726977) > 在编程的道路上,每一个程序员都不可避免地遇到调试的挑战。我仍然记得那些难忘的调试 经历;大学时期,我和朋友共同调试机器人的程序;进入职场后,我又开始钻研数百万行的 C++代 码。从初入编程世界时的探索与迷茫,到如今的稳健与沉稳。这背后蕴含着无数次的学习与实践。 更为关键的是,得益于站在诸多行业前辈的肩膀上。而本书的首作者 Michael,正是其中一位令人 尊敬的巨人。幸运的是,我在美国工作期间,有幸得到他的直接指导和悉心帮助。 当清华大学的编辑联系我,询问是否有兴趣出版书籍时,我想到了从学生时代到职场的点滴 经验。我常常与同学或者同事分享自己的体会,也在知乎账号(CrackingOysters1)上发表相关文章。 但要整理成一本完整的书籍,仍有不少工作要做。这时,我想到了 Michael 以及他那份关于高效调 试的英文书稿。于是,我建议我们基于这份书稿,共同打造一本新的书籍。因此,本书中绝大部 分的内容都深受他的经验智慧所启发,同时我也主要增添了关于 Google Address Sanitzer 和逆向调 试的章节、第 9 章以及第 12-18 章的内容。 diff --git a/script.py b/script.py new file mode 100644 index 0000000..db237e3 --- /dev/null +++ b/script.py @@ -0,0 +1,30 @@ +# find all the markdown files and add the epilogue to them +import os +import re + +def find_files_recursively(): + for root, dirs, files in os.walk('.'): + for file in files: + if file.endswith('.md'): + yield os.path.join(root, file) + +def add_epilogue(file, epilogue): + with open(file, 'r') as f: + content = f.read() + with open(file, 'w') as f: + f.write('\n') + f.write(epilogue) + f.write('\n') + f.write(content) +def main(): + epilogue = '''# 更新 +2024-07-05 +本书已经出版 +![image](https://github.com/Celthi/effective-debugging-zh/assets/5187962/29b04963-5535-432c-b56f-8a2d5dbc2ec6) +由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 +如果你想阅读更加完善的版本,推荐购买正版书籍。''' + for file in find_files_recursively(): + add_epilogue(file, epilogue) + +if __name__ == '__main__': + main() diff --git a/src/appendix_A.md b/src/appendix_A.md index 655fea2..0a63ef3 100644 --- a/src/appendix_A.md +++ b/src/appendix_A.md @@ -1 +1,8 @@ + +# 更新 +2024-07-05 +本书已经出版 +![image](https://github.com/Celthi/effective-debugging-zh/assets/5187962/29b04963-5535-432c-b56f-8a2d5dbc2ec6) +由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 +如果你想阅读更加完善的版本,推荐购买正版书籍。 # 附录A 拓展调试能力 diff --git a/src/appendix_B.md b/src/appendix_B.md index e0c3c05..193099a 100644 --- a/src/appendix_B.md +++ b/src/appendix_B.md @@ -1 +1,8 @@ + +# 更新 +2024-07-05 +本书已经出版 +![image](https://github.com/Celthi/effective-debugging-zh/assets/5187962/29b04963-5535-432c-b56f-8a2d5dbc2ec6) +由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 +如果你想阅读更加完善的版本,推荐购买正版书籍。 # 附录B 调试混合语言 diff --git a/src/chapter_1.md b/src/chapter_1.md index 4dd6e2f..19af4eb 100644 --- a/src/chapter_1.md +++ b/src/chapter_1.md @@ -1,3 +1,10 @@ + +# 更新 +2024-07-05 +本书已经出版 +![image](https://github.com/Celthi/effective-debugging-zh/assets/5187962/29b04963-5535-432c-b56f-8a2d5dbc2ec6) +由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 +如果你想阅读更加完善的版本,推荐购买正版书籍。 # 第一章 调试符号和调试器 当谈论调试一个程序的时候,调试器也许是人们想到的第一个事物,因为它是这个过程中不可避免的部分。而这源于考虑到现代编程语言和操作系统的复杂性,就算不是无法实现,知道一个程序的状态也是非常困难。一个写代码的开发人员应该已经知道什么是调试器和如何或多或少去使用一个调试器。但是你了解调试器足够多吗? diff --git a/src/chapter_1/01-01debug_symbols.md b/src/chapter_1/01-01debug_symbols.md index 34591f2..2f56134 100644 --- a/src/chapter_1/01-01debug_symbols.md +++ b/src/chapter_1/01-01debug_symbols.md @@ -1,3 +1,10 @@ + +# 更新 +2024-07-05 +本书已经出版 +![image](https://github.com/Celthi/effective-debugging-zh/assets/5187962/29b04963-5535-432c-b56f-8a2d5dbc2ec6) +由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 +如果你想阅读更加完善的版本,推荐购买正版书籍。 # 调试符号 调试符号是和相关的机器码、全局数据对象等等一起由编译器生成的。接着它们被链接器收集和组织,写入到可执行文件(大部分UNIX平台)的调试section或者是一个单独的文件(Windows程序数据库,或者pdb文件)。一个源码级别的调试器为了理解一个进程的内存镜像如一个程序的运行实例,需要从它的仓库里面读取调试符号。 diff --git a/src/chapter_1/01-02debug_symbol_overview.md b/src/chapter_1/01-02debug_symbol_overview.md index 0d0daa0..0ef38a6 100644 --- a/src/chapter_1/01-02debug_symbol_overview.md +++ b/src/chapter_1/01-02debug_symbol_overview.md @@ -1,3 +1,10 @@ + +# 更新 +2024-07-05 +本书已经出版 +![image](https://github.com/Celthi/effective-debugging-zh/assets/5187962/29b04963-5535-432c-b56f-8a2d5dbc2ec6) +由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 +如果你想阅读更加完善的版本,推荐购买正版书籍。 # 调试符号概览 为了具有完全的源码级别调试能力,编译器需要生成许多调试符号信息,它们可以根据描述的对象分类如下: diff --git a/src/chapter_1/01-03dwarf_format.md b/src/chapter_1/01-03dwarf_format.md index 70441f9..5df430b 100644 --- a/src/chapter_1/01-03dwarf_format.md +++ b/src/chapter_1/01-03dwarf_format.md @@ -1,4 +1,11 @@ +# 更新 +2024-07-05 +本书已经出版 +![image](https://github.com/Celthi/effective-debugging-zh/assets/5187962/29b04963-5535-432c-b56f-8a2d5dbc2ec6) +由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 +如果你想阅读更加完善的版本,推荐购买正版书籍。 + # DWARF格式 DWARF像结构体一样以树的形式组织调试符号。这跟大部分语言内在也是树结构的词法作用域相对应。每一个树节点是一个DIE (Debug information entry),它带了特定的调试符号:一个对象,一个函数,一个源文件等等。一个结点可能具有任意数量的子结点或者兄弟结点。比如,一个函数DIE可能有很多代表函数局部变量的子DIEs。 diff --git a/src/chapter_1/01-04inconsistent_data_type.md b/src/chapter_1/01-04inconsistent_data_type.md index 2c5afd0..67d567e 100644 --- a/src/chapter_1/01-04inconsistent_data_type.md +++ b/src/chapter_1/01-04inconsistent_data_type.md @@ -1,4 +1,11 @@ +# 更新 +2024-07-05 +本书已经出版 +![image](https://github.com/Celthi/effective-debugging-zh/assets/5187962/29b04963-5535-432c-b56f-8a2d5dbc2ec6) +由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 +如果你想阅读更加完善的版本,推荐购买正版书籍。 + # 不一致的数据类型 diff --git a/src/chapter_1/01-05debugger_internal.md b/src/chapter_1/01-05debugger_internal.md index 6224bdf..ac26fa3 100644 --- a/src/chapter_1/01-05debugger_internal.md +++ b/src/chapter_1/01-05debugger_internal.md @@ -1,3 +1,10 @@ + +# 更新 +2024-07-05 +本书已经出版 +![image](https://github.com/Celthi/effective-debugging-zh/assets/5187962/29b04963-5535-432c-b56f-8a2d5dbc2ec6) +由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 +如果你想阅读更加完善的版本,推荐购买正版书籍。 # 调试器的内在 大部分程序员通过实践学习怎样使用一个调试器。依赖于经验,一些人比另外一些人更熟悉调试器各种命令。但是只有一小部分知道调试器的内在结构。在本节中,我将从用户的视角讨论一些编译器的实现细节。这不仅仅是为了满足你对调试器魔法的好奇,也可能更重要的是帮助你更好地理解调试器,因而你知道最大优势地使用工具。 diff --git a/src/chapter_1/01-06tips_and_caveats.md b/src/chapter_1/01-06tips_and_caveats.md index 79d837f..325306b 100644 --- a/src/chapter_1/01-06tips_and_caveats.md +++ b/src/chapter_1/01-06tips_and_caveats.md @@ -1,3 +1,10 @@ + +# 更新 +2024-07-05 +本书已经出版 +![image](https://github.com/Celthi/effective-debugging-zh/assets/5187962/29b04963-5535-432c-b56f-8a2d5dbc2ec6) +由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 +如果你想阅读更加完善的版本,推荐购买正版书籍。 ## 技巧和注意事项 在大多数时候,使用一个调试器是直观的。只要调试器依赖的东西是正确和好的状态,那么它就可以完美地工作。但是总有些时候,一点事情就会破坏你一整天。当调试器不能按照你需要合作的时候,那会是非常沮丧的。更严重的破坏是,它可能会给出“错误”的信息导致一个假的结论。在你花费了很多时间去追寻一个错误的理由,你发现最基本的假设是不对的。但是,很多时候,不应该去怪罪调试器。通常是我们自己的错误理解或者是自大导致这些悲剧。调试器会抱怨任何它不喜欢的事情。比如,跟二进制相比,一个源码文件具有靠后的时间戳可能意味着一个源代码被改变了;一个不匹配的库文件相对core dump文件里面显示的库文件具有不一样的check sum。有时候一个程序不在你设置的断点停止;或者你不能抓住一个变量被意外改变的时刻;或者调用栈很明显已经是垃圾等等。另外一方面,调试器有很多工程师不知道的强大的特性。在大多数时候,我们仅仅使用了所有功能的一小部分,用来处理常见的调试需求。但是,如果我们花费一点时间来学习调试器高级的特性是值得。它将帮助我们更有效率地调试和解决那些不是每天都能遇到的困难问题。在接下来的章节,我将强调一些在过去帮助过我的技巧。 diff --git a/src/chapter_1/01-07ad_hoc_debug_symbol.md b/src/chapter_1/01-07ad_hoc_debug_symbol.md index 9b8e280..3a1b6ee 100644 --- a/src/chapter_1/01-07ad_hoc_debug_symbol.md +++ b/src/chapter_1/01-07ad_hoc_debug_symbol.md @@ -1,3 +1,10 @@ + +# 更新 +2024-07-05 +本书已经出版 +![image](https://github.com/Celthi/effective-debugging-zh/assets/5187962/29b04963-5535-432c-b56f-8a2d5dbc2ec6) +由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 +如果你想阅读更加完善的版本,推荐购买正版书籍。 # 特殊的调试符号 前面的章节已经讨论了调试符号和调试器是怎么在一个调试会话里使用这个信息。作为一个增强,如果需要,我们可以往调试器添加更多的调试符号。在即使我们知道一个变量的具体类型,仍然不能打印这个变量的时候是非常有帮助的。调试器不能理解变量的问题是没有它的调试符号。这对系统库、三方库、遗留的符号只有部分或者全部去掉的二进制或者一些情况不编译带调试符号情况来说,是常见的。一种变通这个困难的方式是编译一个新的带有想要的调试符号的库文件。当调试器把新库的符号加载后,我们就可以具有调试这些二进制的更好准备。让我们看看一个第三方库的数据结构的例子。 diff --git a/src/chapter_1/01-08breakpoints_and_watchpoints.md b/src/chapter_1/01-08breakpoints_and_watchpoints.md index 7ca24d8..28425a6 100644 --- a/src/chapter_1/01-08breakpoints_and_watchpoints.md +++ b/src/chapter_1/01-08breakpoints_and_watchpoints.md @@ -1,3 +1,10 @@ + +# 更新 +2024-07-05 +本书已经出版 +![image](https://github.com/Celthi/effective-debugging-zh/assets/5187962/29b04963-5535-432c-b56f-8a2d5dbc2ec6) +由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 +如果你想阅读更加完善的版本,推荐购买正版书籍。 # 断点和监测点 使用最多的调试器特性大概是在一个函数的入口或者特定的源码行设置断点。一个简单的断点在许多案子可能不够。比如通常会在一个被怀疑的变量可能会被不正确修改的地方设置一些断点。但是当断点被碰到之前要经过上百次,则是繁琐和不具有可操作性的。你甚至可能会错过那个时刻,因为你需要从那么多合法的选择中找到那个坏的。普遍的解决方案是条件断点——将一个特定的条件表达式与断点关联起来。当断点被碰到时,调试器计算表达式。如果计算结果是真值,那么程序停止等待用户操作;否则程序继续运行。读者应该意识到条件断点的性能损耗。尽管条件断点为假值时看起来被调试的程序不被打断地运行着,程序实际上每一次都会停止下来,在表达式被计算过后重新恢复运行。如果消耗过大,比如可能会经常被调用的函数导致,我们需要采用一种更快的方式来检查数据。举个例子,函数插入可以避免调试器的介入(参看第六章获取更多细节) diff --git a/src/chapter_1/01-09alter_execution_and_side_effect.md b/src/chapter_1/01-09alter_execution_and_side_effect.md index e0e6705..1e9bfa9 100644 --- a/src/chapter_1/01-09alter_execution_and_side_effect.md +++ b/src/chapter_1/01-09alter_execution_and_side_effect.md @@ -1,3 +1,10 @@ + +# 更新 +2024-07-05 +本书已经出版 +![image](https://github.com/Celthi/effective-debugging-zh/assets/5187962/29b04963-5535-432c-b56f-8a2d5dbc2ec6) +由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 +如果你想阅读更加完善的版本,推荐购买正版书籍。 # 改变运行和副作用 调试器通常用来观察一个被跟踪的进程的状态。它也可以改变debugee的状态,从改变它原本要执行的正常运行。这个方法创新性的使用是无限的。比如,验证当内存消耗完的时候程序的错误,调试器可以简单地设置函数`malloc`的返回值为`NULL`。它是检查一些难以或者昂贵地模拟的特别案例的快速和实惠的方式。 diff --git a/src/chapter_1/01-10automate_symbol_matching.md b/src/chapter_1/01-10automate_symbol_matching.md index bc5334d..70adf06 100644 --- a/src/chapter_1/01-10automate_symbol_matching.md +++ b/src/chapter_1/01-10automate_symbol_matching.md @@ -1,3 +1,10 @@ + +# 更新 +2024-07-05 +本书已经出版 +![image](https://github.com/Celthi/effective-debugging-zh/assets/5187962/29b04963-5535-432c-b56f-8a2d5dbc2ec6) +由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 +如果你想阅读更加完善的版本,推荐购买正版书籍。 # 自动化符号匹配 至此,我希望你相信调试符号需要匹配来让调试器变得有用。没有它,调试器要么拒绝一个用户的请求,或者更糟的是给出不正确的答案。在原则上找到包含匹配符号的文件不是有挑战性。但是如果产品包含很多模块和很多发布版本、服务包,热修复和补丁要支持,它可以是繁琐和错误频出的。自动化找到正确调试符号文件是更容易的。 diff --git a/src/chapter_1/01-11post_mortem_analysis.md b/src/chapter_1/01-11post_mortem_analysis.md index c97995d..92385f2 100644 --- a/src/chapter_1/01-11post_mortem_analysis.md +++ b/src/chapter_1/01-11post_mortem_analysis.md @@ -1,3 +1,10 @@ + +# 更新 +2024-07-05 +本书已经出版 +![image](https://github.com/Celthi/effective-debugging-zh/assets/5187962/29b04963-5535-432c-b56f-8a2d5dbc2ec6) +由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 +如果你想阅读更加完善的版本,推荐购买正版书籍。 # 事后的分析 不仅正在运行的进程,一个debugee可以是一个core dump文件,通常在进程crash的时候由系统例行生成。系统也提供了API给应用程序或者工具来不杀掉进程的同时生成进程的core dump文件。这对调查像不间断服务器程序的性能或者很难访问的远程程序的线下分析是合适的。 diff --git a/src/chapter_1/01-12memory_protection.md b/src/chapter_1/01-12memory_protection.md index e3ddea3..71cf930 100644 --- a/src/chapter_1/01-12memory_protection.md +++ b/src/chapter_1/01-12memory_protection.md @@ -1,3 +1,10 @@ + +# 更新 +2024-07-05 +本书已经出版 +![image](https://github.com/Celthi/effective-debugging-zh/assets/5187962/29b04963-5535-432c-b56f-8a2d5dbc2ec6) +由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 +如果你想阅读更加完善的版本,推荐购买正版书籍。 # 内存保护 在一些平台,如HP-UX,一个用户可能不能够在加载的共享库里面设置断点。理由是共享库默认被加载在公共可读的段。因此调试器不能够在代码段插入断点的陷入指令。为了改变这个默认行为,用户需要修改共享库加载的模式。下面的HP-UX命令在特定的模块设置一个标志位和指导系统运行把它们加载到私有的、可写的段中。 diff --git a/src/chapter_1/01-13breakpoints_doesnt_work.md b/src/chapter_1/01-13breakpoints_doesnt_work.md index 8ccb904..5e68ab5 100644 --- a/src/chapter_1/01-13breakpoints_doesnt_work.md +++ b/src/chapter_1/01-13breakpoints_doesnt_work.md @@ -1,3 +1,10 @@ + +# 更新 +2024-07-05 +本书已经出版 +![image](https://github.com/Celthi/effective-debugging-zh/assets/5187962/29b04963-5535-432c-b56f-8a2d5dbc2ec6) +由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 +如果你想阅读更加完善的版本,推荐购买正版书籍。 # 断点不工作 如果程序不如预期那样在预先设置的断点停止,你可能想要检查下面的列表来保证断点被设置正确和准确。 diff --git a/src/chapter_1/01-14summary.md b/src/chapter_1/01-14summary.md index 75dbfd0..ebb3d36 100644 --- a/src/chapter_1/01-14summary.md +++ b/src/chapter_1/01-14summary.md @@ -1,3 +1,10 @@ + +# 更新 +2024-07-05 +本书已经出版 +![image](https://github.com/Celthi/effective-debugging-zh/assets/5187962/29b04963-5535-432c-b56f-8a2d5dbc2ec6) +由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 +如果你想阅读更加完善的版本,推荐购买正版书籍。 # 总结 使用调试器是一个程序开发人员和某些其他工程师必备的基本技能之一。一个调试器通常有一个超大命令集合和取决于调试器的实现和宿主系统的能力,许多它的功能对被调试的进程有显著性地影响。除了它的常用命令,为了更有效率地使用它,我们需要学习更多调试器高级的功能。当一个问题变得更难和在影响范围变得更庞大,越来越多的需求想要调试器具有更多的能力。自定义调试器命令命令和插件是这个挑战的解决办法。我们将在接下来的章节看到更多调试器插件的例子。 diff --git a/src/chapter_10.md b/src/chapter_10.md index baeb8db..2da5470 100644 --- a/src/chapter_10.md +++ b/src/chapter_10.md @@ -1 +1,8 @@ + +# 更新 +2024-07-05 +本书已经出版 +![image](https://github.com/Celthi/effective-debugging-zh/assets/5187962/29b04963-5535-432c-b56f-8a2d5dbc2ec6) +由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 +如果你想阅读更加完善的版本,推荐购买正版书籍。 # 第十章 使用地址消毒工具 diff --git a/src/chapter_2.md b/src/chapter_2.md index 5c04076..0b81121 100644 --- a/src/chapter_2.md +++ b/src/chapter_2.md @@ -1,3 +1,10 @@ + +# 更新 +2024-07-05 +本书已经出版 +![image](https://github.com/Celthi/effective-debugging-zh/assets/5187962/29b04963-5535-432c-b56f-8a2d5dbc2ec6) +由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 +如果你想阅读更加完善的版本,推荐购买正版书籍。 # 第二章 堆数据结构 数据结构无疑是任何程序的核心部分。内存分配和释放,数据对象的构建、析构、引用和访问是一个程序最普遍和复杂的操作。因为C/C++语言赋能程序员通过引用和指针最大的自由去操作内存对象,不会有人惊讶这些程序大部分的bugs是跟一种形式或者另外一种形式的错误的内存访问。我在这方面有一手的经历,因为每天都在调试如段错误这种程序错误。这些问题包含室内的测试和客户产品的环境。大部分问题归结为分配内存的不正确使用。 diff --git a/src/chapter_2/02-01understand_memory_manager.md b/src/chapter_2/02-01understand_memory_manager.md index 3818a16..1efb1d9 100644 --- a/src/chapter_2/02-01understand_memory_manager.md +++ b/src/chapter_2/02-01understand_memory_manager.md @@ -1,3 +1,10 @@ + +# 更新 +2024-07-05 +本书已经出版 +![image](https://github.com/Celthi/effective-debugging-zh/assets/5187962/29b04963-5535-432c-b56f-8a2d5dbc2ec6) +由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 +如果你想阅读更加完善的版本,推荐购买正版书籍。 # 理解内存管理器 市面上有许多商业性的内存管理器,许多更多自定义的设计和实现跑在大量应用上。由于我们的目标不是编写一个自己的新的内存管理器,理解内存管理器设计和实现的每一个细节是不必要的。学习每一个内存管理器也不具有可操作性的。但是如果你在调试涉及堆内存的问题,知道一个特定的内存管理器是怎么样在你的程序中记录用户内存块是绝对重要和有帮助的。 diff --git a/src/chapter_2/02-02ptmalloc.md b/src/chapter_2/02-02ptmalloc.md index 621ad2d..9499b11 100644 --- a/src/chapter_2/02-02ptmalloc.md +++ b/src/chapter_2/02-02ptmalloc.md @@ -1,3 +1,10 @@ + +# 更新 +2024-07-05 +本书已经出版 +![image](https://github.com/Celthi/effective-debugging-zh/assets/5187962/29b04963-5535-432c-b56f-8a2d5dbc2ec6) +由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 +如果你想阅读更加完善的版本,推荐购买正版书籍。 # ptmalloc Ptmalloc是在Doug Lee的内存分配器上面包了一层并发分配的增强。它是Linux Red Hat发行版和许多其他的系统默认内存管理器。在性能和空间节约角度,在最好的内存管理器中,广受好评。下面的讨论适用于Ptmalloc 2.7.0. diff --git a/src/chapter_2/02-03tcmalloc.md b/src/chapter_2/02-03tcmalloc.md index 9ff2d02..986c58c 100644 --- a/src/chapter_2/02-03tcmalloc.md +++ b/src/chapter_2/02-03tcmalloc.md @@ -1,3 +1,10 @@ + +# 更新 +2024-07-05 +本书已经出版 +![image](https://github.com/Celthi/effective-debugging-zh/assets/5187962/29b04963-5535-432c-b56f-8a2d5dbc2ec6) +由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 +如果你想阅读更加完善的版本,推荐购买正版书籍。 # Tcmalloc (待补充) diff --git a/src/chapter_2/02-04multiple_heaps.md b/src/chapter_2/02-04multiple_heaps.md index cd8e87b..9df3e5e 100644 --- a/src/chapter_2/02-04multiple_heaps.md +++ b/src/chapter_2/02-04multiple_heaps.md @@ -1,3 +1,10 @@ + +# 更新 +2024-07-05 +本书已经出版 +![image](https://github.com/Celthi/effective-debugging-zh/assets/5187962/29b04963-5535-432c-b56f-8a2d5dbc2ec6) +由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 +如果你想阅读更加完善的版本,推荐购买正版书籍。 # 多个堆 现代内存管理器,像Ptmalloc和Tcmalloc,能够创建和管理多个堆。一个堆可以包含一个或多个段。这些段不需要在地址上连续。它们被逻辑性地组织在一起来服务一个线程集合、一个单独的特性或者一个特定的程序模块。多个堆可以让跑在多个处理器机器的多线程程序极大提高性能,而今天这样的程序是常态。还有其他的使用多个堆的优势: diff --git a/src/chapter_2/02-05leverage_heap_metadata.md b/src/chapter_2/02-05leverage_heap_metadata.md index 3edfbc5..8d63c81 100644 --- a/src/chapter_2/02-05leverage_heap_metadata.md +++ b/src/chapter_2/02-05leverage_heap_metadata.md @@ -1,3 +1,10 @@ + +# 更新 +2024-07-05 +本书已经出版 +![image](https://github.com/Celthi/effective-debugging-zh/assets/5187962/29b04963-5535-432c-b56f-8a2d5dbc2ec6) +由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 +如果你想阅读更加完善的版本,推荐购买正版书籍。 # 利用堆元数据 在前一个章节中,我们看到了两个受欢迎的内存管理器的实现。正如我在本章说的,理解堆的数据结构对调试内存问题有很大帮助。因为堆元素告诉我们应用程序数据对象的基本状态,它可以提供内存损坏是如何产生的线索。 diff --git a/src/chapter_2/02-06summary.md b/src/chapter_2/02-06summary.md index d543357..c3d2a11 100644 --- a/src/chapter_2/02-06summary.md +++ b/src/chapter_2/02-06summary.md @@ -1,3 +1,10 @@ + +# 更新 +2024-07-05 +本书已经出版 +![image](https://github.com/Celthi/effective-debugging-zh/assets/5187962/29b04963-5535-432c-b56f-8a2d5dbc2ec6) +由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 +如果你想阅读更加完善的版本,推荐购买正版书籍。 # 总结 本章通过两个内存管理器的实现例子解释了用户的内存块是如何被管理的。对于调试的目的,我们最关心编码在块标签或者其他堆元数据的(取决于具体实现)内存块状态信息。通过利用这些堆元数据,一些调试器命令被创建出来用于揭露任意一个内存块的状态。正如我们看到,当我们知道一点堆内在,容易获取我们取消的信息。如果读者使用不同的内存管理器,鼓励你自己编写相似的工具。你很快会发现你从它们获取信息是很容易的。有了这些知识和帮助工具,我们准备好挑战困难的内存问题。 diff --git a/src/chapter_3.md b/src/chapter_3.md index 6e80335..33b943f 100644 --- a/src/chapter_3.md +++ b/src/chapter_3.md @@ -1,3 +1,10 @@ + +# 更新 +2024-07-05 +本书已经出版 +![image](https://github.com/Celthi/effective-debugging-zh/assets/5187962/29b04963-5535-432c-b56f-8a2d5dbc2ec6) +由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 +如果你想阅读更加完善的版本,推荐购买正版书籍。 # 第三章 内存损坏 内存损坏指覆写一块不属于写者的内存;或者无效地修改内存即使内存属于写者。比如,在竞争条件下,数据被改成了参与的线程没有期待的东西。这些损坏的数据可以是内存管理器的内部堆数据结构或者是用户空间,即应用程序数据对象。这些错误最终显示的样子广泛来说很不同。 diff --git a/src/chapter_3/03-01how_is_memory_corrupted.md b/src/chapter_3/03-01how_is_memory_corrupted.md index 61ddd45..7efc2e2 100644 --- a/src/chapter_3/03-01how_is_memory_corrupted.md +++ b/src/chapter_3/03-01how_is_memory_corrupted.md @@ -1,3 +1,10 @@ + +# 更新 +2024-07-05 +本书已经出版 +![image](https://github.com/Celthi/effective-debugging-zh/assets/5187962/29b04963-5535-432c-b56f-8a2d5dbc2ec6) +由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 +如果你想阅读更加完善的版本,推荐购买正版书籍。 # 内存是怎么损坏的 一块内存损坏的方式有很多种。不管应用程序逻辑层各式各样的错误,内存损坏的普遍原因是有问题的代码访问了超出了被内存管理器或者编译器分配的底层内存块的边界的数据对象。下面列出了在实践中常看到的各式各样内存访问错误。跟那些因为有大量的变量和许多逻辑层总是更加隐晦的大型程序的实际bug一比,这些例子看上去可能简单和愚蠢。但是元数据被损坏是一样的,可以用相同的策略来攻克。 diff --git a/src/chapter_3/03-02debug_memory_corruption.md b/src/chapter_3/03-02debug_memory_corruption.md index d37d351..381e942 100644 --- a/src/chapter_3/03-02debug_memory_corruption.md +++ b/src/chapter_3/03-02debug_memory_corruption.md @@ -1,3 +1,10 @@ + +# 更新 +2024-07-05 +本书已经出版 +![image](https://github.com/Celthi/effective-debugging-zh/assets/5187962/29b04963-5535-432c-b56f-8a2d5dbc2ec6) +由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 +如果你想阅读更加完善的版本,推荐购买正版书籍。 # 调试内存损坏 调试内存损坏真正的挑战是观察到的程序错误没有揭露导致它的错误代码。一个程序通常在有bug的代码作出错误的内存访问时不会显示任何症状。但是程序其中一个变量被意外地改变为一个不正确的值;在一些文献中它被叫做传染。这个变量随着程序继续运行会感染其他变量。这些问题传播最终会发展为一个严重的失败:程序要么crash要么生成错误的结果。因为原因和结果的距离,当错误被注意到时候,最后的变量和正在运行的代码通常跟实际的bug不相关,可以展露出很多在时间和位置的随机性。 diff --git a/src/chapter_3/03-03memory_debugging_tools.md b/src/chapter_3/03-03memory_debugging_tools.md index 74e2971..7b9602f 100644 --- a/src/chapter_3/03-03memory_debugging_tools.md +++ b/src/chapter_3/03-03memory_debugging_tools.md @@ -1,3 +1,10 @@ + +# 更新 +2024-07-05 +本书已经出版 +![image](https://github.com/Celthi/effective-debugging-zh/assets/5187962/29b04963-5535-432c-b56f-8a2d5dbc2ec6) +由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 +如果你想阅读更加完善的版本,推荐购买正版书籍。 # 内存调试工具 如果初始调查没有结论,很多时候是这样的,我们应该怎么继续呢?一个普遍的方式是根据搜集到的信息在一个受控的环境重现问题。如果问题是可重现的,我们可以更近地观察问题和用各种方式指导程序。一个完整的审阅代码可能引出内存损坏的新理论。那么你就可以在调试器下重跑程序,在即将被损坏的内存块设置数据断点。这可能不具有可行性,因为被损坏的内存块的地址每次程序运行的时候可能会不一样。如果重现问题的时机是很重要的,那么调试可能会有所谓的海森堡效应,即调试器带来的失真会改变程序的行为和防止问题的重现。(XT,前几天同事就遇到了这样的问题)。另外,有需要各种定制的工具用来尽可能早的检测内存损坏而不是允许它们传播得更远。在本节中,我们将会看到一些这样的工具和它们的实现 diff --git a/src/chapter_3/03-04heap_vs_stack_memory_corruption.md b/src/chapter_3/03-04heap_vs_stack_memory_corruption.md new file mode 100644 index 0000000..b06a424 --- /dev/null +++ b/src/chapter_3/03-04heap_vs_stack_memory_corruption.md @@ -0,0 +1,17 @@ + +# 更新 +2024-07-05 +本书已经出版 +![image](https://github.com/Celthi/effective-debugging-zh/assets/5187962/29b04963-5535-432c-b56f-8a2d5dbc2ec6) +由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 +如果你想阅读更加完善的版本,推荐购买正版书籍。 +--- +# 更新 +2024-07-05 +本书已经出版 +![image](https://github.com/Celthi/effective-debugging-zh/assets/5187962/29b04963-5535-432c-b56f-8a2d5dbc2ec6) +由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 +如果你想阅读更加完善的版本,推荐购买正版书籍。--- +# 堆与栈内存损坏 + +在内存损坏的情况下,数据对象是从堆上分配还是在堆栈上分配有显着差异。堆对象由内存管理器在运行时分配。它的地址取决于很多因素:具体内存管理器实现的分配策略;内存分配/释放请求的历史,它们的大小分布和顺序,这会影响内存管理器缓存哪些空闲内存块,以及是从缓存中选择新内存块还是从全新的段分配;多线程多处理器环境下的并发内存请求等。由于其动态特性,堆对象通常在同一程序的各个实例中具有不同的地址。每次分配时,其相邻的数据对象也可能不同。另一方面,堆栈变量是由编译器静态分配的。它相对于函数堆栈帧的地址是固定的,并且前后变量是已知且不变的。因为内存损坏与罪魁祸首和受害者的相对位置有很大关系(它们通常是地址空间中的邻居),所以堆内存损坏通常呈现出更多的随机性,而栈内存损坏可能是一致的。 diff --git a/src/chapter_4.md b/src/chapter_4.md index b415311..acad4e2 100644 --- a/src/chapter_4.md +++ b/src/chapter_4.md @@ -1,3 +1,10 @@ + +# 更新 +2024-07-05 +本书已经出版 +![image](https://github.com/Celthi/effective-debugging-zh/assets/5187962/29b04963-5535-432c-b56f-8a2d5dbc2ec6) +由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 +如果你想阅读更加完善的版本,推荐购买正版书籍。 # 第四章 C++对象布局 diff --git a/src/chapter_4/04-01alignment_and_endian.md b/src/chapter_4/04-01alignment_and_endian.md index 24737b6..101e40f 100644 --- a/src/chapter_4/04-01alignment_and_endian.md +++ b/src/chapter_4/04-01alignment_and_endian.md @@ -1,3 +1,10 @@ + +# 更新 +2024-07-05 +本书已经出版 +![image](https://github.com/Celthi/effective-debugging-zh/assets/5187962/29b04963-5535-432c-b56f-8a2d5dbc2ec6) +由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 +如果你想阅读更加完善的版本,推荐购买正版书籍。 # 对齐和大小端 (Alignment and Endian) 各种架构支持类似的原始数据类型,如,字节/byte,half word,word,double word,等等。 diff --git a/src/chapter_4/04-02endian.md b/src/chapter_4/04-02endian.md index 2114621..805d14f 100644 --- a/src/chapter_4/04-02endian.md +++ b/src/chapter_4/04-02endian.md @@ -1,3 +1,10 @@ + +# 更新 +2024-07-05 +本书已经出版 +![image](https://github.com/Celthi/effective-debugging-zh/assets/5187962/29b04963-5535-432c-b56f-8a2d5dbc2ec6) +由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 +如果你想阅读更加完善的版本,推荐购买正版书籍。 # 大小端 具体的架构也需要确定内存中数据的字节顺序,或者是大小端方案。 diff --git a/src/chapter_4/04-03object_layout.md b/src/chapter_4/04-03object_layout.md index 695ecdf..74a0da8 100644 --- a/src/chapter_4/04-03object_layout.md +++ b/src/chapter_4/04-03object_layout.md @@ -1 +1,8 @@ + +# 更新 +2024-07-05 +本书已经出版 +![image](https://github.com/Celthi/effective-debugging-zh/assets/5187962/29b04963-5535-432c-b56f-8a2d5dbc2ec6) +由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 +如果你想阅读更加完善的版本,推荐购买正版书籍。 # C++对象布局 diff --git a/src/chapter_4/04-04search_references_tree.md b/src/chapter_4/04-04search_references_tree.md index 57f1753..d260740 100644 --- a/src/chapter_4/04-04search_references_tree.md +++ b/src/chapter_4/04-04search_references_tree.md @@ -1 +1,8 @@ + +# 更新 +2024-07-05 +本书已经出版 +![image](https://github.com/Celthi/effective-debugging-zh/assets/5187962/29b04963-5535-432c-b56f-8a2d5dbc2ec6) +由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 +如果你想阅读更加完善的版本,推荐购买正版书籍。 # 搜索引用树 diff --git a/src/chapter_5.md b/src/chapter_5.md index edbb61f..5d48cf8 100644 --- a/src/chapter_5.md +++ b/src/chapter_5.md @@ -1 +1,8 @@ + +# 更新 +2024-07-05 +本书已经出版 +![image](https://github.com/Celthi/effective-debugging-zh/assets/5187962/29b04963-5535-432c-b56f-8a2d5dbc2ec6) +由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 +如果你想阅读更加完善的版本,推荐购买正版书籍。 # 第五章 优化后的二进制 diff --git a/src/chapter_5/05-01diff_between_debug_and_release.md b/src/chapter_5/05-01diff_between_debug_and_release.md index 7d96fe0..f68de27 100644 --- a/src/chapter_5/05-01diff_between_debug_and_release.md +++ b/src/chapter_5/05-01diff_between_debug_and_release.md @@ -1 +1,8 @@ + +# 更新 +2024-07-05 +本书已经出版 +![image](https://github.com/Celthi/effective-debugging-zh/assets/5187962/29b04963-5535-432c-b56f-8a2d5dbc2ec6) +由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 +如果你想阅读更加完善的版本,推荐购买正版书籍。 # 调试版本和发行版本的区别 diff --git a/src/chapter_5/05-02challenges_debugging_optimized_binary.md b/src/chapter_5/05-02challenges_debugging_optimized_binary.md index ce8c41a..40fdcf7 100644 --- a/src/chapter_5/05-02challenges_debugging_optimized_binary.md +++ b/src/chapter_5/05-02challenges_debugging_optimized_binary.md @@ -1 +1,8 @@ + +# 更新 +2024-07-05 +本书已经出版 +![image](https://github.com/Celthi/effective-debugging-zh/assets/5187962/29b04963-5535-432c-b56f-8a2d5dbc2ec6) +由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 +如果你想阅读更加完善的版本,推荐购买正版书籍。 # 调试优化代码的挑战 diff --git a/src/chapter_5/05-03assembly_intro.md b/src/chapter_5/05-03assembly_intro.md index 68f0abc..cde5fac 100644 --- a/src/chapter_5/05-03assembly_intro.md +++ b/src/chapter_5/05-03assembly_intro.md @@ -1 +1,8 @@ + +# 更新 +2024-07-05 +本书已经出版 +![image](https://github.com/Celthi/effective-debugging-zh/assets/5187962/29b04963-5535-432c-b56f-8a2d5dbc2ec6) +由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 +如果你想阅读更加完善的版本,推荐购买正版书籍。 # 汇编代码介绍 diff --git a/src/chapter_6.md b/src/chapter_6.md index ce56cc7..56b5058 100644 --- a/src/chapter_6.md +++ b/src/chapter_6.md @@ -1 +1,8 @@ + +# 更新 +2024-07-05 +本书已经出版 +![image](https://github.com/Celthi/effective-debugging-zh/assets/5187962/29b04963-5535-432c-b56f-8a2d5dbc2ec6) +由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 +如果你想阅读更加完善的版本,推荐购买正版书籍。 # 第六章 进程镜像 diff --git a/src/chapter_7.md b/src/chapter_7.md index 1619723..42f40e8 100644 --- a/src/chapter_7.md +++ b/src/chapter_7.md @@ -1 +1,8 @@ + +# 更新 +2024-07-05 +本书已经出版 +![image](https://github.com/Celthi/effective-debugging-zh/assets/5187962/29b04963-5535-432c-b56f-8a2d5dbc2ec6) +由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 +如果你想阅读更加完善的版本,推荐购买正版书籍。 # 第七章 进程镜像 diff --git a/src/chapter_8.md b/src/chapter_8.md index fb3b597..4881311 100644 --- a/src/chapter_8.md +++ b/src/chapter_8.md @@ -1 +1,8 @@ + +# 更新 +2024-07-05 +本书已经出版 +![image](https://github.com/Celthi/effective-debugging-zh/assets/5187962/29b04963-5535-432c-b56f-8a2d5dbc2ec6) +由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 +如果你想阅读更加完善的版本,推荐购买正版书籍。 # 第八章 更多调试进程 diff --git a/src/chapter_9.md b/src/chapter_9.md index 892df63..951aeb2 100644 --- a/src/chapter_9.md +++ b/src/chapter_9.md @@ -1,3 +1,10 @@ + +# 更新 +2024-07-05 +本书已经出版 +![image](https://github.com/Celthi/effective-debugging-zh/assets/5187962/29b04963-5535-432c-b56f-8a2d5dbc2ec6) +由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 +如果你想阅读更加完善的版本,推荐购买正版书籍。 # 第九章 使用Python拓展gdb XT: 本文改编自译者的[知乎文章](https://zhuanlan.zhihu.com/p/152274203) diff --git a/src/chapter_9/09-01pretty_printer.md b/src/chapter_9/09-01pretty_printer.md index 1e46640..7f59497 100644 --- a/src/chapter_9/09-01pretty_printer.md +++ b/src/chapter_9/09-01pretty_printer.md @@ -1,3 +1,10 @@ + +# 更新 +2024-07-05 +本书已经出版 +![image](https://github.com/Celthi/effective-debugging-zh/assets/5187962/29b04963-5535-432c-b56f-8a2d5dbc2ec6) +由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 +如果你想阅读更加完善的版本,推荐购买正版书籍。 # 将难看的数据变得好看 以下面的代码为例 ```c++ diff --git a/src/chapter_9/09-02dry.md b/src/chapter_9/09-02dry.md index 18925ce..b9d731b 100644 --- a/src/chapter_9/09-02dry.md +++ b/src/chapter_9/09-02dry.md @@ -1,3 +1,10 @@ + +# 更新 +2024-07-05 +本书已经出版 +![image](https://github.com/Celthi/effective-debugging-zh/assets/5187962/29b04963-5535-432c-b56f-8a2d5dbc2ec6) +由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 +如果你想阅读更加完善的版本,推荐购买正版书籍。 # 将重复的工作变成一个命令 比如在调试的时候,你知道当前栈指向一个字符串,但是你不知道具体在哪里,你想遍历这个栈将它找出来,那么你可以借助Python自定义一个命令"stackwalk",这个命令可以直接用Python代码遍历栈,将字符串找出来。 diff --git a/src/chapter_9/09-03write_your_owner_pretty_printer.md b/src/chapter_9/09-03write_your_owner_pretty_printer.md index cf5e591..bf0ad67 100644 --- a/src/chapter_9/09-03write_your_owner_pretty_printer.md +++ b/src/chapter_9/09-03write_your_owner_pretty_printer.md @@ -1,3 +1,10 @@ + +# 更新 +2024-07-05 +本书已经出版 +![image](https://github.com/Celthi/effective-debugging-zh/assets/5187962/29b04963-5535-432c-b56f-8a2d5dbc2ec6) +由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 +如果你想阅读更加完善的版本,推荐购买正版书籍。 # 编写你自己的美化器 前言 在CrackingOysters:你还在用GDB调试程序吗?介绍了使用Python拓展gdb方便平时的debug体验。 diff --git a/src/introduction.md b/src/introduction.md index 7673779..fd6486c 100644 --- a/src/introduction.md +++ b/src/introduction.md @@ -1,3 +1,10 @@ + +# 更新 +2024-07-05 +本书已经出版 +![image](https://github.com/Celthi/effective-debugging-zh/assets/5187962/29b04963-5535-432c-b56f-8a2d5dbc2ec6) +由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 +如果你想阅读更加完善的版本,推荐购买正版书籍。 # 引言 这是一本关于调试的书。多年以来,作为一名程序员写代码和调试,我经历了过山车般的情绪变化:困惑,失望,兴奋,以及不停地重复这些。当处理看上去永无止境的bugs时,这是非常真实的。随着我获得了更多的调试技巧,学习到更多产品和架构知识,大部分问题变得容易解决。但是,还是时不时有一些极难的bugs看上去不能解决,需要花费好多小时甚至是好几天去缩小范围和修复极难的问题。 diff --git a/src/translator_preface.md b/src/translator_preface.md index c8ee739..4cecde9 100644 --- a/src/translator_preface.md +++ b/src/translator_preface.md @@ -1,3 +1,10 @@ + +# 更新 +2024-07-05 +本书已经出版 +![image](https://github.com/Celthi/effective-debugging-zh/assets/5187962/29b04963-5535-432c-b56f-8a2d5dbc2ec6) +由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 +如果你想阅读更加完善的版本,推荐购买正版书籍。 # 更新 2023-12-20 本书已经出版, [高效C/C++调试] ISBN: 9787302649717