Yebangyu's Blog

Fond of Concurrency Programming, Distributed System and Performance Optimization.

一个轻量的lock free程序调试工具

lock free 程序时序混乱,逻辑复杂,反直觉的地方很多,心智负担很重,因此调试起来也非常困难。

下面用C++编写了一个用于调试lock free代码的程序,非常轻量,代码如下,只适用于linux环境。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//xx.h
#include <pthread.h>
class LockFreeLogger
{
public:
  struct Event
  {
    char *message;
    pthread_t thread_id;
    int info;
  };
  LockFreeLogger()
  {
    pos_ = 0;
    buffer_ = new Event[SIZE];
  }
  void log(char *message, int info)
  {
    int pos = __sync_fetch_and_add(&pos_, 1);
    buffer_[pos].message = message;
    buffer_[pos].info = info;
    buffer_[pos].thread_id = pthread_self();
  }
  void report()
  {
    //single thread code
    for (int i = 0; i < pos_; i++) {
      //cout or fwrite buffer_[i];
    }
  }
private:
  int pos_;
  Event *buffer_;
  static const int SIZE = 1234567;
};

extern LockFreeLogger logger;

#define LOG(message, info) logger.log(message, info)

当然,为了能使用它,应该在某个cpp里面定义一个实例出来

1
2
3
//xx.cpp
#include "xx.h"
LockFreeLogger logger;

然后就可以使用LOG(message, info)来记录信息了。比如说LOG("current value of counter", id)等。读者诸君可以根据需要,自由拓展Event结构。

注意事项

1,LockFreeLogger构造函数里的内存分配操作可能会失败,工业级代码需要考虑异常或者错误处理。

2,数组下标可能会越界,达到或者超过SIZE。不过调试的时候可以考虑把SIZE设大一点,一般情况下也就够用了。

3,注意到这个调试程序有时候并不能work。为什么?举个简单的场景:假如你的lock free程序中存在一个bug,也就是两条语句之间漏加了memory barrier,导致乱序。

你没发觉,并在两条语句中间加了日志,想看看变量的值,结果因为加了代码,乱序不出现,bug不出现了。

4,编写这个程序的思想,是我自己想出来的。后来我在《Multi Core Programming》这本书的第八章看到了同样的idea,又后来看到了Jeff Preshing写了一篇博客,介绍了一样的想法和做法,不过他的代码是针对Windows环境的。