C++线程异常检测工具(Valrind之helgrind和DRD)

应用场景

C++多线程运行的程序中:

  • 接口使用问题:常见的多线程接口使用不当问题(不是本文介绍重点)
  • 竞态问题:多线程场景下未加锁保护竞态资源
  • 多线程死锁问题
  • 锁范围问题

工具使用局限性

  • 只能检测基于POSIXpthread库实现的多线程程序。当前C++11的thread库在Linux就是使用POSIXpthread库实现的。
  • 只能检测程序运行过的流程中的多线程竞态资源访问、死锁、加锁范围过大等问题。因此,需要覆盖完全的话,则需要覆盖所有的分支和流程。
  • 仅支持程序的正常退出和信号为SIGINT(ctrl+c)的程序退出,不支持kill -9程序的操作

工具使用

首先将应用程序,编译多线程场景下的可执行程序,需要链接pthread库。

g++ -O0 xxxx.cpp -o xxxx -lpthread

helgrind工具

此工具可支持检测:多线程接口使用问题、竞态问题、死锁问题。

valgrind --tool=helgrind ./xxxx

DRD工具

此工具可支持检测:多线程接口使用问题、竞态问题、锁范围问题。

valgrind --tool=drd --exclusive-threshold=10 ./xxx

其中,--exclusive-threshold=10,表示互斥锁或者写着(独占),占用此资源超过10ms,将报告信息。因此,通过查看每个锁占用的时间,根据自己的业务逻辑需要进行判定是否可以减小锁的范围,减小独占时间,提高并发性。

常见场景举例

竞态问题

代码示例:

#include <thread>
using namespace std;

uint32_t g_num = 0;

int main()
{
    auto run = [&]() {
        g_num++;
    };

    thread th(run);
    g_num++;

    th.join();

    return 0;
}

通过工具使用章节,运行后的结果如下:

==721== Helgrind, a thread error detector
==721== Copyright (C) 2007-2017, and GNU GPL'd, by OpenWorks LLP et al.
==721== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==721== Command: ./test
==721== 
==721== ---Thread-Announcement------------------------------------------
==721== 
==721== Thread #1 is the program's root thread
==721== 
==721== ---Thread-Announcement------------------------------------------
==721== 
==721== Thread #2 was created
==721==    at 0x4BA2122: clone (clone.S:71)
==721==    by 0x486A2EB: create_thread (createthread.c:101)
==721==    by 0x486BE0F: pthread_create@@GLIBC_2.2.5 (pthread_create.c:817)
==721==    by 0x4842917: ??? (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_helgrind-amd64-linux.so)
==721==    by 0x495D0A8: std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State> >, void (*)()) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.28)
==721==    by 0x10935A: std::thread::thread<main::{lambda()#1}&, , void>(main::{lambda()#1}&) (in /tmp/valgrind/test)
==721==    by 0x109272: main (in /tmp/valgrind/test)
==721== 
==721== ----------------------------------------------------------------
==721== 
==721== Possible data race during read of size 4 at 0x10C01C by thread #1
==721== Locks held: none
==721==    at 0x109273: main (in /tmp/valgrind/test)
==721== 
==721== This conflicts with a previous write of size 4 by thread #2
==721== Locks held: none
==721==    at 0x10923B: main::{lambda()#1}::operator()() const (in /tmp/valgrind/test)
==721==    by 0x1097A6: void std::__invoke_impl<void, main::{lambda()#1}>(std::__invoke_other, main::{lambda()#1}&&) (in /tmp/valgrind/test)
==721==    by 0x109747: std::__invoke_result<main::{lambda()#1}>::type std::__invoke<main::{lambda()#1}>(std::__invoke_result&&, (main::{lambda()#1}&&)...) (in /tmp/valgrind/test)
==721==    by 0x1096E5: void std::thread::_Invoker<std::tuple<main::{lambda()#1}> >::_M_invoke<0ul>(std::_Index_tuple<0ul>) (in /tmp/valgrind/test)
==721==    by 0x1096A6: std::thread::_Invoker<std::tuple<main::{lambda()#1}> >::operator()() (in /tmp/valgrind/test)
==721==    by 0x10967B: std::thread::_State_impl<std::thread::_Invoker<std::tuple<main::{lambda()#1}> > >::_M_run() (in /tmp/valgrind/test)
==721==    by 0x495CDE3: ??? (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.28)
==721==    by 0x4842B1A: ??? (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_helgrind-amd64-linux.so)
==721==  Address 0x10c01c is 0 bytes inside data symbol "g_num"
==721== 
==721== ----------------------------------------------------------------
==721== 
==721== Possible data race during write of size 4 at 0x10C01C by thread #1
==721== Locks held: none
==721==    at 0x10927C: main (in /tmp/valgrind/test)
==721== 
==721== This conflicts with a previous write of size 4 by thread #2
==721== Locks held: none
==721==    at 0x10923B: main::{lambda()#1}::operator()() const (in /tmp/valgrind/test)
==721==    by 0x1097A6: void std::__invoke_impl<void, main::{lambda()#1}>(std::__invoke_other, main::{lambda()#1}&&) (in /tmp/valgrind/test)
==721==    by 0x109747: std::__invoke_result<main::{lambda()#1}>::type std::__invoke<main::{lambda()#1}>(std::__invoke_result&&, (main::{lambda()#1}&&)...) (in /tmp/valgrind/test)
==721==    by 0x1096E5: void std::thread::_Invoker<std::tuple<main::{lambda()#1}> >::_M_invoke<0ul>(std::_Index_tuple<0ul>) (in /tmp/valgrind/test)
==721==    by 0x1096A6: std::thread::_Invoker<std::tuple<main::{lambda()#1}> >::operator()() (in /tmp/valgrind/test)
==721==    by 0x10967B: std::thread::_State_impl<std::thread::_Invoker<std::tuple<main::{lambda()#1}> > >::_M_run() (in /tmp/valgrind/test)
==721==    by 0x495CDE3: ??? (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.28)
==721==    by 0x4842B1A: ??? (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_helgrind-amd64-linux.so)
==721==  Address 0x10c01c is 0 bytes inside data symbol "g_num"
==721== 
==721== 
==721== Use --history-level=approx or =none to gain increased speed, at
==721== the cost of reduced accuracy of conflicting-access information
==721== For lists of detected and suppressed errors, rerun with: -s
==721== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)

从上面的日志可以看出,g_num是一个竞态资源,未加锁访问,存在访问冲突conflicts

解决方案

  • 使用原子变量
  • 加锁保护

此处采用原子变量的方式进行举例:

#include <thread>
#include <atomic>
using namespace std;

atomic_uint32_t g_num {0};

int main()
{
    auto run = [&]() {
        g_num++;
    };

    thread th(run);
    g_num++;

    th.join();

    return 0;
}

输出日志结果:

==820== Helgrind, a thread error detector
==820== Copyright (C) 2007-2017, and GNU GPL'd, by OpenWorks LLP et al.
==820== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==820== Command: ./test
==820== 
==820== 
==820== Use --history-level=approx or =none to gain increased speed, at
==820== the cost of reduced accuracy of conflicting-access information
==820== For lists of detected and suppressed errors, rerun with: -s
==820== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

多线程死锁问题

代码示例:

#include <vector>
#include <thread>
#include <chrono>
#include <mutex>

using namespace std;

uint32_t g_num  = 0;
mutex mtxA;
mutex mtxB;

void Fun1()
{
    lock_guard<mutex> guard1(mtxA);
    this_thread::sleep_for(chrono::milliseconds(10)); // 增加延时,是为了另外一个线程获取到另外一把锁

    lock_guard<mutex> guard2(mtxB);

    g_num++;
}

void Fun2()
{
    lock_guard<mutex> guard2(mtxB);
    this_thread::sleep_for(chrono::milliseconds(10));

    lock_guard<mutex> guard1(mtxA);

    g_num++;
}

int main()
{
    vector<thread> ths;
    ths.push_back(thread(Fun1));
    ths.push_back(thread(Fun2));

    for (auto& th : ths) {
        th.join();
    }

    return 0;
}

注:该程序死锁了,需要使用ctrl+c终止程序。

通过工具使用章节,运行后的结果如下:

==1999== Helgrind, a thread error detector
==1999== Copyright (C) 2007-2017, and GNU GPL'd, by OpenWorks LLP et al.
==1999== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==1999== Command: ./test
==1999== 
==1999== 
==1999== Process terminating with default action of signal 2 (SIGINT)
==1999==    at 0x486CCD7: __pthread_clockjoin_ex (pthread_join_common.c:145)
==1999==    by 0x483FB15: ??? (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_helgrind-amd64-linux.so)
==1999==    by 0x495D046: std::thread::join() (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.28)
==1999==    by 0x109624: main (in /tmp/valgrind/test)
==1999== ---Thread-Announcement------------------------------------------
==1999== 
==1999== Thread #2 was created
==1999==    at 0x4BA2122: clone (clone.S:71)
==1999==    by 0x486A2EB: create_thread (createthread.c:101)
==1999==    by 0x486BE0F: pthread_create@@GLIBC_2.2.5 (pthread_create.c:817)
==1999==    by 0x4842917: ??? (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_helgrind-amd64-linux.so)
==1999==    by 0x495D0A8: std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State> >, void (*)()) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.28)
==1999==    by 0x109F28: std::thread::thread<void (&)(), , void>(void (&)()) (in /tmp/valgrind/test)
==1999==    by 0x109578: main (in /tmp/valgrind/test)
==1999== 
==1999== ----------------------------------------------------------------
==1999== 
==1999== Thread #2: Exiting thread still holds 1 lock
==1999==    at 0x4876170: __lll_lock_wait (lowlevellock.c:52)
==1999==    by 0x486E0A2: pthread_mutex_lock (pthread_mutex_lock.c:80)
==1999==    by 0x483FE78: ??? (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_helgrind-amd64-linux.so)
==1999==    by 0x10972D: __gthread_mutex_lock(pthread_mutex_t*) (in /tmp/valgrind/test)
==1999==    by 0x10989B: std::mutex::lock() (in /tmp/valgrind/test)
==1999==    by 0x109A0F: std::lock_guard<std::mutex>::lock_guard(std::mutex&) (in /tmp/valgrind/test)
==1999==    by 0x10940F: Fun1() (in /tmp/valgrind/test)
==1999==    by 0x10B85F: void std::__invoke_impl<void, void (*)()>(std::__invoke_other, void (*&&)()) (in /tmp/valgrind/test)
==1999==    by 0x10B7F7: std::__invoke_result<void (*)()>::type std::__invoke<void (*)()>(void (*&&)()) (in /tmp/valgrind/test)
==1999==    by 0x10B789: void std::thread::_Invoker<std::tuple<void (*)()> >::_M_invoke<0ul>(std::_Index_tuple<0ul>) (in /tmp/valgrind/test)
==1999==    by 0x10B746: std::thread::_Invoker<std::tuple<void (*)()> >::operator()() (in /tmp/valgrind/test)
==1999==    by 0x10B717: std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (*)()> > >::_M_run() (in /tmp/valgrind/test)
==1999== 
==1999== ---Thread-Announcement------------------------------------------
==1999== 
==1999== Thread #3 was created
==1999==    at 0x4BA2122: clone (clone.S:71)
==1999==    by 0x486A2EB: create_thread (createthread.c:101)
==1999==    by 0x486BE0F: pthread_create@@GLIBC_2.2.5 (pthread_create.c:817)
==1999==    by 0x4842917: ??? (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_helgrind-amd64-linux.so)
==1999==    by 0x495D0A8: std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State> >, void (*)()) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.28)
==1999==    by 0x109F28: std::thread::thread<void (&)(), , void>(void (&)()) (in /tmp/valgrind/test)
==1999==    by 0x1095AA: main (in /tmp/valgrind/test)
==1999== 
==1999== ----------------------------------------------------------------
==1999== 
==1999== Thread #3: Exiting thread still holds 1 lock
==1999==    at 0x4876170: __lll_lock_wait (lowlevellock.c:52)
==1999==    by 0x486E0A2: pthread_mutex_lock (pthread_mutex_lock.c:80)
==1999==    by 0x483FE78: ??? (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_helgrind-amd64-linux.so)
==1999==    by 0x10972D: __gthread_mutex_lock(pthread_mutex_t*) (in /tmp/valgrind/test)
==1999==    by 0x10989B: std::mutex::lock() (in /tmp/valgrind/test)
==1999==    by 0x109A0F: std::lock_guard<std::mutex>::lock_guard(std::mutex&) (in /tmp/valgrind/test)
==1999==    by 0x1094DA: Fun2() (in /tmp/valgrind/test)
==1999==    by 0x10B85F: void std::__invoke_impl<void, void (*)()>(std::__invoke_other, void (*&&)()) (in /tmp/valgrind/test)
==1999==    by 0x10B7F7: std::__invoke_result<void (*)()>::type std::__invoke<void (*)()>(void (*&&)()) (in /tmp/valgrind/test)
==1999==    by 0x10B789: void std::thread::_Invoker<std::tuple<void (*)()> >::_M_invoke<0ul>(std::_Index_tuple<0ul>) (in /tmp/valgrind/test)
==1999==    by 0x10B746: std::thread::_Invoker<std::tuple<void (*)()> >::operator()() (in /tmp/valgrind/test)
==1999==    by 0x10B717: std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (*)()> > >::_M_run() (in /tmp/valgrind/test)
==1999== 
==1999== 
==1999== Use --history-level=approx or =none to gain increased speed, at
==1999== the cost of reduced accuracy of conflicting-access information
==1999== For lists of detected and suppressed errors, rerun with: -s
==1999== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)

从上面的打印输出日志可以看出:

一般程序退出时,都会解锁,但是在程序退出时,仍然持有锁,说明此处发生死锁了。

解决方案

多线程情况下的对多个互斥信号量的加锁顺序保持一致。

程序未死锁,但有死锁风险

将上诉代码改造一下,把所有的延时的调用都删除:

#include <vector>
#include <thread>
#include <chrono>
#include <mutex>

using namespace std;

uint32_t g_num  = 0;
mutex mtxA;
mutex mtxB;

void Fun1()
{
    lock_guard<mutex> guard1(mtxA);
    lock_guard<mutex> guard2(mtxB);

    g_num++;
}

void Fun2()
{
    lock_guard<mutex> guard2(mtxB);
    lock_guard<mutex> guard1(mtxA);

    g_num++;
}

int main()
{
    vector<thread> ths;
    ths.push_back(thread(Fun1));
    ths.push_back(thread(Fun2));

    for (auto& th : ths) {
        th.join();
    }

    return 0;
}

编译后,再借助valgrind的helgrind工具,运行结果如下:

==137== Helgrind, a thread error detector
==137== Copyright (C) 2007-2017, and GNU GPL'd, by OpenWorks LLP et al.
==137== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==137== Command: ./test
==137== 
==137== ---Thread-Announcement------------------------------------------
==137== 
==137== Thread #3 was created
==137==    at 0x4BA2122: clone (clone.S:71)
==137==    by 0x486A2EB: create_thread (createthread.c:101)
==137==    by 0x486BE0F: pthread_create@@GLIBC_2.2.5 (pthread_create.c:817)
==137==    by 0x4842917: ??? (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_helgrind-amd64-linux.so)
==137==    by 0x495D0A8: std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State> >, void (*)()) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.28)
==137==    by 0x109A94: std::thread::thread<void (&)(), , void>(void (&)()) (in /tmp/valgrind/test)
==137==    by 0x10951E: main (in /tmp/valgrind/test)
==137== 
==137== ----------------------------------------------------------------
==137== 
==137==  Thread #3: lock order "0x10F060 before 0x10F0A0" violated
==137== 
==137== Observed (incorrect) order is: acquisition of lock at 0x10F0A0
==137==    at 0x483FEDF: ??? (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_helgrind-amd64-linux.so)
==137==    by 0x10965A: __gthread_mutex_lock(pthread_mutex_t*) (in /tmp/valgrind/test)
==137==    by 0x1097C9: std::mutex::lock() (in /tmp/valgrind/test)
==137==    by 0x109905: std::lock_guard<std::mutex>::lock_guard(std::mutex&) (in /tmp/valgrind/test)
==137==    by 0x10943B: Fun2() (in /tmp/valgrind/test)
==137==    by 0x10B28B: void std::__invoke_impl<void, void (*)()>(std::__invoke_other, void (*&&)()) (in /tmp/valgrind/test)
==137==    by 0x10B223: std::__invoke_result<void (*)()>::type std::__invoke<void (*)()>(void (*&&)()) (in /tmp/valgrind/test)
==137==    by 0x10B1B5: void std::thread::_Invoker<std::tuple<void (*)()> >::_M_invoke<0ul>(std::_Index_tuple<0ul>) (in /tmp/valgrind/test)
==137==    by 0x10B172: std::thread::_Invoker<std::tuple<void (*)()> >::operator()() (in /tmp/valgrind/test)
==137==    by 0x10B143: std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (*)()> > >::_M_run() (in /tmp/valgrind/test)
==137==    by 0x495CDE3: ??? (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.28)
==137==    by 0x4842B1A: ??? (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_helgrind-amd64-linux.so)
==137== 
==137==  followed by a later acquisition of lock at 0x10F060
==137==    at 0x483FEDF: ??? (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_helgrind-amd64-linux.so)
==137==    by 0x10965A: __gthread_mutex_lock(pthread_mutex_t*) (in /tmp/valgrind/test)
==137==    by 0x1097C9: std::mutex::lock() (in /tmp/valgrind/test)
==137==    by 0x109905: std::lock_guard<std::mutex>::lock_guard(std::mutex&) (in /tmp/valgrind/test)
==137==    by 0x10944E: Fun2() (in /tmp/valgrind/test)
==137==    by 0x10B28B: void std::__invoke_impl<void, void (*)()>(std::__invoke_other, void (*&&)()) (in /tmp/valgrind/test)
==137==    by 0x10B223: std::__invoke_result<void (*)()>::type std::__invoke<void (*)()>(void (*&&)()) (in /tmp/valgrind/test)
==137==    by 0x10B1B5: void std::thread::_Invoker<std::tuple<void (*)()> >::_M_invoke<0ul>(std::_Index_tuple<0ul>) (in /tmp/valgrind/test)
==137==    by 0x10B172: std::thread::_Invoker<std::tuple<void (*)()> >::operator()() (in /tmp/valgrind/test)
==137==    by 0x10B143: std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (*)()> > >::_M_run() (in /tmp/valgrind/test)
==137==    by 0x495CDE3: ??? (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.28)
==137==    by 0x4842B1A: ??? (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_helgrind-amd64-linux.so)
==137== 
==137== Required order was established by acquisition of lock at 0x10F060
==137==    at 0x483FEDF: ??? (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_helgrind-amd64-linux.so)
==137==    by 0x10965A: __gthread_mutex_lock(pthread_mutex_t*) (in /tmp/valgrind/test)
==137==    by 0x1097C9: std::mutex::lock() (in /tmp/valgrind/test)
==137==    by 0x109905: std::lock_guard<std::mutex>::lock_guard(std::mutex&) (in /tmp/valgrind/test)
==137==    by 0x109396: Fun1() (in /tmp/valgrind/test)
==137==    by 0x10B28B: void std::__invoke_impl<void, void (*)()>(std::__invoke_other, void (*&&)()) (in /tmp/valgrind/test)
==137==    by 0x10B223: std::__invoke_result<void (*)()>::type std::__invoke<void (*)()>(void (*&&)()) (in /tmp/valgrind/test)
==137==    by 0x10B1B5: void std::thread::_Invoker<std::tuple<void (*)()> >::_M_invoke<0ul>(std::_Index_tuple<0ul>) (in /tmp/valgrind/test)
==137==    by 0x10B172: std::thread::_Invoker<std::tuple<void (*)()> >::operator()() (in /tmp/valgrind/test)
==137==    by 0x10B143: std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (*)()> > >::_M_run() (in /tmp/valgrind/test)
==137==    by 0x495CDE3: ??? (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.28)
==137==    by 0x4842B1A: ??? (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_helgrind-amd64-linux.so)
==137== 
==137==  followed by a later acquisition of lock at 0x10F0A0
==137==    at 0x483FEDF: ??? (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_helgrind-amd64-linux.so)
==137==    by 0x10965A: __gthread_mutex_lock(pthread_mutex_t*) (in /tmp/valgrind/test)
==137==    by 0x1097C9: std::mutex::lock() (in /tmp/valgrind/test)
==137==    by 0x109905: std::lock_guard<std::mutex>::lock_guard(std::mutex&) (in /tmp/valgrind/test)
==137==    by 0x1093A9: Fun1() (in /tmp/valgrind/test)
==137==    by 0x10B28B: void std::__invoke_impl<void, void (*)()>(std::__invoke_other, void (*&&)()) (in /tmp/valgrind/test)
==137==    by 0x10B223: std::__invoke_result<void (*)()>::type std::__invoke<void (*)()>(void (*&&)()) (in /tmp/valgrind/test)
==137==    by 0x10B1B5: void std::thread::_Invoker<std::tuple<void (*)()> >::_M_invoke<0ul>(std::_Index_tuple<0ul>) (in /tmp/valgrind/test)
==137==    by 0x10B172: std::thread::_Invoker<std::tuple<void (*)()> >::operator()() (in /tmp/valgrind/test)
==137==    by 0x10B143: std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (*)()> > >::_M_run() (in /tmp/valgrind/test)
==137==    by 0x495CDE3: ??? (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.28)
==137==    by 0x4842B1A: ??? (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_helgrind-amd64-linux.so)
==137== 
==137==  Lock at 0x10F060 was first observed
==137==    at 0x483FEDF: ??? (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_helgrind-amd64-linux.so)
==137==    by 0x10965A: __gthread_mutex_lock(pthread_mutex_t*) (in /tmp/valgrind/test)
==137==    by 0x1097C9: std::mutex::lock() (in /tmp/valgrind/test)
==137==    by 0x109905: std::lock_guard<std::mutex>::lock_guard(std::mutex&) (in /tmp/valgrind/test)
==137==    by 0x109396: Fun1() (in /tmp/valgrind/test)
==137==    by 0x10B28B: void std::__invoke_impl<void, void (*)()>(std::__invoke_other, void (*&&)()) (in /tmp/valgrind/test)
==137==    by 0x10B223: std::__invoke_result<void (*)()>::type std::__invoke<void (*)()>(void (*&&)()) (in /tmp/valgrind/test)
==137==    by 0x10B1B5: void std::thread::_Invoker<std::tuple<void (*)()> >::_M_invoke<0ul>(std::_Index_tuple<0ul>) (in /tmp/valgrind/test)
==137==    by 0x10B172: std::thread::_Invoker<std::tuple<void (*)()> >::operator()() (in /tmp/valgrind/test)
==137==    by 0x10B143: std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (*)()> > >::_M_run() (in /tmp/valgrind/test)
==137==    by 0x495CDE3: ??? (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.28)
==137==    by 0x4842B1A: ??? (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_helgrind-amd64-linux.so)
==137==  Address 0x10f060 is 0 bytes inside data symbol "mtxA"
==137== 
==137==  Lock at 0x10F0A0 was first observed
==137==    at 0x483FEDF: ??? (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_helgrind-amd64-linux.so)
==137==    by 0x10965A: __gthread_mutex_lock(pthread_mutex_t*) (in /tmp/valgrind/test)
==137==    by 0x1097C9: std::mutex::lock() (in /tmp/valgrind/test)
==137==    by 0x109905: std::lock_guard<std::mutex>::lock_guard(std::mutex&) (in /tmp/valgrind/test)
==137==    by 0x1093A9: Fun1() (in /tmp/valgrind/test)
==137==    by 0x10B28B: void std::__invoke_impl<void, void (*)()>(std::__invoke_other, void (*&&)()) (in /tmp/valgrind/test)
==137==    by 0x10B223: std::__invoke_result<void (*)()>::type std::__invoke<void (*)()>(void (*&&)()) (in /tmp/valgrind/test)
==137==    by 0x10B1B5: void std::thread::_Invoker<std::tuple<void (*)()> >::_M_invoke<0ul>(std::_Index_tuple<0ul>) (in /tmp/valgrind/test)
==137==    by 0x10B172: std::thread::_Invoker<std::tuple<void (*)()> >::operator()() (in /tmp/valgrind/test)
==137==    by 0x10B143: std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (*)()> > >::_M_run() (in /tmp/valgrind/test)
==137==    by 0x495CDE3: ??? (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.28)
==137==    by 0x4842B1A: ??? (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_helgrind-amd64-linux.so)
==137==  Address 0x10f0a0 is 0 bytes inside data symbol "mtxB"
==137== 
==137== 
==137== 
==137== Use --history-level=approx or =none to gain increased speed, at
==137== the cost of reduced accuracy of conflicting-access information
==137== For lists of detected and suppressed errors, rerun with: -s
==137== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 11 from 7)

从上诉日志中,请关注以下这句话:

Thread #3: lock order "0x10F060 before 0x10F0A0" violated

就是说,线程3违反了锁的顺序。从日志中,也可以找到相应的锁的签名,看到其对应的关系和顺序。在平常工作生活中,可以借助检测结果,可以快速的发现,可能存在死锁风险问题。

锁范围问题

只能检测某一个锁占用的时间,不能统计多处、多个锁的占用时间。应用意义不是很大。

代码示例:

#include <vector>
#include <thread>
#include <chrono>
#include <mutex>

using namespace std;

uint32_t g_num  = 0;
mutex mtxA;

void Fun1()
{
    lock_guard<mutex> guard1(mtxA);
    this_thread::sleep_for(chrono::milliseconds(20));
    g_num++;
}

void Fun2()
{
    lock_guard<mutex> guard1(mtxA);
    this_thread::sleep_for(chrono::milliseconds(50));
    g_num++;
}

int main()
{
    vector<thread> ths;
    ths.push_back(thread(Fun1));
    ths.push_back(thread(Fun2));

    for (auto& th : ths) {
        th.join();
    }

    return 0;
}

通过工具使用章节,运行后的结果如下:

==473== drd, a thread error detector                                                                                                              
==473== Copyright (C) 2006-2017, and GNU GPL'd, by Bart Van Assche.                                                                               
==473== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info                                                                        
==473== Command: ./test                                                                                                                           
==473==                                                                                                                                           
==473== Thread 2:                                                                                                                                 
==473== Acquired at:                                                                                                                              
==473==    at 0x484582C: pthread_mutex_lock (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_drd-amd64-linux.so)                                  
==473==    by 0x1096EF: __gthread_mutex_lock(pthread_mutex_t*) (in /tmp/valgrind/test)                                                            
==473==    by 0x10985D: std::mutex::lock() (in /tmp/valgrind/test)                                                                                
==473==    by 0x1099D1: std::lock_guard<std::mutex>::lock_guard(std::mutex&) (in /tmp/valgrind/test)                                              
==473==    by 0x1093D6: Fun1() (in /tmp/valgrind/test)                                                                                            
==473==    by 0x10B821: void std::__invoke_impl<void, void (*)()>(std::__invoke_other, void (*&&)()) (in /tmp/valgrind/test)                      
==473==    by 0x10B7B9: std::__invoke_result<void (*)()>::type std::__invoke<void (*)()>(void (*&&)()) (in /tmp/valgrind/test)                    
==473==    by 0x10B74B: void std::thread::_Invoker<std::tuple<void (*)()> >::_M_invoke<0ul>(std::_Index_tuple<0ul>) (in /tmp/valgrind/test)       
==473==    by 0x10B708: std::thread::_Invoker<std::tuple<void (*)()> >::operator()() (in /tmp/valgrind/test)                                      
==473==    by 0x10B6D9: std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (*)()> > >::_M_run() (in /tmp/valgrind/test)               
==473==    by 0x4968DE3: ??? (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.28)                                                                   
==473==    by 0x48424BA: ??? (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_drd-amd64-linux.so)                                                 
==473== Lock on mutex 0x10f060 was held during 22 ms (threshold: 10 ms).                                                                          
==473==    at 0x484687C: pthread_mutex_unlock (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_drd-amd64-linux.so)                                
==473==    by 0x10971E: __gthread_mutex_unlock(pthread_mutex_t*) (in /tmp/valgrind/test)                                                          
==473==    by 0x10988F: std::mutex::unlock() (in /tmp/valgrind/test)                                                                              
==473==    by 0x1099F4: std::lock_guard<std::mutex>::~lock_guard() (in /tmp/valgrind/test)                                                        
==473==    by 0x109417: Fun1() (in /tmp/valgrind/test)                                                                                            
==473==    by 0x10B821: void std::__invoke_impl<void, void (*)()>(std::__invoke_other, void (*&&)()) (in /tmp/valgrind/test)                      
==473==    by 0x10B7B9: std::__invoke_result<void (*)()>::type std::__invoke<void (*)()>(void (*&&)()) (in /tmp/valgrind/test)                    
==473==    by 0x10B74B: void std::thread::_Invoker<std::tuple<void (*)()> >::_M_invoke<0ul>(std::_Index_tuple<0ul>) (in /tmp/valgrind/test)       
==473==    by 0x10B708: std::thread::_Invoker<std::tuple<void (*)()> >::operator()() (in /tmp/valgrind/test)                                      
==473==    by 0x10B6D9: std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (*)()> > >::_M_run() (in /tmp/valgrind/test)               
==473==    by 0x4968DE3: ??? (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.28)                                                                   
==473==    by 0x48424BA: ??? (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_drd-amd64-linux.so)                                                 
==473== mutex 0x10f060 was first observed at:                                                                                                     
==473==    at 0x484576C: pthread_mutex_lock (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_drd-amd64-linux.so)                                  
==473==    by 0x1096EF: __gthread_mutex_lock(pthread_mutex_t*) (in /tmp/valgrind/test)                                                            
==473==    by 0x10985D: std::mutex::lock() (in /tmp/valgrind/test)                                                                                
==473==    by 0x1099D1: std::lock_guard<std::mutex>::lock_guard(std::mutex&) (in /tmp/valgrind/test)                                              
==473==    by 0x1093D6: Fun1() (in /tmp/valgrind/test)                                                                                            
==473==    by 0x10B821: void std::__invoke_impl<void, void (*)()>(std::__invoke_other, void (*&&)()) (in /tmp/valgrind/test)                      
==473==    by 0x10B7B9: std::__invoke_result<void (*)()>::type std::__invoke<void (*)()>(void (*&&)()) (in /tmp/valgrind/test)                    
==473==    by 0x10B74B: void std::thread::_Invoker<std::tuple<void (*)()> >::_M_invoke<0ul>(std::_Index_tuple<0ul>) (in /tmp/valgrind/test)       
==473==    by 0x10B708: std::thread::_Invoker<std::tuple<void (*)()> >::operator()() (in /tmp/valgrind/test)                                      
==473==    by 0x10B6D9: std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (*)()> > >::_M_run() (in /tmp/valgrind/test)               
==473==    by 0x4968DE3: ??? (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.28)                                                                   
==473==    by 0x48424BA: ??? (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_drd-amd64-linux.so)                                                 
==473==                                                                                                                                           
==473==                                                                                                                                           
==473== For lists of detected and suppressed errors, rerun with: -s                                                                               
==473== ERROR SUMMARY: 2 errors from 1 contexts (suppressed: 20 from 12)                                                                          
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值