Dowser: “内部函数的错误参数”

2 投票
1 回答
1741 浏览
提问于 2025-04-15 21:41

我正在尝试使用Dowser,这个工具真的很酷,但我遇到了一点小问题,谷歌上找不到有用的信息,所以我来这里求助了.. ^^;

我在运行一个CherryPy和SQLAlchemy的应用程序.. 一切正常,除了当我启用Dowser(也就是调用dowser.Root()之后),偶尔会出现一些错误,比如:

系统错误: ../Objects/tupleobject.c:809: 内部函数的参数有问题

这个错误出现在一些看起来很简单的操作上,比如访问一个SQLA映射的字段.. 相关的错误追踪信息如下:

  File "/usr/local/lib/python2.6/dist-packages/SQLAlchemy-0.5.8-py2.6.egg/sqlalchemy/orm/attributes.py", line 158, in __get__
    return self.impl.get(instance_state(instance), instance_dict(instance))
  File "/usr/local/lib/python2.6/dist-packages/SQLAlchemy-0.5.8-py2.6.egg/sqlalchemy/orm/attributes.py", line 377, in get
    value = callable_()
  File "/usr/local/lib/python2.6/dist-packages/SQLAlchemy-0.5.8-py2.6.egg/sqlalchemy/orm/strategies.py", line 586, in __call__
    result = q.all()
  File "/usr/local/lib/python2.6/dist-packages/SQLAlchemy-0.5.8-py2.6.egg/sqlalchemy/orm/query.py", line 1267, in all
    return list(self)
  File "/usr/local/lib/python2.6/dist-packages/SQLAlchemy-0.5.8-py2.6.egg/sqlalchemy/orm/query.py", line 1422, in instances
    rows = [process[0](context, row) for row in fetch]
  File "/usr/local/lib/python2.6/dist-packages/SQLAlchemy-0.5.8-py2.6.egg/sqlalchemy/orm/query.py", line 2032, in main
    return _instance(row, None)
  File "/usr/local/lib/python2.6/dist-packages/SQLAlchemy-0.5.8-py2.6.egg/sqlalchemy/orm/mapper.py", line 1653, in _instance
    identitykey = identity_key(row)
  File "/usr/local/lib/python2.6/dist-packages/SQLAlchemy-0.5.8-py2.6.egg/sqlalchemy/orm/mapper.py", line 1594, in identity_key
    return (identity_class, tuple(row[column] for column in pk_cols))

这可能和Dowser的线程有关,它在访问垃圾回收器吗?你们有什么建议我可以检查的地方吗?

我在Xubuntu Jaunty上运行Python 2.6.2。

谢谢大家的关注!

1 个回答

1

摘要

在我的例子中是 Dozer,而不是 Dowser,但基本上是同样的事情——在一个单独的线程中循环遍历 gc.get_objects 并统计类型。还可以选择互动地追踪所选对象的引用者和被引用者。

正如我在评论中提到的,这个错误仍然会发生。在使用 SQLAlchemy 时,我遇到了两次 SystemError: ../Objects/tupleobject.c:892: bad argument to internal function 的堆栈跟踪错误,这个错误是在元组推导中出现的(一个, )。这里有一个合理的解释。

解释

如果我查看 CPython 的 tupleobject.c 源码 第892行,在这个函数之前有一个显著的注释:

以下函数打破了元组不可变的概念:它改变了元组的大小。只有当只有一个模块引用这个对象时,我们才能做到这一点。你也可以把它看作是创建一个新的元组对象并销毁旧的,只是更高效而已。无论如何,如果这个元组可能已经被代码的其他部分知道,就不要使用这个。

假设:

  • 当 SQLAlchemy 在主线程中构建复杂的元组推导时,
  • Dozer 从另一个线程获取了对“正在进行”的元组的引用,
  • 并打破了 _PyTuple_Resize 的假设(即非空元组只有一个引用者)
    if (v == NULL || Py_TYPE(v) != &PyTuple_Type ||
        (Py_SIZE(v) != 0 && Py_REFCNT(v) != 1)) {
        *pv = 0;
        Py_XDECREF(v);
        PyErr_BadInternalCall();  // line 892
        return -1;
    }

总结

Dozer 是一个很棒的实时内存检查工具,但它有一个局限性,就是被检查的应用程序在使用 Dozer 的情况下可能无法长时间运行(例如,能运行几个小时)。因此,更实际的做法是等到内存泄漏显现出来后,再 实时连接 Dozer 到进程中。

撰写回答