我有一个heisenbug很少发生,它不能在任何环境中复制,它失败了,我不知道如何诊断它。
该错误与内存使用有关。腐败不符合腐败的定义four categories。
工具显示它不是:
我有信心地说这个,因为虽然我无法复制它,但在更高的事务环境中进行日志记录和工具表明上述情况并未发生。
我正在编译gcc c11没有优化,Wall,其他最小标志。
ASAN,电动围栏,hellgrind,memcheck,cppcheck发现没有问题。
堆管理似乎适用于池分配器,边界检查,腐败标记。
绝对没有单元测试
这个问题主要出现在极少数阵列被破坏的情况下,设置了无效边界,只有50个项目,但是项目数量被破坏,我们最终得到<0或> 50。核心转储显示了这一点。通过确定此数组绑定的来源并验证正确的值,我们可以防止此问题,但随后问题将迁移到另一个位置。因为这只影响单个客户和单一交易类型,它向我指示与此客户或交易相关的事物。但那棵树没有结果。
由于这种情况很少发生,我不能排除:
我无法在模拟导致此问题的条件的环境中运行上述任何工具(ASAN,电围栏......)。但我无法复制任何可以运行这些工具的环境。
我唯一的想法是:
我正在寻找我没有考虑过的新方法。如何自动化这种方法,为这类问题提供更好的工具。如何验证对象背后没有变化?
虽然这个问题可能会被视为偏离主题,但是您可以用来帮助追踪未来根本原因的通用工具是实现内存环缓冲区来记录关键事件。它与常规日志不同,因为日志仅限于内存,因此具有非常低的延迟。如果您有足够的内存专用于此日志,您应该能够在下次崩溃时检查客户,并更好地了解导致损坏的事件。
一个非常基本的实现是:
static_assert(0 == (LR_TAPE_SIZE & (LR_TAPE_SIZE-1)),
"LR_TAPE_SIZE must be a power of 2");
static_assert(LR_TAPE_SIZE > (LR_LOG_MAX + 1),
"LR_TAPE_SIZE must be larger than LR_LOG_MAX");
struct lr_tape {
uint32_t wrap : 1;
uint32_t head : 31;
char tape[LR_TAPE_SIZE];
};
int
lr_write(struct lr_tape *lr, const void *buf, uint32_t sz)
{
uint32_t pos = lr->head % LR_TAPE_SIZE;
uint32_t cnt = LR_TAPE_SIZE - pos;
memcpy(&lr->tape[pos], buf, (cnt < sz) ? cnt : sz);
if (cnt < sz) memcpy(&lr->tape[0], buf + cnt, sz - cnt);
lr->head += sz;
lr->wrap = lr->wrap || (lr->head >= LR_TAPE_SIZE);
return sz;
}
然后,您可以为它实现一个简单的printf
类包装器。
int
lr_log(struct lr_tape *lr, const char *fmt, ...)
{
char buf[LR_LOG_MAX + 1];
va_list ap;
int r, p;
va_start(ap, fmt);
r = vsnprintf(buf, LR_LOG_MAX, fmt, ap);
va_end(ap);
if (r <= 0) return r;
if (r >= LR_LOG_MAX) {
r = LR_LOG_MAX;
buf[r-3] = buf[r-2] = buf[r-1] = '.';
}
if (buf[r-1] != '\n') buf[r++] = '\n';
return lr_write(lr, buf, r);
}
以及发射它的方法:
void
lr_output(struct lr_tape *lr, FILE *out)
{
uint32_t pos = lr->head % LR_TAPE_SIZE;
uint32_t cnt = LR_TAPE_SIZE - pos;
if (lr->head == 0) return;
if (lr->wrap) {
fwrite("...", 3, 1, out);
fwrite(&lr->tape[pos], cnt, 1, out);
}
fwrite(lr->tape, pos, 1, out);
}