Peaceful coexistence of PC-Lint and VivaMP

23.02.2009 Andrey Karpov

We are asked different questions relating to using PC-Lint, VivaMP and other static analyzers for testing parallel programs, we are asked if these tools are competitors, and there are many other similar questions. This seems to relate to release of a new version of PC-Lint 9.0 in which support of parallelism analysis is announced (see PC-lint Manual. Section: 12. Multi-thread support). I decided to unite discussions on this topic in which I participated and present them in a form of two questions which I will answer in detail.

The first question.

Can PC-Lint be used for testing parallel programs?

This question is asked frequently but, unfortunately, it is inappropriate. The direct answer is "yes". But people actually imply a different meaning in it - "Can one find errors in programs relating to usage of paralleling algorithms with the help of PC-Lint?". Then the answer is "no" unless you undertake special actions.

The point is that the static analyzer doesn't know if a concrete code section is executed parallel and it can only make some presuppositions. Here are the two causes of this ignorance:

1) Static analyzers, as well as compilers, work with each .cpp file separately, and that's why if in the file A a parallel call of f() function from the file B occurs, we don't know about it while analyzing the file B. Of course, we could create static analyzers which analyze all the files simultaneously (and perhaps there are such analyzers) but it is a very complicated task. At least PC-Lint works with each file separately.

2) Even if the call of f() function is located in the same file, still it is not so easy to understand if there will be a parallel call. The call may in a complicated way depend on the algorithm's logic and input data which the static analyzer doesn't possess.

Let's consider an example of a function (given in the manual to PC-Lint 9.0):

void f()
{
  static int n = 0;
  /* ... */
}

The problem is that if f() function is called from parallel threads an error of initializing 'n' variable can occur. To diagnose this case we need to explicitly indicate to PC-Lint that f() function can be called parallel. For this purpose we should use the construction of this kind:

//lint -sem(f, thread)

Yet in this case during diagnosis you will get a warning message: Warning 457: "Thread 'f(void)' has an unprotected write access to variable 'n' which is used by thread 'f(void)" .

Actually, the statement I've made about impossibility of automatically diagnosing errors in parallel code without special measures taken is not quite true. PC-Lint supports POSIX threads and can, for example, automatically detect errors relating to locks if such functions as pthread_mutex_lock() and pthread_mutex_unlock() are used. But if the paralleling mechanism is built not on POSIX threads, again you will have to use special directives to teach PC-Lint what functions lead to locks and unlocks:

-sem(function-name, thread_lock)
-sem(function-name, thread_unlock)

In this case you can detect errors in the code like the following:

//lint -sem( lock, thread_lock )
//lint -sem( unlock, thread_unlock )
extern int g();
void lock(void), unlock(void);
void f()
{
  //-------------
  lock();
  if( g() )
    return; // Warning 454
  unlock();
  //-------------
  if( g() )
  {
    lock();
    unlock();
    unlock(); // Warning 455
    return;
  }
  //-------------
  if( g() )
  lock();
  { // Warning 456
    // do something interesting
  }
}

I'll try to draw the conclusion once more. PC-Lint analyzer can effectively detect parallelism-related errors if you provide it with preliminary "prompts". Otherwise it can test parallel code considering it sequential computation and therefore fail to detect some errors.

From all discussed above the second question follows.

If PC-Lint analyzer allows searching errors relating to parallelism, is there any sense to use VivaMP?

There are two answers:

1) PC-Lint cannot detect errors in programs built on OpenMP technology.

2) VivaMP is a specialized product for diagnosing parallel programs while PC-Lint is a good static analyzer of general purpose. I emphasize once more that it is good, but general-purpose. Parallelism analysis implemented in it is only one of the subsystems. VivaMP tool is created specially for diagnosing parallel programs and possesses large abilities in this sphere.

We can state that VivaMP and PC-Lint are not competitors. They supplement each other. What VivaMP can diagnose PC-Lint cannot. What PC-Lint can do VivaMP cannot. But it is gradually learning to :). I'll explain this by examples.

Let's take that example with the static variable once more:

void f()
{
  #pragma omp parallel num_threads(2)
  {
    static int cachedResult = ComputeSomethingSlowly();
    ...
  }
  ...
}

The static variable is defined inside a parallel OpenMP section what will lead to an initializing error. In this case PC-Lint cannot detect the error. We cannot specify that f() function is executed parallel as in this example only a part of the function's code will be paralleled. But VivaMP will detect the error and show the message: V1204. Data race risk. Unprotected static variable declaration in a parallel code.

The similar situation is with the lock/unlock error. Let's consider the following code:

void foo()
{
  ...
  #pragma omp parallel sections
  {
    #pragma omp section // V1102.
    {
      omp_set_lock(&myLock);
    }
    #pragma omp section // V1102.
    {
      omp_unset_lock(&myLock);
    }
  }
  ...
}

In this case there are two OpenMP directives which asymmetrically use omp_set_lock and omp_unset_lock functions in two sections. That is, the given code contains two errors about which VivaMP will warn (V1102. Non-symmetrical use of set/unset functions for the following lock variable(s): myLock).

And PC-Lint cannot help here at all. The point is that OpenMP-directives don't mean anything to it, that is, it simply ignores them. And, consequently, from the viewpoint of PC-Lint the code will look as follows:

//lint -sem(omp_set_lock, thread_lock)
//lint -sem(omp_unset_lock, thread_unlock)
void foo()
{
  ...
  {
    omp_set_lock(&myLock);
  }
  {
    omp_unset_lock(&myLock);
  }
  ...
}

Even if we specify to PC-Lint that omp_set_lock/omp_unset_lock is used for lock/unlock, it will consider foo() function correct unlike VivaMP. There is one lock and one unlock. There is no error.

I repeat once again that the reason is that PC-Lint cannot analyze OpenMP-directives defined with the help of #pragma constructions due to what the algorithm's logic looks different to it.

Conclusion

VivaMP and PC-Lint are the two good tools meant for different aspects of parallel programming and they supplement each other very well.