Go back to Richel Bilderbeek's homepage.

Go back to Richel Bilderbeek's C++ page.

 

 

 

 

 

(C++) DRD

 

DRD is member of the valgrind tool suite and useful for detecting deadlocks.

 

Below is an example of causing a deadlock, followed by using DRD to detect it.

 

 

 

 

 

Example

 

The example below will end in a deadlock: a thread calls the member function MyClass::DoIt. This member function acquires the class its only mutex. Then, MyClass::DoIt calls MyClass::DoItAgain, which starts by acquiring the same mutex. Because this mutex is already in use, the program will wait infinitely.

 

 

#include <iostream>
#include <thread>
#include <regex>
#include <boost/bind.hpp>

struct MyClass
{
  void DoIt()
  {
    ///Acquire single access
    std::lock_guard<std::mutex> lock(m_mutex);

    ///Use of std::clog to write to screen directly
    std::clog << __func__ << '\n';

    ///Causes deadlock: DoItAgain wants to acquire m_mutex, which is in use already!
    DoItAgain();
  }

  private:
  void DoItAgain()
  {
    ///Causes deadlock: m_mutex is in use already!
    std::lock_guard<std::mutex> lock(m_mutex);

    ///Use of std::clog to write to screen directly
    std::clog << __func__ << '\n';
  }

  ///The MyClass mutex
  static std::mutex m_mutex;
};

std::mutex MyClass::m_mutex;

int main()
{
  ///Create the class that will cause a deadlock
  MyClass my_class;

  ///Create a thread that calls the class its method that will cause a deadlock
  std::thread t(boost::bind(&MyClass::DoIt,my_class));

  ///Due to deadlock, it will take forever to wait until t has finished
  t.join();
}

 

Screen output:

 

DoIt

 

The problem in this example can be solved by using a std::recursive_mutex instead.

 

 

 

 

 

Detecting a deadlock using DRD

 

Detecting a deadlock using DRD gives an indication of the problem. DRD calls the program like this:

 

#!/bin/sh
valgrind --tool=drd --log-file=helgrind.txt ../CppDeadlock-build-desktop/./CppDeadlock

 

After terminating the program by CTRL-C, the following output is generated by DRD:

 

==8958== drd, a thread error detector
==8958== Copyright (C) 2006-2010, and GNU GPL'd, by Bart Van Assche.
==8958== Using Valgrind-3.6.1 and LibVEX; rerun with -h for copyright info
==8958== Command: ../CppDeadlock-build-desktop/./CppDeadlock
==8958== Parent PID: 8957
==8958==
==8958== Thread 2:
==8958== Recursive locking not allowed: mutex 0x804c194, recursion count 1, owner 2.
==8958==    at 0x40295E0: pthread_mutex_lock (drd_pthread_intercepts.c:584)
==8958==    by 0x80494EF: MyClass::DoIt() (in /home/richel/Projects/Website/CppDeadlock-build-desktop/CppDeadlock)
==8958==    by 0x4399265: ??? (in /usr/lib/i386-linux-gnu/libstdc++.so.6.0.14)
==8958==    by 0x4027E8B: vgDrd_thread_wrapper (drd_pthread_intercepts.c:281)
==8958==    by 0x4027E8B: vgDrd_thread_wrapper (drd_pthread_intercepts.c:281)
==8958==    by 0x42E1E98: start_thread (pthread_create.c:304)
==8958==    by 0x44F273D: clone (clone.S:130)
==8958== mutex 0x804c194 was first observed at:
==8958==    at 0x40295E0: pthread_mutex_lock (drd_pthread_intercepts.c:584)
==8958==    by 0x804949C: MyClass::DoIt() (in /home/richel/Projects/Website/CppDeadlock-build-desktop/CppDeadlock)
==8958==    by 0x4399265: ??? (in /usr/lib/i386-linux-gnu/libstdc++.so.6.0.14)
==8958==    by 0x4027E8B: vgDrd_thread_wrapper (drd_pthread_intercepts.c:281)
==8958==    by 0x4027E8B: vgDrd_thread_wrapper (drd_pthread_intercepts.c:281)
==8958==    by 0x42E1E98: start_thread (pthread_create.c:304)
==8958==    by 0x44F273D: clone (clone.S:130)
==8958==

 

 

 

 

 

DRD tips

 

From [1].

 

8.2.13. Hints and Tips for Effective Use of DRD
The following information may be helpful when using DRD:

Make sure that debug information is present in the executable being analyzed, such that DRD can print function name and line number information in stack traces. Most compilers can be told to include debug information via compiler option -g.

Compile with option -O1 instead of -O0. This will reduce the amount of generated code, may reduce the amount of debug info and will speed up DRD's processing of the client program. For more information, see also Getting started.

If DRD reports any errors on libraries that are part of your Linux distribution like e.g. libc.so or libstdc++.so, installing the debug packages for these libraries will make the output of DRD a lot more detailed.

When using C++, do not send output from more than one thread to std::cout. Doing so would not only generate multiple data race reports, it could also result in output from several threads getting mixed up. Either use printf or do the following:

* Derive a class from std::ostreambuf and let that class send output line by line to stdout. This will avoid that individual lines of text produced by different threads get mixed up.

* Create one instance of std::ostream for each thread. This makes stream formatting settings thread-local. Pass a per-thread instance of the class derived from std::ostreambuf to the constructor of each instance.

* Let each thread send its output to its own instance of std::ostream instead of std::cout.

 

 

 

 

 

References

 

  1. Valgrind user manual section 8.2.13

 

 

 

 

 

Go back to Richel Bilderbeek's C++ page.

Go back to Richel Bilderbeek's homepage.

 

Valid XHTML 1.0 Strict