鲲鹏社区首页
中文
注册
开发者
动态内存一致性检测工具简介

动态内存一致性检测工具简介

DevKit

发表于 2025/12/05

0

1. 背景

DevKit命令行包中dr-check功能采用动态检查模式检查C/C++源码在鲲鹏平台运行时是否存在内存一致性问题,并提供内存一致性的检测结果与插入内存屏障的建议。

工具包获取地址: https://www.hikunpeng.com/developer/devkit/download?tab=commandLine

2. 动态检测

动态检测是指通过二进制插桩的方式在程序运行时动态拦截并修改其指令流,通过将目标代码片段替换为跳转指令,重定向到预先编写的桩代码;执行完桩代码后再恢复原始指令并继续程序运行。整个过程在内存中完成,不修改原始二进制文件。

3. 数据竞争检测

3.1 竞态访问场景

写后写:

线程 1 和线程 2 同时写入同一个变量。多个线程同时执行写操作,但由于弱一致性模型下内存操作的并发执行和不确定性,内存系统可能以不同的顺序应用这些写入操作,导致最终变量的值不确定。

读后写:

线程 1 读取一个变量,接着线程 2 写入该变量。后续的读取操作(可能由线程 1 或其他线程执行)可能无法看到线程 2 的写入,因为内存系统可能没有及时更新所有的缓存或内存位置,导致读取到旧的数据而不是新写入的值。

写后读:

线程 1 写入一个变量的值,然后线程 2 尝试读取该变量,内存系统可能对操作进行重新排序,线程 2 可能在写入操作完全传播到所有处理器或内存位置之前就执行了读取操作,从而得到旧的值而不是新写入的值。

3.2 Happens-before 关系

Happens-before 是并发操作间的偏序关系,如果操作 A happens-before 操作 B,则 A 的内存效果对 B 可见

  • 程序顺序:如果α和β是同一线程执行的任意两个事件,程序顺序中α在β之前,则α->β。
  • Release和Acquire:如果α是release,β是其对应的acquire,两者都在同一个同步对象S上操作,则α->β。
  • Fork操作:线程t执行执行fork->(t,u) 线程u的操作
  • Join操作:线程u的操作 线程t执行执行join (t,u)
  • 传递性:if α ->β and β->γ,则α ->γ
  • 如果两个对同一内存位置的访问没有 happened-before 关系,并且至少有一个是写操作,那么就可能存在数据竞争。

3.3 检测方法

判断并发程序中的两个内存访问操作(至少一个是写操作)是否可能“同时”发生,即它们之间没有被正确的同步操作强制排序。

  1. 在程序运行过程中记录内存读写、加锁解锁、线程启动结束、volatile 访问等相关事件。
  2. 以 happens-before 规则为边,事件为节点,构建偏序有向图。
  3. 遍历图中所有的内存访问事件,检查冲突。
  • 对于每一对访问同一内存地址的冲突操作(如“写-写”或“写-读”),检查它们在关系图中是否存在路径。
  • 如果不存在任何路径从一个操作指向另一个操作(即互相不可达),则判定这两个操作是“并发”的,报告一个潜在的数据竞争。

4. 功能展示

准备存在内存一致性问题的demo名为test.c

#include <unistd.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>

volatile int *x,*y,*r1,*r2;
volatile int flag;

void thread0(void *arg)
{
    while(flag == 0);
    *y = 1;
    *x = 1;
}

void thread1(void *arg)
{
    while(flag == 0);
    *r2 = *x;
    *r1 = *y;
}

int main()
{
    int n = 0;
    int count = 0;
    while(n < 100) {
        pthread_t t1, t2;
        x = (int *)malloc(sizeof(int));
        y = (int *)malloc(sizeof(int));
        r1 = (int *)malloc(sizeof(int));
        r2 = (int *)malloc(sizeof(int));
        *x = *y = *r1 = *r2 = flag = 0;

        pthread_create(&t1, NULL, (void *)thread0, NULL);
        pthread_create(&t2, NULL, (void *)thread1, NULL);

        usleep(10);
        flag = 1;

        pthread_join(t1, NULL);
        pthread_join(t2, NULL);

        if (*r1 == 0 && *r2 == 1)
        {
            printf("error %d\n", n);
            count++;
        }

        free((void *)x);
        free((void *)y);
        free((void *)r1);
        free((void *)r2);
        n++;
    }
    printf("Run %d times, error %d times.\n", n, count);
    return 0;
}

编译该test.c并生成保存符号信息的二进制可执行文件test

gcc test.c -o test -g -lpthread

使用DevKit命令行工具中亲和工具dr-check对test进行动态的内存一致性问题进行检测

./devkit advisor dr-check -f /path/to/test

会生成三种格式的报告,查看html格式报告可直观地看到线程间对同一地址可能存在竞态访问,用户根据源码文件信息及修改意见对问题进行修复

本页内容