我有一个heisenbug发生得很少,以至于它不能在任何环境下复制,它会严重失败,而且我也不知道如何诊断它。
该错误与内存使用情况有关。 损坏不符合已定义的four categories损坏。
工具显示它不是:
我很自信地说这句话,因为虽然我不能复制它,但是在较高事务环境中的日志记录和工具表明上述情况不会发生。
我正在编译gcc c11,没有优化,Wall和其他最小标志。
ASAN,电围栏,hellgrind,memcheck和cppcheck都没有问题。
堆管理似乎可以与池分配器,边界检查和损坏标记一起使用。
绝对没有单元测试
主要在以下情况中出现此问题:非常很少的阵列损坏,设置了无效边界,只有50个项目,但是项目数被损坏,我们最终得到<0或>50。核心转储显示了这一点。通过确定此数组绑定来自何处并验证正确的值,我们可以避免此问题,但随后该问题将迁移到另一个位置。由于这仅影响单个客户和单个交易类型,因此向我指示与此客户或交易相关的内容。但是那棵树没有结果。
由于这种情况很少发生,因此我不能排除:
我无法在模拟导致这种情况的环境中运行上述任何工具(ASAN,电围栏...)。但是我无法在可以运行这些工具的任何环境中复制它。
我唯一的想法是:
我正在寻找尚未考虑的新颖方法。自动执行此操作的方法,这些类型的问题的更好的工具。您如何验证对象在背后没有变化?
答案 0 :(得分:1)
尽管这个问题很可能会以离题的形式结束,但是您将来可以用来帮助追查根本原因的通用工具是实现内存中的环形缓冲区来记录关键事件。它与常规日志不同,因为日志仅存储到内存中,因此延迟非常短。如果您有足够的内存专用于此日志,那么您应该能够在下一次崩溃时为客户检查它,并更好地了解导致损坏的事件。
一个非常基本的实现是:
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);
}