这是命令 perlhacktips,可以使用我们的多个免费在线工作站之一在 OnWorks 免费托管服务提供商中运行,例如 Ubuntu Online、Fedora Online、Windows 在线模拟器或 MAC OS 在线模拟器
程序:
您的姓名
perlhacktips - Perl 核心 C 代码黑客的技巧
商品描述
本文档将帮助您了解在 Perl 核心 C 上进行黑客攻击的最佳方法
代码。 它涵盖了常见问题、调试、分析等。
如果你还没有读过 perlhack 和 perlhacktut,你可能想先读一下。
COMMON 问题
Perl 源代码遵循 ANSI C89 规则:没有 C99(或 C++)扩展。 在某些情况下,我们必须
考虑 ANSI 之前的要求。 你不在乎一些特别的
平台已经破坏了 Perl? 我听说对 J2EE 程序员的需求仍然很大。
Perl的 环境 问题
· 不使用线程编译
使用线程编译(-Duseithreads)完全重写函数原型
Perl 的。 你最好尝试你的改变。 与此相关的是差异
“Perl_-less”和“Perl_-ly”API之间,例如:
Perl_sv_setiv(aTHX_ ...);
sv_setiv(...);
第一个显式传入上下文,这是例如线程所需要的
建立。 第二个隐含地做到了这一点; 不要把它们混在一起。 如果你不是
传入 aTHX_,您将需要执行 dTHX(或 dVAR)作为
功能。
请参阅 perlguts 中的“如何支持多个解释器和并发”以获取更多信息
关于上下文的讨论。
· 不使用 -DDEBUGGING 编译
DEBUGGING 定义向编译器公开了更多的代码,因此有更多的处理方式
出错。 你应该试试看。
· 引入(非只读)全局变量
不要引入任何可修改的全局变量,真正的全局变量或静态文件。 他们很坏
形成和复杂化多线程和其他形式的并发。 正确的方法是
将它们作为新的解释器变量引入,请参阅 内部变量 (在最后
二进制兼容性)。
引入只读(const)全局变量是可以的,只要你用例如“nm
libperl.a|egrep -v ' [TURtr] '"(如果你的“nm”有 BSD 风格的输出)你的数据
添加的确实是只读的。 (如果是,它不应该出现在那个的输出中
命令。)
如果您想要静态字符串,请将它们设置为常量:
static const char etc[] = "...";
如果您想拥有常量字符串数组,请仔细注意正确的组合
“常量”的:
静态常量字符 * 常量 yippee[] =
{"你好", "ho", "银"};
有一种方法可以完全隐藏任何可修改的全局变量(它们都移到堆中),
编译设置“-DPERL_GLOBAL_STRUCT_PRIVATE”。 它通常不使用,但
可用于测试,在“背景和 PERL_IMPLICIT_CONTEXT”中阅读更多相关信息
在perlguts。
· 不导出你的新函数
某些平台(Win32、AIX、VMS、OS/2,仅举几例)需要任何
公共 API(共享 Perl 库)的一部分被显式标记为导出。
见讨论 嵌入文件 在perlguts。
· 导出你的新函数
真正的新功能或艰巨的重构的新闪亮结果
现在已准备就绪并正确导出。 那么可能会出现什么问题呢?
也许只是因为您的函数不需要首先导出。 珀尔
导出功能的历史悠久而不光荣,以至于它不应该具有。
如果该函数仅在一个源代码文件中使用,请将其设为静态。 见
关于 嵌入文件 在perlguts。
如果该函数在多个文件中使用,但仅用于 Perl 的内部
使用(这应该是常见的情况),不要将其导出到公共 API。 见
关于 嵌入文件 在perlguts。
便携性 问题
以下是编译和/或执行失败的常见原因,不常见
Perl 就是这样。 C FAQ 是很好的睡前阅读。 请用尽可能多的 C 测试您的更改
尽可能使用编译器和平台; 无论如何,我们会的,而且很高兴能自救
公开的尴尬。
如果使用 gcc,您可以添加“-std=c89”选项,希望能捕获其中的大部分
不可移植性。 (但是,它也可能会捕获系统标头中的不兼容性
文件。)
使用配置“-Dgccansipedantic”标志启用gcc“-ansi -pedantic”标志
执行更严格的 ANSI 规则。
如果使用“gcc -Wall”,请注意并非所有可能的警告(如“-Wunitialized”)
除非你也用“-O”编译。
请注意,如果使用 gcc,从 Perl 5.9.5 开始,Perl 核心源代码文件(那些
在源代码分发的顶层,但不是例如 ext/ 下的扩展
使用尽可能多的“-std=c89”、“-ansi”自动编译,
“-pedantic”,以及一系列“-W”标志(参见 cflags.SH)。
还要仔细研究 perlport 以避免对操作系统做出任何错误的假设,
文件系统、字符集等。
你可以偶尔尝试一下“make microperl”,看看我们是否还能编译 Perl
只需要最少的接口。 (参见 README.micro。)
不要假设操作系统指示某个编译器。
· 将指针转换为整数或将整数转换为指针
虚空漂流者(U8* p)
{
IV i = p;
or
虚空漂流者(U8* p)
{
IV i = (IV)p;
两者都是坏的,坏的,不可移植的。 使用 PTR2IV() 做对的宏。
(同样,有 PTR2UV(), PTR2NV(), INT2PTR()和 NUM2PTR().)
· 函数指针和数据指针之间的转换
从技术上讲,函数指针和数据指针之间的转换是不可移植的
和未定义,但实际上它似乎工作,但你应该使用
FPTR2DPTR() 和 DPTR2FPTR() 宏。 有时你也可以和工会玩游戏。
· 假设 sizeof(int) == sizeof(long)
有 longs 为 64 位的平台,以及 int 为 64 位的平台,以及
虽然我们要震惊你,即使是短裤是 64 位的平台。 这是所有的了
根据C标准合法。 (换句话说,“long long”不是一种便携方式
指定 64 位,并且“long long”甚至不能保证比
“长”。)
相反,使用定义 IV、UV、IVSIZE、I32SIZE 等。 避免类似的事情
I32 因为它们是 而不去 保证是 究竟 32 位,它们是 at 最少 32位
他们也不能保证 INT or 长. 如果您确实明确需要 64 位
变量,使用 I64 和 U64,但前提是由 HAS_QUAD 保护。
· 假设可以为任何类型的数据解引用任何类型的指针
字符 *p = ...;
长马 = *p; /* 坏的 */
许多平台,完全正确,如果 p
碰巧没有正确对齐。
· 左值强制转换
(int)*p = ...; /* 坏的 */
简直不便携。 让你的左值成为正确的类型,或者使用临时的
变量,或联合的肮脏技巧。
· 认为 什么 关于结构(尤其是那些你无法控制的,比如那些
来自系统标题)
· 结构体中存在某个字段
· 除了你所知道的领域,没有其他领域存在
· 字段具有特定的符号、大小或类型
· 字段是按一定顺序排列的
· 虽然 C 保证结构定义中指定的顺序,
不同平台之间的定义可能不同
· sizeof(struct) 或对齐方式在任何地方都相同
· 字段之间可能会有填充字节来对齐字段 -
字节可以是任何东西
· 结构需要对齐到所需的最大对齐
按字段 - 对于本机类型通常相当于
sizeof() 领域的
· 假设字符集是ASCIIish
Perl 可以在EBCDIC 平台下编译和运行。 参见 perlebcdic。 这是透明的
大多数情况下,但由于字符集不同,您不应该使用数字
(十进制、八进制或十六进制)常量来引用字符。 你可以放心地说“A”,
但不是 0x41。 您可以放心地说“\n”,但不能说“\012”。 但是,您可以使用宏
定义于 utf8.h 可移植地指定任何代码点。 ”LATIN1_TO_NATIVE(0xDF)”是
将成为代码点,在任何平台上都意味着 LATIN SMALL LETTER SHARP S
您正在运行(在 ASCII 平台上,它无需添加任何额外代码即可编译,因此
那些性能下降为零)。 “LATIN1_TO_NATIVE”的可接受输入
从 0x00 到 0xFF。 如果您的输入不能保证在该范围内,请使用
“UNICODE_TO_NATIVE”代替。 “NATIVE_TO_LATIN1”和“NATIVE_TO_UNICODE”翻译
相反的方向。
如果您需要没有助记符名称的字符的字符串表示
在 C 中,您应该将其添加到列表中 再生/unicode_constants.pl,并有 Perl
根据当前平台为您创建“#define”。
请注意,“是福” 并福” 宏 方便.h 在本机代码上正常工作
点和字符串。
此外,ASCII 中的范围 'A' - 'Z' 是 26 个大写字母的不间断序列
人物。 在 EBCDIC 中情况并非如此。 也不是“a”到“z”。 但是 '0' - '9' 是一个
在两个系统中都没有中断的范围。 不要对其他范围做任何假设。 (注意
对正则表达式模式中范围的特殊处理使其在 Perl 代码中出现
前面提到的范围都是完整的。)
现有代码中的很多注释都忽略了 EBCDIC 的可能性,可能是
因此错误,即使代码有效。 这其实是对成功人士的致敬
能够处理 EBCDIC 的透明插入,而无需更改预
现有代码。
UTF-8 和 UTF-EBCDIC 是两种不同的编码,用于表示 Unicode 代码点
作为字节序列。 具有相同名称(但不同定义)的宏
utf8.h 和 utfebcdic.h 用于让调用代码认为只有
一种这样的编码。 这几乎总是被称为“utf8”,但它意味着
EBCDIC 版本也是如此。 同样,代码中的注释很可能是错误的,即使
代码本身是对的。 比如UTF-8“不变字符”的概念
ASCII 和 EBCDIC 之间的区别。 在 ASCII 平台上,只有不
设置高阶位(即其序数是严格的 ASCII,0 - 127)是
不变,并且代码中的文档和注释可能会假设,通常
指的是诸如“禁止”之类的东西。 情况不同,并没有那么简单
在 EBCDIC 机器上,但只要代码本身使用“NATIVE_IS_INVARIANT()”
宏适当地,它的工作原理,即使注释是错误的。
如 perlhack 中的“TESTING”中所述,在编写测试脚本时,文件
t/charset_tools.pl 包含一些有用的功能,用于编写对两者都有效的测试
ASCII 和 EBCDIC 平台。 有时,虽然,测试不能使用一个函数,它是
不方便根据平台有不同的测试版本。 有 20
在 Perl 当前识别的所有 4 个字符集中相同的代码点
(3 个 EBCDIC 代码页加上 ISO 8859-1 (ASCII/Latin1))。 这些可以用于这样的
测试,虽然有很小的可能性 Perl 将可用
另一个字符集,打破了你的测试。 除了这些代码点之一之外,所有代码点都是 C0
控制字符。 最重要的相同控件是“\0”、“\r”、
和“\N{VT}”(也可以指定为“\cK”、“\x0B”、“\N{U+0B}”或“\013”)。 单人
非控制是 U+00B6 PILCROW SIGN。 相同的控件具有相同的位
所有 4 个字符集中的模式,无论包含的字符串的 UTF8ness
他们。 U+B6 的位模式在所有 4 个非 UTF8 字符串中都相同,但不同
在每个包含的字符串是 UTF-8 编码时。 唯一的其他代码点
所有 4 个字符集都有某种相同之处,即 0xDC 和 0xFC 对。
这些一起代表大写和小写拉丁字母 U WITH DIAERESIS,但是
大写和小写可以颠倒:0xDC 是Latin1 中的大写字母,0xFC 是
小写字母,而 0xFC 是 EBCDIC 中的大写字母,而 0xDC 是小写字母。 这
可以利用 factoid 编写相同的不区分大小写的测试
所有 4 个字符集。
· 假设字符集只是ASCII
ASCII 是 7 位编码,但字节中有 8 位。 128 个额外字符
根据地区不同有不同的含义。 没有语言环境,目前这些
额外的字符通常被认为是未分配的,这已经呈现
一些问题。 这已从 5.12 开始更改,以便这些字符可以
被认为是 Latin-1 (ISO-8859-1)。
·混合#define和#ifdef
#define BURGLE(x) ... \
#ifdef BURGLE_OLD_STYLE /* 坏 */
... 用旧的方式来做 ... \
的#else
...以新的方式来做...\
#ENDIF
您不能可移植地“堆叠”cpp 指令。 例如在上面你需要两个
分开 盗窃() #defines,每个#ifdef 分支一个。
· 在#endif 或 #else 之后添加非注释内容
#ifdef 斯诺什
...
#else !SNOSH /* 坏 */
...
#endif SNOSH /* 坏 */
#endif 和 #else 后面不能有任何非注释。 如果你
想要记录正在发生的事情(这是一个好主意,特别是如果分支是
长),使用(C)注释:
#ifdef 斯诺什
...
#else /* !SNOSH */
...
#endif /* SNOSH */
gcc 选项“-Wendif-labels”警告错误变体(默认情况下在启动时
来自 Perl 5.9.4)。
· 在枚举列表的最后一个元素后有一个逗号
枚举颜色{
蔚蓝,
查特勒斯,
肉桂,/* 坏 */
};
不便携。 去掉最后一个逗号。
另请注意,枚举是否隐式可变形为整数
编译器,您可能需要 (int)。
· 使用 //-注释
// 这个函数 bamfoodles zorklator。 /* 坏的 */
那是 C99 或 C++。 Perl 是 C89。 许多 C 都允许使用 //-comments
编译器,但提高 ANSI C89 的严格性(我们喜欢这样做)导致
编译失败。
· 混合声明和代码
无效zorklator()
{
整数n = 3;
set_zorkmids(n); /* 坏的 */
整数 q = 4;
那是 C99 或 C++。 某些 C 编译器允许这样做,但您不应该这样做。
gcc 选项“-Wdeclaration-after-statements”扫描此类问题(默认情况下
从 Perl 5.9.4 开始)。
· 内部引入变量 为了()
for(int i = ...; ...; ...) { /* 坏 */
那是 C99 或 C++。 虽然在 C89 中也有它确实非常好,
要限制循环变量的范围,唉,我们不能。
· 混合有符号字符指针和无符号字符指针
int foo(char *s) { ... }
...
无符号字符 *t = ...; /* 或 U8* t = ... */
脚); /* 坏的 */
虽然这是合法的做法,但它肯定是可疑的,至少在某些情况下是彻头彻尾的致命
一个平台:例如 VMS cc 认为这是一个致命错误。 人的原因之一
经常犯这个错误是“裸字符”,因此取消引用“裸字符”
字符指针”具有未定义的符号:它取决于编译器和标志
编译器和底层平台的结果,无论结果是有符号还是无符号。
出于同样的原因,使用“char”作为数组索引是不好的。
· 具有字符串常量及其参数作为字符串子字符串的宏
常数
#define FOO(n) printf("number = %d\n", n) /* BAD */
FOO(10);
ANSI 之前的语义相当于
printf("10umber = %d\10");
这可能不是您所期望的。 不幸的是,至少有一个合理的
通用和现代 C 编译器在这里“真正的向后兼容”,在 AIX 中
即使 AIX 编译器的其余部分非常愉快地是 C89,仍然会发生什么。
· 对非基本 C 类型使用 printf 格式
IV 我 = ...;
printf("i = %d\n", i); /* 坏的 */
虽然这可能会在某些平台上偶然工作(其中 IV 恰好是“int”),
一般来说它不能。 IV 可能更大一些。 更糟糕的情况是
更具体的类型(由 Perl 中的配置步骤定义) 配置文件):
uid_t 谁 = ...;
printf("谁 = %d\n", 谁); /* 坏的 */
这里的问题是 Uid_t 可能不仅不是“int”范围的,而且还可能是
无符号,在这种情况下,大 uid 将打印为负值。
没有简单的解决方案,因为 打印输出()智商有限,但
对于许多类型,正确的格式都可以使用“f”或“_f”后缀,例如
例:
IVdf /* 十进制的 IV */
UVxf /* UV 是十六进制 */
printf("i = %"IVdf"\n", i); /* IVdf 是一个字符串常量。 */
Uid_t_f /* Uid_t 十进制 */
printf("who = %"Uid_t_f"\n", who);
或者您可以尝试转换为“足够宽”的类型:
printf("i = %"IVdf"\n", (IV)something_very_small_and_signed);
还要记住 %p 格式确实需要一个空指针:
U8* p = ...;
printf("p = %p\n", (void*)p);
gcc 选项“-Wformat”扫描此类问题。
· 盲目使用可变参数宏
gcc 已经有一段时间了,它们有自己的语法,而 C99 给它们带来了
标准化语法。 不要使用前者,只有在以下情况下才使用后者
HAS_C99_VARIADIC_MACROS 已定义。
· 一味传递va_list
并非所有平台都支持将 va_list 传递给进一步的可变参数 (stdarg) 函数。 这
正确的做法是使用 Perl_va_copy() 如果 NEED_VA_COPY
被定义。
· 使用gcc语句表达式
val = ({...;...;...}); /* 坏的 */
虽然是一个不错的扩展,但它并不便携。 Perl 代码确实会使用它们,如果
可以获得一些额外的速度(本质上是一种时髦的内联形式),但是你
不应该。
· 将多个语句绑定在一个宏中
使用宏 STMT_START 和 STMT_END。
STMT_START {
...
} STMT_END
· 测试操作系统或版本何时应该测试功能
#ifdef __FOONIX__ /* 坏 */
foo = quux();
#ENDIF
除非您 100% 肯定地知道 qux() 仅可用于
“福尼克斯”操作系统 和 这是可用的 和 正确工作 所有 过去,
当下, 和 “Foonix”的未来版本,以上是非常错误的。 这是更多
正确(虽然仍然不完美,因为以下是编译时检查):
#ifdef HAS_QUUX
foo = quux();
#ENDIF
HAS_QUUX 如何在需要的地方定义? 好吧,如果 Foonix 碰巧
足够Unixy能够运行Configure脚本,并且Configure已经教过
关于检测和测试 qux(), HAS_QUUX 将被正确定义。 其他
平台,相应的配置步骤也有望做到这一点。
在紧要关头,如果您迫不及待地等待配置被教育,或者如果您有一个好的
预感在哪里 qux() 可能可用,您可以暂时尝试以下操作:
#if (已定义(__FOONIX__) || 已定义(__BARNIX__))
# 定义 HAS_QUUX
#ENDIF
...
#ifdef HAS_QUUX
foo = quux();
#ENDIF
但无论如何,尽量将功能和操作系统分开。
· 假设Perl的返回值指向的静态内存的内容
C 库函数的包装器不会改变。 许多 C 库函数返回
指向静态存储的指针,可以被后续调用相同或
相关功能。 Perl 为其中一些功能提供了轻量级的包装器,并且
它不会复制静态内存。 一个很好的例子是
对程序有效的环境变量。 Perl 有“PerlEnv_getenv”
从环境中获取值。 但是返回的是一个指向静态内存的指针
C 库。 如果您正在使用该值来立即测试某些内容,那就是
很好,但是如果您保存该值并希望它在以后的处理中保持不变,您
会错,但也许你不会知道,因为不同的 C 库
实现的行为不同,并且您正在测试的平台上的实现
可能适用于您的情况。 但在某些平台上,随后调用
“PerlEnv_getenv”或相关函数将覆盖您第一次调用的内存
指着。 这导致了一些难以调试的问题。 在 perlapi 中做一个“savepv”
制作一个副本,从而避免这些问题。 您必须在使用时释放副本
这样做是为了避免内存泄漏。 如果您无法控制它何时被释放,您将
需要以凡人的标量制作副本,如下所示:
if ((s = PerlEnv_getenv("foo") == NULL) {
... /* 处理 NULL 情况 */
}
其他{
s = SvPVX(sv_2mortal(newSVpv(s, 0)));
}
仅当“s”为“NUL”终止时,上面的示例才有效; 否则你必须通过
它的长度为“newSVpv”。
有问题 系统 接口
· 分配(0) 重新分配(0), calloc(0, 0) 是不可移植的。 便携式分配在
至少一个字节。 (一般来说,您应该很少需要在这种低级别上工作,但是
而是使用各种 malloc 包装器。)
· 打印函数() - 返回类型不可移植。 利用 我的_snprintf() 代替。
安全性 问题
最后但并非最不重要的是,这里有各种更安全编码的技巧。 另见 perlclib
应该使用 libc/stdio 替代品。
· 不使用 gets()
否则我们会公开嘲笑你。 严重地。
· 不使用 临时文件()
使用 VHDL 语言编写 mkstemp() 代替。
· 不使用 字符串() or 字符串() or strncpy() or 字符串猫()
使用 VHDL 语言编写 我的_strlcpy() 和 我的_strlcat() 相反:他们要么使用本机实现,
或 Perl 自己的实现(借自 INN 的公共领域实现)。
· 不使用 冲刺() or vsprintf()
如果您真的只想要纯字节字符串,请使用 我的_snprintf() 和 我的_vsnprintf()
相反,它将尝试使用 打印函数() 和 vsnprintf() 如果这些更安全的 API 是
可用的。 如果你想要比普通字节字符串更漂亮的东西,请使用“Perl_form”()
或 SV 和“Perl_sv_catpvf()”。
请注意,glibc "printf()"、"sprintf()" 等在 glibc 2.17 版之前有问题。
他们不允许具有精度的“%.s”格式创建无效的字符串
如果程序的当前底层语言环境是 UTF-8,则为 UTF-8。 发生的事情是
%s 及其操作数被简单地跳过,没有任何通知。
.
· 不使用 原子()
使用 VHDL 语言编写 grok_atoUV() 代替。 原子() 在溢出时具有不明确的行为,并且不能
用于增量解析。 它也受语言环境的影响,这是不好的。
· 不使用 strtol() or strtoul()
使用 VHDL 语言编写 grok_atoUV() 代替。 strtol() or strtoul() (或他们的 IV/UV 友好宏
伪装, 斯特罗尔() 和 斯特劳尔()或 环环() 和 阿图尔() 受语言环境影响,
不好。
调试
你可以编译一个特殊的 Perl 调试版本,它允许你使用“-D”
Perl 的选项,以了解有关 Perl 正在做什么的更多信息。 但有时没有
除了使用调试器深入研究之外,还可以查看核心转储的堆栈跟踪
(在错误报告中非常有用),或者试图找出核心之前出了什么问题
dump 发生了,或者我们如何最终得到错误或意外的结果。
戳 at Perl的
要真正了解 Perl,您可能需要构建 Perl 以进行调试,例如
这个:
./配置 -d -D 优化=-g
使
“-g”是 C 编译器的一个标志,用于让它产生调试信息,这将允许
我们单步执行一个正在运行的程序,并查看我们在哪个 C 函数中(没有
我们可能只看到函数的数字地址的调试信息,
这不是很有帮助)。
配置 还将打开“调试”编译符号,它启用所有
Perl 中的内部调试代码。 有很多东西可以调试
这个:perlrun 列出了所有这些,了解它们的最好方法是玩
跟他们。 最有用的选项可能是
l 上下文(循环)栈处理
t 跟踪执行
o 方法和重载解析
c 字符串/数字转换
调试代码的某些功能可以使用 XS 模块来实现。
-Dr => 使用重新“调试”
-Dx => 使用 O '调试'
运用 a 源级 调试器
如果“-D”的调试输出对你没有帮助,是时候单步调试 perl 了
使用源级调试器执行。
· 我们将在这里使用“gdb”作为我们的例子; 这些原则将适用于任何调试器(许多
供应商将他们的调试器称为“dbx”),但请查看您正在使用的调试器的手册。
要启动调试器,请键入
gdb./perl
或者,如果您有一个核心转储:
gdb ./perl 核心
您需要在 Perl 源代码树中执行此操作,以便调试器可以读取源代码。
您应该会看到版权消息,然后是提示。
(GDB)
“帮助”将带您进入文档,但这里是最有用的命令:
· 运行 [参数]
使用给定的参数运行程序。
· 中断函数名称
· 破解 source.c:xxx
告诉调试器当我们到达指定的
函数(但请参阅 perlguts 中的“内部函数”!)或命名中的给定行
源文件。
· 步
一次一行地浏览程序。
· 下一个
一次单步执行程序,而不进入函数。
· 继续
运行直到下一个断点。
· 结束
运行到当前函数结束,然后再次停止。
· '进入'
只需按 Enter 将再次执行最近的操作 - 这是一种祝福
逐步完成数英里的源代码。
· p型
打印给定参数的 C 定义。
(gdb) ptype PL_op
类型 = 结构操作 {
操作 * op_next;
操作 * op_sibparent;
操作 *(*op_ppaddr)(void);
PADOFFSET op_targ;
无符号整数 op_type : 9;
无符号整数 op_opt : 1;
无符号整数 op_slabbed : 1;
无符号整数 op_savefree : 1;
无符号整数 op_static:1;
无符号整数 op_folded : 1;
无符号整数 op_spare : 2;
U8 操作标志;
U8 op_private;
} *
· 打印
执行给定的 C 代码并打印其结果。 警告: Perl 大量使用
宏,和 GDB 不一定支持宏(见后面的“gdb 宏支持”)。
您必须自己替换它们,或者在源代码文件上调用 cpp(请参阅
“.i 目标”)因此,例如,您不能说
打印 SvPV_nolen(sv)
但你必须说
打印 Perl_sv_2pv_nolen(sv)
您可能会发现拥有一个“宏字典”很有帮助,您可以通过说“cpp
-dM perl.c | 排序”。即便如此, CPP 不会为您递归应用这些宏。
GDB 宏 支持
最近的版本 GDB 有相当好的宏支持,但为了使用它,你需要
使用调试信息中包含的宏定义编译 perl。 使用 GCC
3.1 版,这意味着使用“-Doptimize=-g3”进行配置。 其他编译器可能会使用
不同的开关(如果它们完全支持调试宏)。
倾倒 Perl的 时间 结构
绕过这个宏地狱的一种方法是在 转储文件; 这些
有点像内部的 Devel::Peek,但它们也涵盖 OP 和其他结构
这是您无法从 Perl 获得的。 让我们举个例子。 我们将使用“$a = $b + $c”
之前使用过,但给它一些上下文:“$b = "6XXXX"; $c = 2.3;"。 哪里好
停下来逛逛的地方?
“pp_add”怎么样,我们之前检查过的用于实现“+”运算符的函数:
(gdb) 打破 Perl_pp_add
1x0f 处的断点 46249:文件 pp_hot.c,第 309 行。
请注意,我们使用“Perl_pp_add”而不是“pp_add”——参见 perlguts 中的“内部函数”。 和
断点到位,我们可以运行我们的程序:
(gdb) 运行 -e '$b = "6XXXX"; $c = 2.3; $a = $b + $c'
当 gdb 读取相关的源文件和库时,很多垃圾会过去,并且
然后:
断点 1,在 pp_hot.c:309 处的 Perl_pp_add()
309 DSP; 数据目标; tryAMAGICbin(add,opASSIGN);
(gdb) 步骤
第311话
(GDB)
我们之前看过这段代码,我们说“dPOPTOPnnrl_ul”安排了两个
“NV”要放置在“左”和“右”中——让我们稍微扩展一下:
#define dPOPTOPnnrl_ul NV 右 = POPn; \
SV *leftsv = TOPs; \
NV 左 = USE_LEFT(leftsv) ? SvNV(leftsv) : 0.0
“POPn”从堆栈顶部获取 SV 并直接获取其 NV(如果
“SvNOK”已设置)或通过调用“sv_2nv”函数。 “TOPs”从
堆栈顶部 - 是的,“POPn”使用“TOPs” - 但不会删除它。 然后我们使用“SvNV”来
以与以前相同的方式从“leftsv”获取 NV - 是的,“POPn”使用“SvNV”。
由于我们没有 $b 的 NV,我们必须使用“sv_2nv”来转换它。 如果我们一步
再次,我们会发现自己在那里:
(gdb) 步骤
Perl_sv_2nv (sv=0xa0675d0) 在 sv.c:1669
第1669章
(GDB)
我们现在可以使用“Perl_sv_dump”来调查 SV:
(gdb) 打印 Perl_sv_dump(sv)
SV = PV(0xa057cc0) at 0xa0675d0
参考计数 = 1
标志 = (POK,pPOK)
PV = 0xa06a510 "6XXXX"\0
当前 = 5
长度 = 6
$1 = 无效
我们知道我们将得到 6,所以让我们完成子程序:
(gdb) 完成
运行直到退出 #0 Perl_sv_2nv (sv=0xa0675d0) at sv.c:1671
0x462669 在 Perl_pp_add () at pp_hot.c:311
第311话
我们也可以dump出这个op:当前的op总是存储在“PL_op”中,我们可以dump出
它与“Perl_op_dump”。 这将为我们提供与 B::Debug 类似的输出。
(gdb) 打印 Perl_op_dump(PL_op)
{
13 类型 = 添加 ===> 14
目标 = 1
标志 =(标量,孩子)
{
类型 = 空 ===> (12)
(是 rv2sv)
标志 =(标量,孩子)
{
11 类型 = gvsv ===> 12
标志 =(标量)
GV = 主要:: b
}
}
#稍后完成#
运用 GDB 至 看 at 具体的 部分 of a 程序
通过上面的示例,您知道要查找“Perl_pp_add”,但是如果有
在整个地方多次调用它,或者你不知道你是什么操作
寻找?
一种方法是在您要查找的内容附近的某处注入一个罕见的调用。 为了
例如,您可以在方法之前添加“study”:
学习;
在 gdb 中做:
(gdb) 打破 Perl_pp_study
然后一步一步直到你找到你要找的东西。 这在循环中效果很好,如果你
只想在某些迭代中中断:
对于我的 $c (1..100) {
研究如果 $c == 50;
}
运用 GDB 至 看 at 什么 这些因素包括原料奶的可用性以及达到必要粉末质量水平所需的工艺。 解析器/词法分析器 旨在 做
如果您想查看 perl 在解析/词法分析您的代码时在做什么,您可以使用“BEGIN
{}”:
打印“之前\n”;
开始{学习; }
打印“之后\n”;
在 gdb 中:
(gdb) 打破 Perl_pp_study
如果您想查看解析器/词法分析器在“if”块等内部做什么
需要有点棘手:
if ($a && $b && do { BEGIN { study } 1 } && $c) { ... }
源 守则 静态的 分析
存在用于分析 C 源代码的各种工具 静态地,而不是 动态,
也就是说,不执行代码。 可以检测资源泄漏,未定义
行为、类型不匹配、可移植性问题、会导致非法的代码路径
内存访问,以及其他类似的问题,只需解析 C 代码并查看
结果图,它说明了执行和数据流的什么。 至于
事实上,这正是 C 编译器知道对可疑代码发出警告的方式。
皮棉, 夹板
优秀的老式 C 代码质量检查器“lint”可在多个平台上使用,但
请注意,不同的人有几种不同的实现方式
供应商,这意味着标志在不同平台上是不相同的。
有一种称为“夹板”(安全编程 Lint)的 lint 变体,可从
http://www.splint.org/ 应该可以在任何类 Unix 平台上编译。
有“棉绒”和Makefile 中的目标,但您可能不得不处理
标志(见上文)。
覆盖范围
覆盖率 (http://www.coverity.com/) 是一种类似于 lint 的产品,并作为测试平台
他们的产品 他们定期检查几个开源项目,然后他们给出
向缺陷数据库开放源代码开发人员的帐户。
CPD (剪切和粘贴 探测器)
cpd 工具检测剪切和粘贴编码。 如果剪切和粘贴代码的一个实例
更改,所有其他位置可能也应该更改。 因此这样的代码应该
可能会变成子程序或宏。
持续时间(http://pmd.sourceforge.net/cpd.html) 是 pmd 项目的一部分
(http://pmd.sourceforge.net/)。 pmd 最初是为 Java 的静态分析而编写的
代码,但后来它的 cpd 部分被扩展为解析 C 和 C++。
从 SourceForge 站点下载 pmd-bin-XYzip (),从
它,然后在源代码上运行它:
java -cp pmd-XYjar net.sourceforge.pmd.cpd.CPD \
--minimum-tokens 100 --files /some/where/src --language c > cpd.txt
您可能会遇到内存限制,在这种情况下,您应该使用 -Xmx 选项:
java -Xmx512M ...
GCC 警告
尽管可以写很多关于 gcc 警告的不一致和覆盖问题
(比如“-Wall”不意味着“所有警告”,或者一些常见的可移植性问题不是
被“-Wall”覆盖,或者“-ansi”和“-pedantic”都定义不明确
警告的集合等等),gcc 仍然是保持我们编码的有用工具
鼻子干净。
“-Wall”默认开启。
“-ansi”(和它的搭档,“-pedantic”)总是很高兴,但是
不幸的是,它们在所有平台上都不是安全的,例如它们可能导致致命
与系统标头冲突(Solaris 是一个主要示例)。 如果配置
使用“-Dgccansipedantic”,“cflags”前端选择“-ansi -pedantic”作为
已知安全的平台。
从 Perl 5.9.4 开始,添加了以下额外标志:
·“-Wendif-标签”
·“-Wextra”
· "-Wdeclaration-after-statement"
拥有以下标志会很好,但他们首先需要自己的 Augean
稳定大师:
·“-Wpointer-arith”
· "-Wshadow"
· "-Wstrict-prototypes"
"-Wtraditional" 是 gcc 捆绑大量
一个交换机下的警告(实际上不可能部署,因为它会
抱怨很多)但它确实包含一些有益的警告
可单独使用,例如宏中有关字符串常量的警告
包含宏参数:这在 ANSI 之前的行为与在 ANSI 中的行为不同,
并且一些 C 编译器仍在过渡中,AIX 就是一个例子。
</p> of other C 编译器
其他 C 编译器(是的,有 旨在 除了 gcc 之外的其他 C 编译器)通常都有它们的“严格
ANSI”或“带有一些可移植性扩展的严格 ANSI”模式,例如 Sun
Workshop 有它的“-Xa”模式(虽然是隐含的),或者 DEC(现在,HP...)有它的
“-std1”模式开启。
记忆 调试器
注意 1:在较旧的内存调试器下运行,例如 Purify、valgrind 或 Third degree
大大减慢执行速度:秒变成分钟,分钟变成小时。 为了
例如从 Perl 5.8.1 开始,ext/Encode/t/Unicode.t 需要非常长的时间
在例如 Purify、Third degree 和 valgrind 下完成。 在 valgrind 下需要超过
六个小时,即使是在一台活泼的电脑上。 所说的测试必须做一些相当
对内存调试器不友好。 如果你不想等待,你可以简单地杀死
远离 perl 进程。 valgrind 的执行速度大约减慢了 10 倍,
AddressSanitizer 按因子 2。
注意 2: 尽量减少内存泄漏误报的次数(参见“PERL_DESTRUCT_LEVEL”
更多信息),您必须将环境变量 PERL_DESTRUCT_LEVEL 设置为 2。对于
例如,像这样:
环境 PERL_DESTRUCT_LEVEL=2 valgrind ./perl -Ilib ...
注意 3: 当 eval 或
需要,在调用堆栈中看到“S_doeval”是这些的好兆头。 修复这些泄漏
不幸的是,这很重要,但它们最终必须被修复。
注意 4: DynaLoader 不会在其自身之后完全清理,除非 Perl 是用
配置选项“-Accflags=-DDL_UNLOAD_ALL_AT_EXIT”。
瓦尔格林德
valgrind 工具可用于找出内存泄漏和非法堆内存
访问。 从 3.3.0 版本开始,Valgrind 仅支持 x86、x86-64 和 PowerPC 上的 Linux
和达尔文 (OS X) 在 x86 和 x86-64 上)。 特殊的“test.valgrind”目标可用于
在 valgrind 下运行测试。 发现的错误和内存泄漏记录在名为
测试文件.valgrind 默认情况下,输出内联显示。
用法示例:
制作 test.valgrind
由于 valgrind 会增加大量开销,因此运行测试将需要更长的时间。 这
valgrind 测试支持并行运行以帮助解决此问题:
TEST_JOBS=9 制作 test.valgrind
请注意,上述两个调用将非常冗长,因为可访问内存和泄漏 -
默认情况下启用检查。 如果您只想查看纯错误,请尝试:
VG_OPTS='-q --leak-check=no --show-reachable=no' TEST_JOBS=9 \
制作 test.valgrind
Valgrind 还提供了一个 cachegrind 工具,在 perl 上调用为:
VG_OPTS=--tool=cachegrind 制作 test.valgrind
由于系统库(最明显的是 glibc)也触发错误,valgrind 允许
使用抑制文件抑制此类错误。 自带的默认抑制文件
valgrind 已经捕获了很多。 一些额外的抑制定义在
t/perl.supp.
要获得 valgrind 并了解更多信息,请参阅
http://valgrind.org/
地址消毒剂
AddressSanitizer 是一个 clang 和 gcc 扩展,自 v3.1 以来包含在 clang 和 gcc 自
v4.8。 它检查非法堆指针、全局指针、堆栈指针和释放后使用
错误,并且足够快,您可以轻松编译调试或优化的 perl
用它。 但它不检查内存泄漏。 AddressSanitizer 可用于 Linux,
Mac OS X 和很快在 Windows 上。
要使用 AddressSanitizer 构建 perl,您的配置调用应如下所示:
sh 配置 -des -Dcc=clang \
-Accflags=-faddress-sanitizer -Aldflags=-faddress-sanitizer \
-Alddlflags=-共享\ -faddress-sanitizer
这些参数的意思是:
· -Dcc=clang
如果它不在您的文件中,则应将其替换为您的 clang 可执行文件的完整路径
路径。
· -Accflags=-地址-消毒剂
使用 AddressSanitizer 编译 perl 和扩展源。
· -Aldflags=-地址-消毒剂
将 perl 可执行文件与 AddressSanitizer 链接。
· -Alddlflags=-shared\ -faddress-sanitizer
使用 AddressSanitizer 链接动态扩展。 您必须手动指定“-shared”
因为使用“-Alddlflags=-shared”将阻止配置设置默认值
“lddlflags”的值,通常包含“-shared”(至少在 Linux 上)。
也可以看看http://code.google.com/p/address-sanitizer/wiki/AddressSanitizer>.
剖析
根据您的平台,有多种分析 Perl 的方法。
有两种常用的分析可执行文件的技术: 统计 时间采样
和 基本块 计数.
第一种方法定期对 CPU 程序计数器进行采样,并且由于
程序计数器可以与为函数生成的代码相关联,我们得到一个
程序在哪些函数中花费时间的统计视图。 警告是
非常小的/快速的函数出现在配置文件中的可能性较低,并且
周期性地中断程序(这通常相当频繁,在
毫秒级)施加了额外的开销,可能会扭曲结果。 这
第一个问题可以通过运行更长时间的代码来缓解(通常这是一个很好的
分析的想法),第二个问题通常由分析工具保护
他们自己。
第二种方法将生成的代码划分为 基本包 块. 基本块是
仅在开头输入并仅在结尾退出的代码段。 为了
例如,条件跳转开始一个基本块。 基本块分析通常通过
仪表 通过添加代码 进入 基本包 阻止 #nnnn 簿记代码到
生成的代码。 在代码执行期间,基本块计数器然后
适当更新。 需要注意的是,添加的额外代码可能会扭曲结果:
同样,分析工具通常会尝试将其自身的影响排除在结果之外。
教授 剖析
教授 是许多 Unix 平台中可用的分析工具,它使用 统计 时间-
采样. 您可以构建一个配置文件版本 perl的 通过使用带有标志的 gcc 进行编译
“-pg”。 要么编辑 配置文件 或重新运行 配置. 运行 Perl 的配置版本
将创建一个名为的输出文件 gmon.out. 其中包含收集的分析数据
在执行过程中。
快速提示:
$ sh 配置-des -Dusedevel -Accflags='-pg' \
-Aldflags='-pg' -Alddlflags='-pg -shared' \
&& 制作 perl
$ ./perl ... # 在当前目录下创建 gmon.out
$ gprof ./perl > 输出
少出
(您可能需要将“-shared”添加到 <-Alddlflags> 行,直到 RT #118199
解决)
这个 教授 然后工具可以以各种方式显示收集到的数据。 通常 教授
了解以下选项:
· -一种
从配置文件中抑制静态定义的函数。
·-b
禁止配置文件中的详细描述。
· -e 例程
从配置文件中排除给定的例程及其后代。
· -f 例程
在配置文件中仅显示给定的例程及其后代。
·-s
生成一个名为的摘要文件 gmon.sum. 然后可以将其提供给后续的 gprof
运行以在多次运行中累积数据。
·-z
显示使用率为零的例程。
有关可用命令和输出格式的更详细说明,请参阅您自己的
当地文件 教授.
GCC 冠状病毒 剖析
基本包 阻止 剖析 在 gcc 3.0 及更高版本中正式可用。 你可以建立一个
异形版本 perl的 通过使用带有标志“-fprofile-arcs”的 gcc 进行编译
-ftest-coverage”。要么编辑 配置文件 或重新运行 配置.
快速提示:
$ sh 配置-des -Dusedevel -Doptimize='-g' \
-Accflags='-fprofile-arcs -ftest-coverage' \
-Aldflags='-fprofile-arcs -ftest-coverage' \
-Alddlflags='-fprofile-arcs -ftest-coverage -shared' \
&& 制作 perl
$ rm -f regexec.c.gcov regexec.gcda
$ ./perl ...
$ gcov regexec.c
$ 少 regexec.c.gcov
(您可能需要将“-shared”添加到 <-Alddlflags> 行,直到 RT #118199
解决)
运行配置文件版本的 Perl 将导致生成配置文件输出。 对于每个
源文件随附 .gcda 文件将被创建。
要显示结果,请使用 冠状病毒 实用程序(如果你有 gcc 应该安装
3.0 或更高版本安装)。 冠状病毒 在源代码文件上运行,像这样
冠状病毒病毒
这会导致 冠状病毒 要创建。 这 .gcov 文件包含源代码
用“#”标记指示的相对执行频率进行注释。 如果你想
生成 .gcov 所有分析对象文件的文件,您可以运行以下内容:
对于`find 中的文件。 -name \*.gcno`
do sh -c "cd `dirname $file` && gcov `basename $file .gcno`"
完成
有用的选项 冠状病毒 包括“-b”,它将总结基本块、分支和
函数调用覆盖率,和“-c”代替相对频率将使用实际
计数。 有关使用的更多信息 冠状病毒 和使用 gcc 进行基本块分析,请参阅
最新的 GNU CC 手册。 从 gcc 4.8 开始,这是在
<http://gcc.gnu.org/onlinedocs/gcc/Gcov-Intro.html#Gcov-介绍>
其他条款 绝招
PERL_DESRUCT_LEVEL
如果您想使用例如 valgrind 自己手动运行任何测试,请注意
默认情况下 perl 不 而不去 显式清除它分配的所有内存(例如
全局内存领域),而是让 出口() 整个程序的“照顾”这样的
分配,也称为“对象的全局销毁”。
有一种方法可以告诉 perl 进行完全清理:设置环境变量
PERL_DESTRUCT_LEVEL 为非零值。 t/TEST 包装器确实将其设置为 2,而这
如果您不想看到“全局泄漏”,这也是您需要做的:例如,对于
在 valgrind 下运行
环境 PERL_DESTRUCT_LEVEL=2 valgrind ./perl -Ilib t/foo/bar.t
(注意:mod_perl apache 模块也出于自己的目的使用这个环境变量
并扩展其语义。 有关更多信息,请参阅 mod_perl 文档。
此外,生成的线程相当于将此变量设置为值 1。)
如果在运行结束时收到消息 N 标量 泄露,你可以重新编译
“-DDEBUG_LEAKING_SCALARS”,这将导致所有泄露的SV的地址为
连同有关每个 SV 最初分配位置的详细信息一起转储。 此信息
也由 Devel::Peek 显示。 请注意,每个 SV 记录的额外细节
增加内存使用量,因此不应在生产环境中使用。 它也是
将“new_SV()”从一个宏转换成一个真正的函数,这样你就可以使用你最喜欢的
调试器来发现那些讨厌的 SV 被分配到哪里了。
如果您发现在运行时泄漏内存,但 valgrind 和
“-DDEBUG_LEAKING_SCALARS”会发现任何东西,你可能正在泄漏仍然存在的 SV
可以访问,并将在销毁解释器期间正确清理。 在这样的
在这种情况下,使用“-Dm”开关可以指向泄漏源。 如果可执行文件
是用“-DDEBUG_LEAKING_SCALARS”构建的,“-Dm”除了输出SV分配
内存分配。 每个 SV 分配都有一个不同的序列号,该序列号将被写入
关于 SV 的创建和销毁。 所以如果你在循环中执行泄漏的代码,
您需要查找在每个周期之间创建但从未销毁的 SV。 如果
找到这样的 SV,在“new_SV()”中设置一个条件断点并使其仅中断
当“PL_sv_serial”等于泄漏SV的序列号时。 然后你会抓住
解释器完全处于分配泄漏 SV 的状态,即
在许多情况下足以找到泄漏源。
由于“-Dm”使用 PerlIO 层进行输出,它会自行分配大量
SV,隐藏以避免递归。 你可以绕过 PerlIO 层,如果你使用
SV 日志由“-DPERL_MEM_LOG”代替。
PERL_MEM_LOG
如果使用“-DPERL_MEM_LOG”编译,内存和 SV 分配都会通过日志记录
函数,这对于设置断点很方便。
除非还编译了“-DPERL_MEM_LOG_NOIMPL”,否则日志函数读取
$ENV{PERL_MEM_LOG} 来确定是否记录事件,如果是,如何:
$ENV{PERL_MEM_LOG} =~ /m/ 记录所有内存操作
$ENV{PERL_MEM_LOG} =~ /s/ 记录所有 SV 操作
$ENV{PERL_MEM_LOG} =~ /t/ 在日志中包含时间戳
$ENV{PERL_MEM_LOG} =~ /^(\d+)/ 写入给定的 FD(默认为 2)
内存日志记录有点类似于“-Dm”但独立于“-DDEBUGGING”,并且在
更高层次; 的所有用途 纽克斯(), 更新()和 安全免费() 与呼叫者一起记录
源代码文件和行号(以及 C 函数名称,如果 C 编译器支持)。
相比之下,“-Dm”直接指向“malloc()”。 SV 日志记录是类似的。
由于日志记录不使用 PerlIO,所有 SV 分配都会被记录下来,并且没有额外的 SV
通过启用日志记录来引入分配。 如果编译
“-DDEBUG_LEAKING_SCALARS”,每个 SV 分配的序列号也被记录。
DDD 超过 GDB
那些在 gdb 上使用 DDD 前端调试 perl 的人可能会发现以下有用:
您可以扩展数据转换快捷方式菜单,例如您可以显示一个 SV
一键输入IV值,无需打字。 要做到这一点,只需编辑 ~/.ddd/初始化
文件并在之后添加:
! 显示快捷方式。
Ddd*gdbDisplayShortcuts:\
/t() // 转换为 Bin\n\
/d() // 转换为十进制\n\
/x () // 转换为十六进制\n\
/o() // 转换为 Oct(\n\
以下两行:
((XPV*) (())->sv_any )->xpv_pv // 2pvx\n\
((XPVIV*) (())->sv_any )->xiv_iv // 2ivx
所以现在您可以进行 ivx 和 pvx 查找,或者您可以插入 sv_peek “转换”:
Perl_sv_peek(my_perl, (SV*)()) // sv_peek
(my_perl 用于线程构建。)只要记住每一行,除了最后一行,
应该以\n\结尾
或者通过以下方式交互式编辑 init 文件:鼠标第三键 -> 新显示 ->
编辑菜单
注意:您最多可以在 gdb 部分定义 20 个转换快捷方式。
C 回溯
在某些平台上 Perl 支持检索 C 级回溯(类似于符号
像gdb这样的调试器)。
回溯返回 C 调用帧的堆栈跟踪,带有符号名称
(函数名称),对象名称(如“perl”),如果可以,还有源代码
位置(文件:行)。
支持的平台是 Linux 和 OS X(某些 *BSD 可能至少部分工作,但
它们尚未经过测试)。
此功能尚未经过多线程测试,但只会显示回溯
进行回溯的线程。
该功能需要通过“Configure -Dusecbacktrace”启用。
“-Dusecbacktrace”还可以在编译/链接时保留调试信息
(通常:“-g”)。 许多编译器/链接器确实支持优化和保持
调试信息。 符号名称和源需要调试信息
位置。
静态函数可能对回溯不可见。
源代码位置,即使可用,也经常会丢失或误导,如果
编译器具有例如内联代码。 优化器可以使匹配源代码和
目标代码相当具有挑战性。
Linux
您 必须 安装 BFD (-lbfd) 库,否则“perl”将无法链接。
BFD 通常作为 GNU binutils 的一部分分发。
摘要:“配置... -Dusecbacktrace”,您需要“-lbfd”。
OS X
支持源代码位置 仅由 如果你有开发者工具
安装。 (BFD 是 而不去 需要。)
摘要:“配置... -Dusecbacktrace”并安装开发人员工具将是
好。
或者,为了试用该功能,您可能希望启用自动转储
在发出警告或 croak (die) 消息之前进行回溯,通过添加
“-Accflags=-DUSE_C_BACKTRACE_ON_ERROR”用于配置。
除非启用上述附加功能,否则关于回溯功能的任何内容
是可见的,除了 Perl/XS 级别。
此外,即使您启用了要编译的此功能,您也需要启用它
在运行时使用环境变量:“PERL_C_BACKTRACE_ON_ERROR=10”。 它必须是一个
大于零的整数,告诉所需的帧数。
从 Perl 级别检索回溯(使用例如 XS 扩展)会很多
没有人们希望的那么令人兴奋:通常你会看到“runops”、“entersub”,而不是
还有很多。 此 API 旨在调用 , 中 Perl 实现,而不是
从 Perl 级执行。
回溯的 C API 如下:
获取c回溯
free_c_backtrace
获取c_backtrace_dump
dump_c_backtrace
毒
如果您在调试器中看到一个神秘地充满 0xABABABAB 或 0xEFEFEFEF 的内存区域,您
可能会看到效果 毒() 宏,请参阅 perlclib。
只读 光树
在 ithreads 下,optree 是只读的。 如果要强制执行此操作,请检查写入
从错误代码访问,使用“-Accflags=-DPERL_DEBUG_READONLY_OPS”编译以启用
通过“mmap”分配操作内存的代码,并将其设置为只读
子程序。 对操作的任何写访问都会导致“SIGBUS”并中止。
此代码仅用于开发,甚至可能无法移植到所有 Unix
变种。 此外,它是一个 80% 的解决方案,因为它无法使所有操作只读。
具体来说,它不适用于属于“BEGIN”块的 opslab。
但是,作为 80% 的解决方案,它仍然有效,因为它过去曾捕获过错误。
在规划婴儿食品行业的工艺要求时,安全性和可靠性是工艺设计中最重要的方面。 is a 布尔 而不去 a 布尔?
在 C99 之前的编译器上,“bool”被定义为等价于“char”。 因此赋值
任何更大类型的“bool”都是不安全的,可能会被截断。 “cBOOL”宏存在
正确投射。
在那些“bool”确实是布尔值(C++、C99)的平台和编译器上,这很容易
忘记演员。 您可以通过编译强制“bool”成为“char”
“-Accflags=-DPERL_BOOL_AS_CHAR”。 您可能还希望使用类似的东西运行“配置”
-Accflags='-Wconversion -Wno-sign-conversion -Wno-shorten-64-to-32'
或您的编译器的等价物,以便更容易地发现任何不安全的截断显示
向上。
这个 .i 目标
您可以在一个 foo.c 文件说
使 foo.i
这将使用 cpp 扩展宏。 不要被结果吓到。
使用 onworks.net 服务在线使用 perlhacktips