以下是发行此版本时的一些已知问题以及有关如何解决这些问题的信息。
本节介绍了此发行版中编译器的已知问题、问题和解决方法。
如果同一进程中加载了同一目标文件的不同版本,libxprof 将失败。
如果在同一目录中使用相同名称生成了同一文件的两个不同版本,将其链接到两个不同的共享库,并加载到同一进程(可能不是同时),将发生此情况。
解决方法:使用 -xprofile 生成共享库时,确保目标文件名称具有不同的 UNIX 路径名。请注意,即使基本名称相同,路径名也可能不同。例如,
/work/mylib/unshared/x.o /work/mylib/shared/x.o |
视为不同。
OMP:libxprof:断言失败
如果 malloc() 在调用分析运行时例程期间失败,在低内存情况下可能会发生断言失败。
解决方法:添加内存或交换空间。
-xprofile=tcov:prof_dir 错误地解析了相关 prof_dir
在 —xprofile=tcov:dir 下,会相对于将生成目标文件的目录解析非绝对 UNIX 路径名。
解决方法:在 —xprofile={collect,use,tcov}:dir 下使用绝对路径名。
C++ 标准规定,无后缀的十进制整型常量当作 int 处理(如果值符合 int),否则当作 long int 处理。如果值不符合 long int,结果将不确定。
在 32 位模式中,类型 int 和 long 具有相同的大小和数据范围。C++ 编译器遵循 1990 C 标准规则,将 INT_MAX+1 和 LONG_MAX 之间的值当作无符号的 long 处理。这种处理在某些程序中会产生意外的结果。
1999 C 标准更改了无后缀的十进制整数的规则,这样它们就永远不会被当作无符号的类型来处理。类型为 int、long 或 long long 中第一个可以表示值的类型。
C++ 编译器在标准模式中遵循 C99 规则,但在 -compat=4 模式中继续遵循 C90 规则。(在 -compat=4 模式中,编译器的行为类似于 C++ 4.2 编译器。)
如果您希望大型十进制整数被当作无符号的整数,则简便的解决方法是使用 u 或 U 后缀。同样,可以对其他类型使用其他后缀。例如:
// note: 2147483648 == (INT_MAX+1) 2147483648 // (signed) long long 2147483648LL // (signed) long long 2147483648U // same as 2147483648u |
某些 C++ 语句有可能解释成声明或者表达式语句。C++ 消除歧义规则为:如果一个语句可以处理成声明,那么它就是声明。
早期版本的编译器会错误解释类似于下面的代码:
struct S { S(); }; struct T { T( const S& ); }; T v( S() ); // ??? |
编程人员也许本来打算在最后一行定义变量 v,并且用类型为 S 的临时变量对它进行初始化。早期版本的编译器会这样解释这条语句。
但是在声明环境里,构造符号 "S()" 也可以是抽象声明符(不带标识符),表示“没有返回值类型为 S 的参数的函数”。在这种情况下,该语句会自动转换为函数指针 "S(*)()"。这样该语句仍可作为函数 v 的声明,该函数有一个函数指针类型的参数,返回类型为 T 的值。
当前版本的编译器可以正确地解释该语句,但这未必是编程人员所需要的结果。
可以使用两种方法来修改上述代码以便不产生歧义:
T v1( (S()) ); // v1 is an initialized object T v2( S(*)() ); // v2 is a function |
第一行中另加的圆括号表明它不是 v1 作为函数声明的有效语法,所以它的唯一可能解释是“利用类型为 S 的临时值进行初始化的类型为 T 的目标”。
同样,构造符号 "S(*)()" 不可能是一个值,所以它的唯一可能解释是函数声明。
第一行也可以改写为:
T v1 = S();
虽然这时语句含义非常清楚,但这种形式的初始化有时会创建一个额外的临时变量,而一般情况下不会发生这种情况。
建议不要编写与下面语句类似的代码,因为它的含义不清楚,不同的编译器可能会提供不同的结果。
T v( S() ); // not recommended
下面的模板语法是无效的,但 Sun C++ 编译器 4 和 5.0 版并不报告这个错误。在标准模式(缺省模式)下编译时,C++ 编译器 5.1 版本及以后的所有版本都会报告这个语法错误。
template<class T> class MyClass<T> { ... }; // definition error template<class T> class MyClass<T>; // declaration error |
在这两种情况下,MyClass<T> 中的 <T> 无效,必须删除,如下例所示:
template<class T> class MyClass { ... }; // definition template<class T> class MyClass; // declaration |
模板选项 -instances=static(或 -pto)在与 -xcrossfile 或 -xipo 选项组合时无效。使用该组合的程序会经常发生链接失败。
如果使用 -xcrossfile 或 -xipo 选项,请使用缺省的模板编译模型 -instances=global 进行替代。
通常,不要使用 -instances=static(或 -pto)。它不再有任何优点,此外,C++ 用户指南中还对其缺点进行了说明。
-xlang=f77 命令行选项导致编译进程遇到链接程序错误。要避免该错误,并仍包含相应的运行时库,应改为使用 -xlang=f77,f90 进行编译。
以下情况会导致链接问题。
函数在一个地方声明带有一个 const 参数,而在另一个地方又声明带有一个非 const 参数。
示例:
void foo1(const int); void foo1(int); |
这两个声明是等效的,但编译器会将其重整为两个不同的名称。要避免这个问题,则不应将值参数声明为 const。例如,在任何位置都使用 void foo1(int);,包括该函数定义体。
函数有两个具有相同复合类型的参数,但只有一个参数是用 typedef 声明的。
示例:
class T; typedef T x; // foo2 has composite (that is, pointer or array) // parameter types void foo2(T*, T*); void foo2(T*, x*); void foo2(x*, T*); void foo2(x*, x*); |
所有的 foo2 声明都是等效的,并且应该重整相同的名称。但是,编译器只会重整部分声明的名称。为了避免这个问题,应该统一使用 typedef。
如果您无法统一使用 typedef,解决方法是:在定义该函数的文件中使用弱符号,使得声明与函数的定义一致。例如:
#pragma weak "__1_undefined_name" = "__1_defined_name" |
请注意,某些重整名称依赖于目标体系结构。(例如,在 SPARC V9 体系结构 (-m64) 中,size_t 是 unsigned long,而在其他体系结构中是 signed int。)在这种情况下,会出现两个版本的重整名称,分别对应两个模式。这时必须使用两个 pragma,并用适当的 #if 指令对其进行控制。
在兼容模式 (-compat) 下,C++ 编译器会错误地重整成员函数指针的链接名称。此错误会导致 demangler 以及其他一些调试工具(例如 dbx 和 c++filt)报告该成员函数有多余的前导参数,该参数包括对该成员函数所在类的引用。要更正此问题,请添加 -Qoption ccfe -abiopt=pmfun1 标志。请注意,使用这个标志进行编译的源代码可能会与不使用此标志编译的源代码在二进制上不兼容。在标准模式(缺省模式)下,不会出现该问题。
如果您使用 -instances=extern 编译,则使用模板和静态对象的程序会出现未定义符号的链接时错误。使用缺省设置 -instances=global 则不会出现问题。编译器不支持对模板中的非全局名称空间作用域目标的引用。请看以下示例:
static int k; template<class T> class C { T foo(T t) { ... k ... } }; |
在本示例中,一个模板类的成员引用了静态名称空间作用域的变量。请记住,名称空间作用域包含文件作用域。编译器不支持模板类的成员引用静态名称空间作用域的变量。另外,如果模板在其他的编译单元实例化,那么每个实例都会指向不同的 k,这破坏了 C++ 一次定义规则,代码的行为将会不可预测。
下面的方法也是可行的,但这取决于您如何使用 k,以及它应有的功能。第二个选项仅可供属于类成员的函数模板使用。
可以为变量提供外部链接属性:
int k; // not static |
所有的实例都使用同一个 k。
也可以使这个变量成为类的静态成员:
template<class T> class C { static int k; T foo(T t) { ... k ... } }; |
静态类成员具有外部链接属性。每个 C<T>::foo 的实例都使用不同的 k。而 C<T>::k 的一个实例可以被其他函数共享。此选项可能是您需要的选项。
在名称空间内使用 #pragma align 时,必须使用重整名称。例如,在下面的代码中,#pragma align 语句是无效的。要更正此问题,应将 #pragma align 语句中的 a、b 和 c 替换为其重整名称。
namespace foo { #pragma align 8 (a, b, c) // has no effect //use mangled names: #pragma align 8 (__1cDfooBa_, __1cDfooBb_, __1cDfooBc_) static char a; static char b; static char c; } |
早期 C++ 编译器发行版并不支持 C++ 标准要求的函数重载。当前发行版修正了调用重载函数时出现的许多错误。具体来讲,当函数调用确实出现多义性时,编译器有时会选择某个函数;或者在函数调用实际上未产生多义性时,编译器会发出警告,指出此调用具有多义性。
采用某些解决方法来避免多义性消息已经没有必要了。也许您会看到以前未报告的新多义性错误。
导致函数调用多义性的一个主要原因在于仅重载内置类型的子集。
int f1(short); int f1(float); ... f1(1); // ambiguous, "1" is type int f1(1.0); // ambiguous, "1.0" is type double |
要解决此问题,要么根本不重载 f1,要么重载所有未提交的类型:int、unsigned int、long、unsigned long 和 double。(也许还有 long long、unsigned long long 以及 long double 类型)。
导致多义性的另一个主要原因在于:类中的类型转换函数,尤其是当您也重载了运算符或构造函数时。
class T { public: operator int(); T(int); T operator+(const T&); }; T t; 1 + t // ambiguous |
该操作有多义性是因为它可能被处理成:
T(1) + t // overloaded operator 1 + t.operator int() // built-in int addition |
可以提供重载运算符,也可以提供类型转换函数,但同时提供它们则会产生多义性。
实际上,类型转换函数自身也经常会导致多义性,而且往往在不应该进行转换时进行转换。如果您确实需要类型转换,最好使用命名的函数,而不是类型转换函数。例如,使用 int to_i t();,而不是 operator int();。
更改后,1 + t 操作就不再具有多义性了。它只能解释为 T(1) + t。如果您希望有其他的解释,必须写入 1 + t.to_int()。
在此 f95 编译器发行版中应注意以下问题:
在此发行版中删除过时的 FORTRAN 77 库的操作意味着使用依赖于共享库 libF77、libM77 和 libFposix 的传统 Sun WorkShop f77 编译器编译的旧的可执行文件将不会运行。
使用增强的数组构造函数设置参数常量(类型为指定长度的字符)将导致字符元素的值串联在一起。解决方法是使用数组构造函数中所使用的字符长度(而不是指定长度)来定义参数常量。
使用空白名称指定 C 绑定过程是错误的处理方式,将导致为该过程使用空白名称。解决方法是指定 C 绑定名称,如果不需要,也可以不使用 C 绑定名称。
f95 编译器的早期发行版引入了某些不兼容性,并被此编译器发行版所继承,如果您是从 f95 早期发行版进行更新,应注意这一点。请注意下面的不兼容性:
数组内部函数 ANY、ALL、COUNT、MAXVAL、MINVAL、SUM、PRODUCT、DOT_PRODUCT 和 MATMUL 针对相应 SPARC 平台体系结构进行了高度优化。因此,它们使用全局寄存器 %g2、%g3 和 %g4 作为临时寄存器。
如果调用了上述所列的数组内在函数,则用户代码不应该认为这些寄存器可用于暂时存储。当调用数组内在函数时,这些寄存器中的数据将被覆盖。
调试器 dbx 要求编译中使用的所有目标文件都包含在可执行文件中。通常,无需用户执行额外操作,程序即可满足此要求。但使用含有模块的归档文件时例外。如果程序使用了一个模块,但没有引用模块中的任何过程或变量,则产生的目标文件不会包含对模块中定义的符号的引用。仅当引用目标文件中定义的符号时,链接程序才会链接归档文件中的目标文件。如果不存在此类引用,目标文件将不包括在可执行文件中。当 dbx 尝试查找与使用的模块相关联的调试信息时,将发出警告。对于缺少调试信息的符号,则无法提供有关这些符号的信息。
使用 -u 链接程序选项可以解决这个问题。此选项使用一个符号作为其选项参数。它会将该符号添加到未定义的链接程序符号集中,这就需要解析此符号。与模块关联的链接程序符号通常是模块名称,其所有字母均为小写,后面跟有一条下划线。
例如,为了强制包含模块 MODULE_1 的目标文件被归档文件采用,请指定链接程序选项 -u module_1_。如果使用 f95 命令进行链接,请在命令行上使用 -Qoption ld -umodule_1_。
当 dbx 连接到进程时发生数据收集问题
如果将 dbx 连接到一个正在运行的进程,但是没有预先装入收集器库 libcollector.so,将发生一系列错误。
无法收集任何跟踪数据:同步等待跟踪、堆跟踪或 MPI 跟踪。跟踪数据是通过对各个库执行插入操作而收集的。如果没有预先装入 libcollector.so,将无法执行插入操作。
如果在 dbx 连接到进程后程序安装了一个信号处理程序,并且该信号处理程序不传递 SIGPROF 和 SIGEMT 信号,则分析数据和抽样数据将会丢失。
如果程序使用异步 I/O 库 libaio.so,则基于时钟的分析数据和抽样数据将会丢失,因为 libaio.so 需要使用 SIGPROF 来执行异步取消操作。
如果程序使用硬件计数器库 libcpc.so,则硬件计数器溢出分析实验将会遭到破坏,因为收集器和程序都在使用该库。如果在将 dbx 连接到进程后装入了硬件计数器库,只要通过广义搜索而不是在 libcpc.so 中搜索来解析对 libcpc 库函数的引用,硬件计数器实验即可顺利进行。
如果程序调用 setitimer(2),则由于收集器和程序同时使用定时器,可能会使基于时钟的分析实验中断。
dbx 在调试 Java 代码时可能会崩溃
如果从 dbx shell 内部发出一个 cd 命令,或者设置 CLASSPATH 环境变量或 CLASSPATHX 环境变量,dbx 可能会因分段错误而崩溃。
解决方法:
请勿执行上述任何操作。
在执行上述任何操作前,请删除所有监视(显示)。
dbx 在重新调试 Java 代码时可能会崩溃
在 Java 代码的一行内发出两条 debug 命令可能会导致 dbx 崩溃。
如果调试应用程序与生成应用程序时所用的 J2SE 不同,dbx 会抛出异常。
如果调试应用程序与生成应用程序所用的 J2SE 技术版本不同,dbx 会抛出异常。
由于 RTC 预监视分配而报告伪 RUA 错误
在具有多线程程序的不寻常情况下,当运行时检查 (runtime checking, RTC) 检测到对 RTC 开始监视内存分配之前分配的与线程有关的内部数据的访问时,会报告伪 RUA 错误。由于这些情况是正常线程切换行为的一部分,因此可以使用 dbx suppress 命令放心地忽略这些伪 RUA 报告。
Oracle Solaris Studio 12.2 dbx 有以下限制:
在基于 x86 系统的 Linux 操作系统上,无法使用 dbx 的以下功能:
在基于 x64 系统的 Linux 操作系统上,无法使用 dbx 的以下功能:
Java 调试
调试 32 位程序(除非使用 -x exec32 选项启动 dbx)。
当调用 exec() 时,dbx 无法在 Linux 平台上跟踪派生进程,或对新程序执行更改。
Korn shell 中的管道操作符仅限于 Linux 平台。任何需要访问目标进程的 dbx 命令不能作为管道的一部分使用。例如,下面的命令可能会导致 dbx 挂起:
where | head —1
解决方法:
键入 Ctrl-C 组合键以显示新的 dbx 提示符。
dbx 将缓存大量信息,因此,对于上面的示例,您可以使用下面的命令序列:
where where | head —1 |
在 Linux 平台上调试程序时可能会发生下面的问题:
如果程序使用 clone() 实现自己样式的线程,则 dbx 中提供的线程支持不能正确识别这些线程。
解决方法:
使用 libthread.so 而不是 clone()。
Linux 操作系统中的线程库使用 SIGSTOP 信号作为其内部机制的一部分。通常,dbx 会对您隐藏这些信号,并允许您通过其他源监视真正的 SIGSTOP 信号。偶尔,Linux 操作系统会以意想不到的方式使用 SIGSTOP,dbx 将系统生成的 SIGSTOP 解释为用户生成的 SIGSTOP。
解决方法:
使用 ignore 命令告知 dbx 不要捕获 SIGSTOP 信号。
有时线程退出,但 Linux 操作系统并不将退出行为报告给 dbx。当使用新的线程库 (NPTL) 时,这种情况发生的次数会减少。
当线程退出,但是未报告此退出操作时,dbx 会等待永远不发生的事件并且不显示新的提示符。这种情况最可能发生在您在 dbx 中提供了 cont 命令之后,但它也可能发生在 step up 命令、step 命令、next 命令和其他命令之后。
解决方法:
有时,键入 Ctrl+C 组合键会导致 dbx 停止等待并显示新的提示符。
如果 Ctrl+C 组合键不起作用,请退出 dbx 并重新启动。
C++ 表达式的运行时类型信息不适用于 g++ 编译器编译的程序。
不能从 .dbxrc 文件连接至正在运行的进程。.dbxrc 文件不应含有执行代码的命令。但是,您可以将此类命令放在一个文件中,然后使用 dbx source 命令来执行该文件中的命令。
dbx 无法正确地还原指向 compat=4 的成员函数的指针。如果使用 compat=5,则不会出现此问题。
解决方法:按如下方法重新编译程序:
CC —compat=4 —Qoption ccfe —abiopt=pmfun1
该标志引入了 ABI 更改并且不应该在产品生成中使用。
在 SPARC V9 (-m64) 系统中,使用 call 命令或输出函数调用对作为参数或返回值的嵌套小结构不起作用。
使用 libC.so.5 或者 libC.so.4 的旧副本可能会在 C++ 异常区域中引起 dbx 问题。可能会出现关于错误的 stab 和未处理的异常等警告消息。
解决方法:在所有系统上安装最新的 libC.so.5。
Fortran 用户应该用 -stackvar 选项进行编译,以便充分利用运行时检查。
某些程序可能无法正常使用 -stackvar。在这种情况下,请尝试使用 -C 编译器选项,它将在不使用运行时检查的情况下启用数组下标检查。
对于多线程的应用程序,跟踪派生可能不可靠。
使用调用命令或输出函数调用可能会导致多线程应用程序发生死锁。
如果文件是预编译的头文件 (Pre-Compiled Header, PCH) 集合的一部分,请不要使用 dbx 的修复并继续功能来更改头文件。
dbx 命令行解释器是旧版本的 Korn shell (ksh),不支持代码集独立 (Code Set Independence, CSI)。当在 dbx 命令行上键入多字节字符时,会发生解释错误。
如果要在 Linux 上使用 Oracle Message Passing Toolkit 8.2 或 8.2.1,可能需要解决方法。版本 8.1 或 8.2.1c 不需要解决方法,或者如果要使用 Oracle Solaris Studio 编译器,则任何版本都不需要解决方法。
Oracle Message Passing Toolkit 版本号由安装路径指定,例如 /opt/SUNWhpc/HPC8.2.1,或者,您可以按照如下所示键入 mpirun —V 查看输出,其中版本以斜体表示:
mpirun (Open MPI) 1.3.4r22104-ct8.2.1-b09d-r70 |
如果您的应用程序是使用 GNU 或 Intel 编译器编译的,并且要对 MPI 使用 Oracle Message Passing Toolkit 8.2 或 8.2.1,则要获取 MPI 状态数据,必须使用 -WI 和 --enable-new-dtags 选项和 Oracle Message Passing Toolkit link 命令。这些选项将使可执行文件定义 RPATH 及 RUNPATH,从而可使用 LD_LIBRARY_PATH 环境变量启用 MPI 状态库。
本节介绍了已知的 dmake 软件问题及可能的解决方法。
如果在分布式模式下使用 dmake 出现任何问题,请验证以下内容:
$HOME 环境变量应设置为可访问的目录。
% ls -la $HOME
文件 $HOME/.dmakerc 存在且可读,并包含正确的信息。
% cat $HOME/.dmakerc
通过使用 /usr/sbin/ping 命令检查每台主机,确保 $HOME/.dmakerc 文件中提及的所有主机均处于活动状态。
% /usr/sbin/ping $HOST
其中,$HOST 是系统的名称,它作为主机列于 $HOME/.dmakerc 文件中。
通过使用 dmake、rxm 和 rxs 命令,验证 dmake 二进制文件的路径是否正确。
% which dmake % which rxm % which rxs |
远程登录 (rsh) 每一台主机时不需要输入密码,并且每次远程登录所花费的时间处于可接受的范围(小于 2 秒钟)。
% time rsh $HOST uname -a
文件 /etc/opt/SPROdmake/dmake.conf 在每台主机中存在并包含正确的信息。如果此文件不存在,dmake 将仅在此系统上分发一个作业:
% rsh $HOST cat /etc/opt/SPROdmake/dmake.conf
对于每台主机,dmake 二进制文件的路径是正确的:
% rsh $HOST `which dmake` % rsh $HOST `which rxm` % rsh $HOST `which rxs` |
可从每台主机获取生成区域 (rwx):
% cd $BUILD % rm $HOST.check.tmp % echo "Build area is available from host $HOST" > $HOST.check.tmp % rsh $HOST cat $BUILD/$HOST.check.tmp |
其中,$BUILD 是生成区域的完整路径。
可从每台主机获取 $HOME:
% cd $HOME % rm $HOST.check.tmp % echo "HOME is available from host $HOST" > $HOST.check.tmp % rsh $HOST cat $HOME/$HOST.check.tmp |
您可以将任何计算机作为生成服务器,只要其符合以下要求:
对于 dmake 主机(您即将在其中启动生成过程的计算机),您必须在系统不提示输入密码的情况下,能够使用 rsh 在生成服务器上远程执行命令。
必须能够从生成服务器访问安装了 dmake 软件的 bin 目录。缺省情况下,dmake 会假设生成服务器上 dmake 可执行文件的逻辑路径与 dmake 主机上的路径相同。您可以通过在运行时配置文件中将路径名称指定为主机条目的属性来覆盖此假设。
文件 /etc/opt/SPROdmake/dmake.conf 位于主机上且可读,其中包含正确的信息。如果此文件不存在,dmake 将仅在此系统上分发一个作业。
使用 –extract-installation-data 选项运行非 GUI 安装程序可能会失败,并且不显示用户可读的错误消息。