POOMA banner

POOMA Tutorial 13
Compiling, Running, and Debugging
POOMA Programs

Contents:
    Introduction
    Compiling
        Compiler Requirements
        Installing and Configuring POOMA
        How to Decipher Compiler Error Messages
    Running
        POOMA Runtime Arguments
        Object-Based Initialization
    Debugging

Introduction

This tutorial includes information on configuring and building the POOMA library, and application programs using POOMA. It also discusses some topics about running POOMA programs, and gives some anecdotal information about debuggers and debugging.

Compiling

Compiler Requirements

POOMA has been extensively tested with the following C++ compilers:

Other compilers based on version 2.38 or later of the Edison Design Group (EDG) front-end may also be able to compile POOMA. However, we know that CFront-based compilers, Visual C++ 5.0/6.0, and GNU C++ 2.91 do not support the ISO features necessary to compile POOMA. These features include:

In addition, POOMA assumes a fairly standard C++ library including:

Ideally, the library should support ISO standard ".h-less" headers like <vector> that place definitions in the std:: namespace.

The compiler features listed above are an absolute requirement. For example, if your compiler does not support member templates, there is no amount of work that will get POOMA to compile. If your compiler has limited support for these features, it is worth trying to build the library, but there are no assurances of success. Minor deficiencies in the libraries can also be worked around. In fact, we have had to use a mixture of .h-less and .h headers in order to work around various compiler/library problems:

POOMA-based programs can use either .h or non .h headers for everything else in the standard C++ library, but should of course be consistent.

Installing and Configuring POOMA

The POOMA build system can handle several different configurations at once; this allows developers to building libpooma.a and POOMA applications for several configurations at the same time. Each configuration is referred to as a suite, and is described by a suite file. This is the main file that the configuration script described below sets up when it runs.

Note: this configuration script is presently available only for Unix platforms. Programmers who are installing POOMA on the Apple Macintosh, or under Microsoft Windows will need to change #define definitions manually. Under CodeWarrior, on both Macintosh and Windows, these definitions are in the file:

pooma-2.2.0/src/arch/Metrowerks/Pooma.prefix.h

If the Intel VTune C++ compiler is being used with Microsoft Visual C++ on Windows, the definitions that need to be changed are in:

pooma-2.2.0/src/arch/Intel/PoomaConfigurations.h

In addition, when the configuration script is run to set up a new suite, it will go through all the subdirectories and create a directory called <suite> in each subdirectory that contains files that will be compiled (where <suite> is the name of the suite). As described below, the environment variable POOMASUITE must be set to the value of the suite to compile after the configuration script is run.

The POOMA configuration script is located in the top level of the POOMA distribution tree, and is called configure. It is written in Perl, and does the following;

The most useful arguments for configure are:

--arch <arch>
Specify an architecture to configure for. The directory config/arch has several files with .conf extensions, one for each combination of machine and compiler that the current version of POOMA supports. The recommended procedure is to select an architecture file, edit it if necessary, and then run configure --arch <arch> (plus any other options) from the top directory of the POOMA distribution. If the POOMASUITE environment variable is set, and you do not use the --arch flag, the value of POOMASUITE will be used instead. One or the other of these methods must be used to specify the desired suite.
--suite <suite>
Specify the name of the suite file and lib/<suite> build directory to create. This can be different than <arch>, but if you do not give the --suite option, <arch> will be used. If the --suite flag is not given then:
--prefix <installdir>
This selects where to install POOMA after you have built the library. The INSTALL file (located in the root directory of the POOMA distribution, along with the LICENSE file) describes the directory tree that gets created during installation.
--opt or --debug
Select whether to build optimized or debug library by default.
--preinst or --nopreinst
If --preinst is used, the library will pre-instantiate versions of several classes for several types and dimensions. This step is not necessary; the library will build more quickly if you do not use it, but applications may build more quickly.
--ex or --noex
Enable or disable exception handling. Some compilers produce more efficient code and compile faster when exceptions are turned off.
--parallel or --serial
These flags determine whether POOMA will use SMARTS or not. If --parallel is given, the SMARTS header files will be included and parallel evaluation will be done. Otherwise, all operations will run in serial.
SMARTS is the thread and dataflow package that POOMA uses for the multithreaded operation. It was also developed at the Advanced Computing Laboratory, and is available on the same CD-ROM as POOMA. This release of SMARTS only runs on Unix platforms; in order to use SMARTS with POOMA, you must:
--v
This flag causes compilers and linkers to print very verbose output.

The mode table in an earlier tutorial summarizes the modes produced by different combinations of configuration flags.

The configure scripts also has options that will add extra -I, -D, -L and other flags to your compilations. Run configure -h to see a complete list of options.

After running configure and creating a suite file, set the environment variable POOMASUITE to the name of the suite, and run make. There is a makefile at the top level of the POOMA distribution, and in the lib/<suite> directory. When make is finished, there should be a lib/<suite>/libpooma.a library file.

This file can be used in one of two ways. Programmers who are extending POOMA can use libpooma.a without any other work, since the library may need to be recompiled often. Such developers can build the programs in the benchmarks and examples directories (such as examples/Doof2d) by running make in those directories.

Programmers who are just using the library will have to install it. Typing make install will install libpooma.a and the necessary source files in the <installdir> directory (specified with the --prefix flag to configure). Users should then set the environment variable POOMADIR to <installdir>, and POOMAARCH to a string indicating the type of build architecture. This is not the same as the <arch> name used for configure, but is instead just a string indicating the type of machine, and for this release may be one of sgi64, sgin32, sgi32, or linux.

After doing all of this, users can go into any subdirectory under examples or benchmarks and run:

make -f Makefile.user

Makefile.user first includes a "stub" makefile from the directory where libpooma.a is installed. This stub makefile contains settings such as POOMA_INCLUDES that are needed to build POOMA applications. For example, the Makefile.user file in examples/Doof2d contains the following:

### include the POOMA makefile stub, to get compiler flags and libraries
include $(POOMADIR)/$(POOMAARCH)/lib/Makefile.pooma

### the name of the example code to compile
EXAMPLE = Doof2d

### the main target for this makefile
$(EXAMPLE): $(EXAMPLE).cpp
        $(POOMA_CXX) $(POOMA_CXX_DBG_ARGS) -o $(EXAMPLE) $(EXAMPLE).cpp
$(POOMA_INCLUDES) $(POOMA_DEFINES) $(POOMA_LIBS) 

How to Decipher Compiler Error Messages

POOMA makes extensive use of templates to achieve high performance. Unfortunately, this means that a simple mistake often results in dozens of compiler error messages that are both long and obscure. These messages are often tough for experienced C++ programmers to interpret and can be downright scary for newcomers to the language. There is no simple formula for dealing with these messages, but there are strategies that can reduce the pain associated with the process.

To begin with, consider the program below:

01  #include "Pooma/Arrays.h"
02
03  int main(int argc, char *argv[])
04  {
05      Pooma::initialize();
06
07      int p, *pp = &p;    
08      Array<1> z(6);
09      for (p = 0; p < 6; p++)
10          z(PP) = p;
11
12      Pooma::finalize();
13
14      return 0;
15  }

KCC 3.3 reports the following impressive set of error messages (please be patient while this scrolls past you):

"src/Array/Array.h", line 416: error: name followed by "::" must be a class or
          namespace name
        CTAssert(SDomain_t::dimensions == dimensions);
        ^
          detected during instantiation of "ArrayViewReturn<ConstArray<Dim, T,
                    Engine>::Engine_t, TemporaryNewDomain1<ConstArray<Dim, T,
                    Engine>::Domain_t, Sub1>::SliceType_t>::Type_t Array<Dim,
                    T, EngineTag>::operator()(const Sub1 &) const [with Dim=1,
                    T=double, EngineTag=Brick, Sub1=int *]" at line 10 of
                    "test.cpp"

"src/Array/Array.h", line 416: error: class "PoomaCTAssert<<error-constant>>"
          has no member "test"
        CTAssert(SDomain_t::dimensions == dimensions);
        ^
          detected during instantiation of "ArrayViewReturn<ConstArray<Dim, T,
                    Engine>::Engine_t, TemporaryNewDomain1<ConstArray<Dim, T,
                    Engine>::Domain_t, Sub1>::SliceType_t>::Type_t Array<Dim,
                    T, EngineTag>::operator()(const Sub1 &) const [with Dim=1,
                    T=double, EngineTag=Brick, Sub1=int *]" at line 10 of
                    "test.cpp"

"src/Domain/DomainTraits.Loc.h", line 190: error: a value of type
          "DomainTraitsScalar<int *, int *>::Element_t" cannot be assigned to
          an entity of type "DomainTraits<Loc<1>>::Storage_t"
      dom = DomainTraits<T>::getFirst(newdom);
          ^
          detected during:
            instantiation of "void
                      DomainTraits<Loc<1>>::setDomain(DomainTraits<Loc<1>>::Sto
                      rage_t &, const T &) [with T=int *]" at line 286 of
                      "src/Domain/Domain.h"
            instantiation of "void SetDomainFunctor<DT, ST, T, UT,
                      wildcard>::setDomain(ST &, const T &) [with
                      DT=DomainTraits<Loc<1>>,
                      ST=DomainBase<DomainTraits<Loc<1>>>::Storage_t,
                      T=DomainTraitsScalar<int *, int *>::PointDomain_t,
                      UT=DomainTraitsScalar<int *, int *>::PointDomain_t,
                      wildcard=false]" at line 395 of "src/Domain/Domain.h"
            instantiation of "void Domain<1, DT>::setDomain(const T &) [with
                      DT=DomainTraits<Loc<1>>, T=DomainTraitsScalar<int *, int
                      *>::PointDomain_t]" at line 234 of "src/Domain/Loc.h"
            instantiation of "void CopyLocStorageImpl<Dim, T, 1,
                      false>::copy(Loc<Dim> &, const T &) [with Dim=1, T=int
                      *]" at line 242 of "src/Domain/Loc.h"
            instantiation of "void CopyLocStorage<Dim, T>::copy(Loc<Dim> &,
                      const T &) [with Dim=1, T=int *]" at line 410 of
                      "src/Domain/Loc.h"
            instantiation of "Loc<1>::Loc(const T1 &) [with T1=int *]" at line
                      78 of "src/Array/Array.h"
            instantiation of "ArrayViewReturn2<Engine, Domain, 1>::Type_t
                      ArrayViewReturn2<Engine, Domain, 1>::eval(const Engine
                      &, const Domain &) [with Engine=Engine<1, double,
                      Brick>, Domain=int *]" at line 89 of "src/Array/Array.h"
            instantiation of "ArrayViewReturn<Engine, Domain>::Type_t
                      ArrayViewReturn<Engine, Domain>::eval(const Engine &,
                      const Domain &) [with Engine=Engine<1, double, Brick>,
                      Domain=int *]" at line 418 of "src/Array/Array.h"
            instantiation of "ArrayViewReturn<ConstArray<Dim, T,
                      Engine>::Engine_t, TemporaryNewDomain1<ConstArray<Dim,
                      T, Engine>::Domain_t, Sub1>::SliceType_t>::Type_t
                      Array<Dim, T, EngineTag>::operator()(const Sub1 &) const
                      [with Dim=1, T=double, EngineTag=Brick, Sub1=int *]" at
                      line 10 of
                      "test.cpp"


"src/Domain/NewDomain.h", line 753: error: name followed by "::" must be a
          class or namespace name
        SliceType_t retval = AllDomain<SliceType_t::dimensions>();
                                       ^
          detected during:
            instantiation of "NewDomain1<T1>::SliceType_t
                      NewDomain1<T1>::combineSlice(const UT &, const T1 &)
                      [with T1=int *, UT=Interval<1>]" at line 128 of
                      "src/Array/ConstArray.h"
            instantiation of "TemporaryNewDomain1<Domain, Sub>::SliceType_t
                      TemporaryNewDomain1<Domain, Sub>::combineSlice(const
                      Domain &, const Sub &) [with Domain=Interval<1>, Sub=int
                      *]" at line 419 of "src/Array/Array.h"
            instantiation of "ArrayViewReturn<ConstArray<Dim, T,
                      Engine>::Engine_t, TemporaryNewDomain1<ConstArray<Dim,
                      T, Engine>::Domain_t, Sub1>::SliceType_t>::Type_t
                      Array<Dim, T, EngineTag>::operator()(const Sub1 &) const
                      [with Dim=1, T=double, EngineTag=Brick, Sub1=int *]" at
                      line 10 of
                      "test.cpp"

"src/Domain/AllDomain.h", line 84: error: class
          "PoomaCTAssert<<error-constant>>" has no member "test"
      CTAssert(Dim > 0);
      ^
          detected during:
            instantiation of
                      "AllDomain<Dim>::AllDomain() [with Dim=<error-constant>]"
                      at line 753 of "src/Domain/NewDomain.h"
            instantiation of "NewDomain1<T1>::SliceType_t
                      NewDomain1<T1>::combineSlice(const UT &, const T1 &)
                      [with T1=int *, UT=Interval<1>]" at line 128 of
                      "src/Array/ConstArray.h"
            instantiation of "TemporaryNewDomain1<Domain, Sub>::SliceType_t
                      TemporaryNewDomain1<Domain, Sub>::combineSlice(const
                      Domain &, const Sub &) [with Domain=Interval<1>, Sub=int
                      *]" at line 419 of "src/Array/Array.h"
            instantiation of "ArrayViewReturn<ConstArray<Dim, T,
                      Engine>::Engine_t, TemporaryNewDomain1<ConstArray<Dim,
                      T, Engine>::Domain_t, Sub1>::SliceType_t>::Type_t
                      Array<Dim, T, EngineTag>::operator()(const Sub1 &) const
                      [with Dim=1, T=double, EngineTag=Brick, Sub1=int *]" at
                      line 10 of
                      "test.cpp"

"src/Domain/NewDomain.h", line 753: error: no suitable conversion function
          from "AllDomain<<error-constant>>" to
          "NewDomain1<int *>::SliceType_t" exists
        SliceType_t retval = AllDomain<SliceType_t::dimensions>();
                             ^
          detected during:
            instantiation of "NewDomain1<T1>::SliceType_t
                      NewDomain1<T1>::combineSlice(const UT &, const T1 &)
                      [with T1=int *, UT=Interval<1>]" at line 128 of
                      "src/Array/ConstArray.h"
            instantiation of "TemporaryNewDomain1<Domain, Sub>::SliceType_t
                      TemporaryNewDomain1<Domain, Sub>::combineSlice(const
                      Domain &, const Sub &) [with Domain=Interval<1>, Sub=int
                      *]" at line 419 of "src/Array/Array.h"
            instantiation of "ArrayViewReturn<ConstArray<Dim, T,
                      Engine>::Engine_t, TemporaryNewDomain1<ConstArray<Dim,
                      T, Engine>::Domain_t, Sub1>::SliceType_t>::Type_t
                      Array<Dim, T, EngineTag>::operator()(const Sub1 &) const
                      [with Dim=1, T=double, EngineTag=Brick, Sub1=int *]" at
                      line 10 of
                      "test.cpp"

"src/Domain/NewDomain.h", line 131: error: expression must have class type
        DomainTraits<RT>::getDomain(rt, DS + i).setDomain(
        ^
          detected during:
            instantiation of "void CombineSliceDomainWC<RT, UT, CT, DS,
                      SliceDS, incl, wc>::combine(RT &, const UT &, const CT
                      &) [with RT=NewDomain1<int *>::SliceType_t,
                      UT=Interval<1>, CT=int *, DS=0, SliceDS=0, incl=false,
                      wc=false]" at line 207
            instantiation of "void CombineSliceDomain<RT, UT, CT, DS, SliceDS,
                      incl>::combine(RT &, const UT &, const CT &) [with
                      RT=NewDomain1<int *>::SliceType_t, UT=Interval<1>,
                      CT=int *, DS=0, SliceDS=0, incl=false]" at line 766
            instantiation of "RT NewDomain1<T1>::fillSlice(RT &, const UT &,
                      const T1 &) [with T1=int *, RT=NewDomain1<int
                      *>::SliceType_t, UT=Interval<1>]" at line 754
            instantiation of "NewDomain1<T1>::SliceType_t
                      NewDomain1<T1>::combineSlice(const UT &, const T1 &)
                      [with T1=int *, UT=Interval<1>]" at line 128 of
                      "src/Array/ConstArray.h"
            instantiation of "TemporaryNewDomain1<Domain, Sub>::SliceType_t
                      TemporaryNewDomain1<Domain, Sub>::combineSlice(const
                      Domain &, const Sub &) [with Domain=Interval<1>, Sub=int
                      *]" at line 419 of "src/Array/Array.h"
            instantiation of "ArrayViewReturn<ConstArray<Dim, T,
                      Engine>::Engine_t, TemporaryNewDomain1<ConstArray<Dim,
                      T, Engine>::Domain_t, Sub1>::SliceType_t>::Type_t
                      Array<Dim, T, EngineTag>::operator()(const Sub1 &) const
                      [with Dim=1, T=double, EngineTag=Brick, Sub1=int *]" at
                      line 10 of
                      "test.cpp"

The first thing to keep in mind is that the error messages are telling you exactly what went wrong in your program. However, like a patient speaking to a doctor, the compiler is reporting symptoms: "It hurts, here, here, and here." It isn't saying directly, "You have accidentally used an int* to index array z."

Second, start at the first message and work down. As you can see, C++ compilers will often report several different error messages for the same mistake. The first one is usually the most direct statement of what's wrong so start there. In our example, KCC is reporting an error at line 416 of the POOMA header Array/Array.h. This line reads:

CTAssert(SDomain_t::dimensions == dimensions);

It is specifically complaining about the fact that it doesn't think SDomain_t is a class or namespace name, which means that qualifying it with '::' doesn't make sense. This is a symptom, but it isn't very useful, especially to someone who isn't familiar with the innards of POOMA. It may be disconcerting that the error message is in POOMA code. However, it is simply the reality with templates that bad user code can result in a template error deep inside POOMA.

Third, the real information is in the instantiation chain. By "instantiation chain", we mean the set of templates, starting with your code, that the C++ compiler was instantiating when it ran into trouble. In KCC errors, the instantiation chain can be recognized by a series of lines beginning with "detected during: instantiation of". The best way to read these chains is from the instantiation closest to user code to that deepest in POOMA. In the first error message, this is easy because there is only one instantiation listed. KCC claims it trying to instantiate:

Array<Dim, T, Engine>::operator()(const Sub &)

where: Dim is 1, T is double, EngineTag is Brick, and Sub1 is int*, at line 10 of our example program. Now, this is useful because we see that the problem is with the line:

z(PP) = p;

There is indeed a call to operator() call on that line, i.e. z(PP). Moreover, KCC is telling us that the argument we passed in has a type int*, which is not a legal domain type. If we change z(PP) to z(p), the problem is solved.

The other error messages give essentially the same information in different ways. More complicated situations may require following the instantiation chain through several levels. The most important thing is not to get blinded by the quantity of output.

The error messages produced by EGCS are formatted differently, but the procedure for interpreting them is the same. Unfortunately, CodeWarrior Professional 4 does not print out an instantiation chain, which makes diagnosing template problems very difficult. Metrowerks knows about this problem and is fixing it. However, until then, we can only suggest compiling your code with EGCS or KCC as a means to diagnose difficult problems.

Running

POOMA Runtime Arguments

The following run-time flags can be used to control various aspects of the behavior of a POOMA-based application:

The set of flags shown below control log messages, warnings, debugging output, and so on. All of these options have a -no form as well, such as --pooma-noinfo.

The first four of the flags above are related to a set of macros defined in the src/Pooma/Pooma.h header file. The first of these, POOMA_PRINT(stream,text), prints a message to a given stream in a thread-safe manner. The second, POOMA_PRINTDEBUG(level,text), prints a message to the POOMA debug output stream POOMA::pdebug if the POOMA_PRINTDEBUG option was selected when POOMA was built. The last three macros are POOMA_INFO(text), POOMA_WARN(text), and POOMA_ERROR(text), which print messages to the information, warning, and error output streams (Pooma::pinfo, Pooma::pwarn, and Pooma::perr respectively). These four streams are actually predefined Inform objects (described in the tutorial on Text I/O).

There are also flags that globally affect certain POOMA classes:

Finally, these three flags are used by POOMA's SMARTS threading package, and are not fully implemented in this release:

Object-Based Initialization

As mentioned in the first tutorial, POOMA can be initialized by passing argc and argv to Pooma::initialize(), or by creating an instance of Pooma::Options, configuring it, and then passing that options object to Pooma::initialize(). Thus, instead of using:

Pooma::initialize(argc, argv);
a program can do the following:
Pooma::Options opts;                    // create the options object
opts.concurrency(8);                    // tell POOMA to use 8 threads
opts.logfile("pooma.log");              // turn on output logging
Pooma::initialize(opts);                // initialize Pooma

These two methods can be combined, which allows a program to override any options the user might have specified:

Pooma::Options opts(argc, argv);        // parse command line
opts.concurrency(8);                    // but always use 8 threads
Pooma::initialize(opts);                // actual initialization

For more information on the configuration options available to POOMA programs, please see the POOMA documentation.

Debugging

Debugging the templated classes and functions in POOMA codes is challenging. Many debuggers have difficulty with finding and stopping in the particular template instance you're interested in. Few, if any, debuggers allow invocation of member functions from objects, whether they are instances of template or nontemplate classes.

Future revisions of this tutorial may include more information on debugging. For now, we include some anecdotal information that may be helpful:

The Metrowerks CodeWarrior Professional 5.2 debugger does a good job of understanding template code, and correctly demangling symbol names. The Windows version is much less successful than the Macintosh version in maintaining proper state when you trace into template functions and template or nontemplate members of template classes. On Windows, it often fails to recognize any local variables.

On IRIX 6.5, we have had some success with dbx, TotalView (TotalView 3.9 or higher, Etnus (http://www.etnus.com)), and SGI's cvd debuggers. The following table indicates combinations of compilers and debuggers on IRIX which are compatible:

 
TotalView
dbx
cvd
KCC
compatible
incompatible
incompatible
CC
compatible
compatible
compatible
EGCS (g++)
incompatible
compatible
compatible*

Compatible compiler/debugger combinations on IRIX 6.5.
*Object member access
sometimes crashes cvd with EGCS.

These debuggers fail in some cases to demangle names of objects which are instances of template classes.

See also the discussion of the dbprint() function family. This describes how to set up function prototypes allowing you to examine data values from POOMA containers like Field and Array interactively from some debuggers.



[Prev] [Home] [Next]
Copyright © Los Alamos National Laboratory 1998-2000