【问题标题】:sublime text 3 plugin host crash recovery [closed]sublime text 3插件主机崩溃恢复[关闭]
【发布时间】:2016-05-19 11:05:06
【问题描述】:

我为 Sublime Text 3 开发了一个插件,我的 python 代码使用 c 类型绑定来 clang。有时调用 libclang 会出现libclang: crash detected during reparsing 的段错误(我还不明白原因,但这与这个问题无关)。这会导致插件主机崩溃。

所以问题是:python 中是否有任何方法可以从底层 c 绑定的失败中恢复?我很乐意在遇到崩溃的这个特定文件上跳过此操作。

谢谢!

UPD:在 cmets 中进行了简短的讨论,进一步详细说明缺乏适当的小型可重现示例是有意义的。这不是我懒惰的原因,我确实努力让我希望帮助的人尽可能容易地理解这个问题。但在这种情况下,这真的很难。最初的问题是由 libclang 段错误在一些我还没有确定的奇怪情况下引起的。它可能与一个在不支持 c++11 的情况下编译的库和另一个在使用 c++11 支持的情况下编译的库有关,但我想强调 - 这与问题无关。这里的问题是 python 调用的东西有一个段错误,这个段错误导致 Sublime Text plugin_host 退出。所以这里有一个简单的例子,但不是因为缺乏尝试。如果您有如何构建一个的想法,我也愿意接受建议。很抱歉这个问题的质量很差,这是我目前最好的。

【问题讨论】:

  • 寻求调试帮助的问题(“为什么这段代码不起作用?”)必须包括所需的行为、特定问题或错误在问题本身中重现它所需的最短代码。没有明确的问题陈述的问题对其他读者没有用处。请参阅:How to create a Minimal, Complete, and Verifiable Example
  • @MattDMo 谢谢你的建议,我总是试着做一个简单的例子,但在这种情况下我不知道怎么做。我不是 python 的期望,所以很难创建一个会失败的例子。我只知道如果从 python 使用的 c 库以段错误结束,它应该会发生。我很抱歉这个问题的质量很差,但我不能做得更好,所以欢迎任何建议!
  • 我有点怀疑“错误是什么?”与“如何从错误中恢复?”无关。或者缺少任何细节是必要的。
  • @AndrasDeak 这是一个有效的观点。我很乐意提供详细信息,但失败的原始问题是与 libclang 相关的另一个深奥问题。它连接到一个正在使用 c++11 支持编译的库,然后在使用 c++11 编译代码时尝试使用该库。至少这是我目前最好的猜测。所以我很难更好地描述这个。我明白这听起来很粗略,但这就是我现在所拥有的一切,而不是因为缺乏尝试。最终,从 python 调用的 c 代码中存在段错误。
  • @niosus 是否有任何方法可以为其他人提供重现此问题的方法?我认为这是问题的根本问题 - 我们很乐意为您提供帮助,但如果无法重现您所看到的内容,我们就无法做到,而且这个问题离题了。

标签: python sublimetext3 sublimetext sublime-text-plugin libclang


【解决方案1】:

根据我所掌握的细节,我有理由确定您的问题归结为“Python 能否处理使用外部函数接口时发生的错误”。

我很确定答案是否定的,我整理了以下测试场景来解释原因:

这是我们的测试 C++ 模块(带有一些 C 用于名称修改),它将在我们面前爆炸,test.cc

#include <iostream>
#include <signal.h>

class Test{
    public:
        void test(){
            std::cout << "stackoverflow" << std::endl;
            // this will crash us. shouldn't really matter what SIG as long as it crashes Python
            raise (SIGABRT);
        }
};


extern "C" {
    Test* Test_new(){ return new Test(); }
    void Test_example(Test* test){ test->test(); }
}

clang -shared -undefined dynamic_lookup -o test.so test.cc

还有我们的调用脚本,test.py:

from ctypes import cdll

test_so = cdll.LoadLibrary("test.so")

class PyTest:
    def __init__(self):
        self.obj = test_so.Test_new()

    def output(self):
        test_so.Test_example(self.obj)

if __name__ == "__main__":
    p = PyTest()
    p.output()

叫它:

Ξ /tmp/29_may → python test.py
stackoverflow
[1]    55992 abort      python test.py

这会按预期使 Python 崩溃,并在 OS X 上生成一个不错的“报告错误”详细信息:

Crashed Thread:        0  Dispatch queue: com.apple.main-thread

Exception Type:        EXC_CRASH (SIGABRT)
Exception Codes:       0x0000000000000000, 0x0000000000000000

Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0   libsystem_kernel.dylib          0x00007fff95bf48ea __kill + 10
1   test.so                         0x0000000110285006 Test::test() + 70
2   test.so                         0x0000000110284fb5 Test_example + 21
3   _ctypes.so                      0x000000011026d7c7 ffi_call_unix64 + 79
4   _ctypes.so                      0x000000011026dfe6 ffi_call + 818
5   _ctypes.so                      0x000000011026970b _ctypes_callproc + 867
6   _ctypes.so                      0x0000000110263b91 PyCFuncPtr_call + 1100
7   org.python.python               0x000000010fd18ad7 PyObject_Call + 99
8   org.python.python               0x000000010fd94e7f PyEval_EvalFrameEx + 11417
9   org.python.python               0x000000010fd986d1 fast_function + 262
10  org.python.python               0x000000010fd95553 PyEval_EvalFrameEx + 13165
11  org.python.python               0x000000010fd91fb4 PyEval_EvalCodeEx + 1387
12  org.python.python               0x000000010fd91a43 PyEval_EvalCode + 54
13  org.python.python               0x000000010fdb1816 run_mod + 53
14  org.python.python               0x000000010fdb18b9 PyRun_FileExFlags + 133
15  org.python.python               0x000000010fdb13f9 PyRun_SimpleFileExFlags + 711
16  org.python.python               0x000000010fdc2e09 Py_Main + 3057
17  libdyld.dylib                   0x00007fff926d15ad start + 1

我复制并粘贴了这个,因为它比 strace 更干净/更容易解析(另外,我很懒;)。 对__kill 的调用是我们崩溃的地方;我们再也看不到 Python 的回归,这意味着它超出了我们的控制范围。

为了证明这一点,将我们的test.py 修改为test_handle_exception.py 以尝试捕获异常:

from ctypes import cdll

test_so = cdll.LoadLibrary("test.so")

class PyTest:
    def __init__(self):
        self.obj = test_so.Test_new()

    def output(self):
        test_so.Test_example(self.obj)

if __name__ == "__main__":
    p = PyTest()

    try:
        p.output()
    except:
        print("If you're reading this, we survived somehow.")

然后再次运行它:

Ξ /tmp/29_may → python test_handle_exception.py
stackoverflow
[1]    56297 abort      python test_handle_exception.py

不幸的是,据我所知,我们无法在 Python 层捕获异常/崩溃,因为它发生在字节码控制“之下”。非特定的Exception 子句将尝试捕获发生的any 异常,其中以下语句是捕获异常时采取的操作。 If you're reading this, we survived somehow. 从未发送到标准输出,我们崩溃了,这意味着 Python 没有机会做出反应。

如果可以,请在 C++ 代码中处理此异常。您也许可以发挥创造力并使用multiprocessing 分叉一个进程,该进程可能会在不关闭主进程的情况下崩溃,但我对此表示怀疑。

【讨论】:

  • 太棒了,这是我希望的例子。我以某种方式尝试通过取消引用 NULL 来做到这一点,但这似乎不起作用。没有考虑加薪。所以你说,我们不能对在 python 下失败的代码做太多事情,对吧?
  • @niosus 很高兴我能帮助你。我会遵循 Andras Deak 和 MattDmo 的 cmets 中的建议,因为可能有一些特定于您的场景的东西可以帮助某人帮助您。回复:在 Python 层处理失败的 C/C++ 代码:不,不幸的是,据我所知,Python 没有机会处理异常。如果您追踪导致段错误的原因,您可能会编写一个 C++ 函数来处理该错误(实际上:可能不会),但这可能比解决根本原因更努力。
  • 我相信,尽管问题的质量很差,但您还是找到了原因并提供了我应该首先提供的示例。这绝对是我的问题的答案,即使它是负面的。非常感谢。至于根本原因,我要么想办法解决它,要么在这里展示它来询问它。现在它对我来说太深奥了,并不总是发生。
  • 很高兴我能帮上忙——有时很难知道要包含哪些细节。祝你的插件好运。
  • @niosus(和 tristan)不幸的是,Sublime Text 3 用于运行插件代码的 Python 内部版本不包含 multiprocessing 模块,因此该建议是不可行的。但是,它确实包含threading,并且plugin_host(通常)是线程安全的,因此可能是一种方法。但是,正如答案中提到的,您可能只需要确定发生段错误的位置并在 C++ 中添加适当的错误处理。
猜你喜欢
  • 1970-01-01
  • 2015-06-18
  • 2014-02-13
  • 2021-10-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-04-29
  • 1970-01-01
相关资源
最近更新 更多