Oracle Solaris Studio 12.2 发行版的新增功能

第 8 章 此发行版中的已知问题、限制和解决方法

以下是发行此版本时的一些已知问题以及有关如何解决这些问题的信息。

编译器

本节介绍了此发行版中编译器的已知问题、问题和解决方法。

编译器共有的问题

与 —xprofile 相关的已知问题

C++

更正大型十进制整型常量的解释

C++ 标准规定,无后缀的十进制整型常量当作 int 处理(如果值符合 int),否则当作 long int 处理。如果值不符合 long int,结果将不确定。

在 32 位模式中,类型 intlong 具有相同的大小和数据范围。C++ 编译器遵循 1990 C 标准规则,将 INT_MAX+1 和 LONG_MAX 之间的值当作无符号的 long 处理。这种处理在某些程序中会产生意外的结果。

1999 C 标准更改了无后缀的十进制整数的规则,这样它们就永远不会被当作无符号的类型来处理。类型为 intlonglong long 中第一个可以表示值的类型。

C++ 编译器在标准模式中遵循 C99 规则,但在 -compat=4 模式中继续遵循 C90 规则。(在 -compat=4 模式中,编译器的行为类似于 C++ 4.2 编译器。)

如果您希望大型十进制整数被当作无符号的整数,则简便的解决方法是使用 uU 后缀。同样,可以对其他类型使用其他后缀。例如:


              // 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

如果将 -xipo 或 -xcrossfile 与 -instances=static 组合,链接会失败

模板选项 -instances=static(或 -pto)在与 -xcrossfile-xipo 选项组合时无效。使用该组合的程序会经常发生链接失败。

如果使用 -xcrossfile-xipo 选项,请使用缺省的模板编译模型 -instances=global 进行替代。

通常,不要使用 -instances=static(或 -pto)。它不再有任何优点,此外,C++ 用户指南中还对其缺点进行了说明。

跨语言链接错误

-xlang=f77 命令行选项导致编译进程遇到链接程序错误。要避免该错误,并仍包含相应的运行时库,应改为使用 -xlang=f77,f90 进行编译。

名称重整链接问题

以下情况会导致链接问题。

调试工具错误地报告成员函数有多余的前导参数

在兼容模式 (-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,以及它应有的功能。第二个选项仅可供属于类成员的函数模板使用。

  1. 可以为变量提供外部链接属性:


                  int k; // not static 

    所有的实例都使用同一个 k

  2. 也可以使这个变量成为类的静态成员:


                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 语句是无效的。要更正此问题,应将 #pragma align 语句中的 a、bc 替换为其重整名称。


        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()

Fortran

在此 f95 编译器发行版中应注意以下问题:

f95 编译器的早期发行版引入了某些不兼容性,并被此编译器发行版所继承,如果您是从 f95 早期发行版进行更新,应注意这一点。请注意下面的不兼容性:

数组内部函数使用全局寄存器:

数组内部函数 ANY、ALL、COUNT、MAXVAL、MINVAL、SUM、PRODUCTDOT_PRODUCTMATMUL 针对相应 SPARC 平台体系结构进行了高度优化。因此,它们使用全局寄存器 %g2、%g3%g4 作为临时寄存器。

如果调用了上述所列的数组内在函数,则用户代码不应该认为这些寄存器可用于暂时存储。当调用数组内在函数时,这些寄存器中的数据将被覆盖。

归档库中的 f95 模块不包括在可执行文件中:

调试器 dbx 要求编译中使用的所有目标文件都包含在可执行文件中。通常,无需用户执行额外操作,程序即可满足此要求。但使用含有模块的归档文件时例外。如果程序使用了一个模块,但没有引用模块中的任何过程或变量,则产生的目标文件不会包含对模块中定义的符号的引用。仅当引用目标文件中定义的符号时,链接程序才会链接归档文件中的目标文件。如果不存在此类引用,目标文件将不包括在可执行文件中。当 dbx 尝试查找与使用的模块相关联的调试信息时,将发出警告。对于缺少调试信息的符号,则无法提供有关这些符号的信息。

使用 -u 链接程序选项可以解决这个问题。此选项使用一个符号作为其选项参数。它会将该符号添加到未定义的链接程序符号集中,这就需要解析此符号。与模块关联的链接程序符号通常是模块名称,其所有字母均为小写,后面跟有一条下划线。

例如,为了强制包含模块 MODULE_1 的目标文件被归档文件采用,请指定链接程序选项 -u module_1_。如果使用 f95 命令进行链接,请在命令行上使用 -Qoption ld -umodule_1_

工具

dbx

已知的 dbx 问题和解决方法

  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),则由于收集器和程序同时使用定时器,可能会使基于时钟的分析实验中断。

  2. dbx 在调试 Java 代码时可能会崩溃

    如果从 dbx shell 内部发出一个 cd 命令,或者设置 CLASSPATH 环境变量或 CLASSPATHX 环境变量,dbx 可能会因分段错误而崩溃。

    解决方法:

    • 请勿执行上述任何操作。

    • 在执行上述任何操作前,请删除所有监视(显示)。

  3. dbx 在重新调试 Java 代码时可能会崩溃

    在 Java 代码的一行内发出两条 debug 命令可能会导致 dbx 崩溃。

  4. 如果调试应用程序与生成应用程序时所用的 J2SE 不同,dbx 会抛出异常。

    如果调试应用程序与生成应用程序所用的 J2SE 技术版本不同,dbx 会抛出异常。

  5. 由于 RTC 预监视分配而报告伪 RUA 错误

    在具有多线程程序的不寻常情况下,当运行时检查 (runtime checking, RTC) 检测到对 RTC 开始监视内存分配之前分配的与线程有关的内部数据的访问时,会报告伪 RUA 错误。由于这些情况是正常线程切换行为的一部分,因此可以使用 dbx suppress 命令放心地忽略这些伪 RUA 报告。

dbx 限制和不兼容情况

Oracle Solaris Studio 12.2 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 命令。这些选项将使可执行文件定义 RPATHRUNPATH,从而可使用 LD_LIBRARY_PATH 环境变量启用 MPI 状态库。

dmake

本节介绍了已知的 dmake 软件问题及可能的解决方法。

如果在分布式模式下使用 dmake 出现任何问题,请验证以下内容:

  1. $HOME 环境变量应设置为可访问的目录。

    % ls -la $HOME

  2. 文件 $HOME/.dmakerc 存在且可读,并包含正确的信息。

    % cat $HOME/.dmakerc

  3. 通过使用 /usr/sbin/ping 命令检查每台主机,确保 $HOME/.dmakerc 文件中提及的所有主机均处于活动状态。

    % /usr/sbin/ping $HOST

    其中,$HOST 是系统的名称,它作为主机列于 $HOME/.dmakerc 文件中。

  4. 通过使用 dmakerxmrxs 命令,验证 dmake 二进制文件的路径是否正确。


           % which dmake
           % which rxm
           % which rxs   
    
  5. 远程登录 (rsh) 每一台主机时不需要输入密码,并且每次远程登录所花费的时间处于可接受的范围(小于 2 秒钟)。

    % time rsh $HOST uname -a

  6. 文件 /etc/opt/SPROdmake/dmake.conf 在每台主机中存在并包含正确的信息。如果此文件不存在,dmake 将仅在此系统上分发一个作业:

    % rsh $HOST cat /etc/opt/SPROdmake/dmake.conf

  7. 对于每台主机,dmake 二进制文件的路径是正确的:


           % rsh $HOST `which dmake`
           % rsh $HOST `which rxm`
           % rsh $HOST `which rxs`    
    
  8. 可从每台主机获取生成区域 (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 是生成区域的完整路径。

  9. 可从每台主机获取 $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 限制

您可以将任何计算机作为生成服务器,只要其符合以下要求:

安装

使用 –extract-installation-data 选项运行非 GUI 安装程序可能会失败,并且不显示用户可读的错误消息。