sublime text 3插件主机崩溃恢复
sublime text 3 plugin host crash recovery
我为 Sublime Text 3 开发了一个插件,我的 python 代码使用 c 类型绑定到 clang。有时调用 libclang 会出现 libclang: crash detected during reparsing
段错误(我还不明白原因,但这与这个问题无关)。这会导致插件主机崩溃。
所以问题是:python 中是否有任何方法可以从底层 c 绑定失败中恢复?我很乐意在遇到崩溃的这个特定文件上跳过此操作。
谢谢!
UPD:评论中进行了简短的讨论,进一步阐述缺乏合适的小型可重现示例是有意义的。这不是因为我的懒惰,我确实试图让我希望得到帮助的人尽可能容易地理解这个问题。但在这种情况下,它真的很难。最初的问题是由 libclang 在一些我还没有确定的奇怪情况下的段错误引起的。它可能与一个库在没有 c++11 支持的情况下编译而另一个在使用 c++11 支持编译时使用它有关,但我想强调 - 这与问题无关。这里的问题是 python 正在调用的东西中存在段错误,这个段错误导致 Sublime Text plugin_host 退出。所以这里有一个简单的例子,但并不是因为缺乏尝试。如果您对如何构建一个有想法,我也愿意接受建议。很抱歉这个问题的质量很差,这是目前我最好的。
根据我掌握的详细信息,我有理由相信您的问题可以归结为 "can Python handle errors that occurred when using the foreign function interface."
我很确定答案是 "no",我整理了以下测试场景来解释原因:
这是我们的测试 C++ 模块(带有一些用于名称修改目的的 C),它会在我们面前爆炸,test.cc
:
#include <iostream>
#include <signal.h>
class Test{
public:
void test(){
std::cout << "Whosebug" << 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
Whosebug
[1] 55992 abort python test.py
这会按预期崩溃 Python,并在 OS X:
上生成一个很好的 "report error" 详细信息
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
我复制并粘贴了它,因为它 cleaner/easier 比 strace 更易于解析(另外,我很懒;)。
对 __kill
的调用是我们崩溃的地方;我们从来没有看到 return 到 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
Whosebug
[1] 56297 abort python test_handle_exception.py
不幸的是,据我所知,我们无法在Python层捕获exception/crash,因为它发生在"beneath"字节码的控制之下。非特定的 Exception
子句将尝试捕获发生的 any 异常,其中以下语句是捕获到异常时采取的操作。 If you're reading this, we survived somehow.
从未发送到标准输出,我们崩溃了,这意味着 Python 没有机会做出反应。
如果可以,请在您的 C++ 代码中处理此异常。您也许可以发挥创意并使用 multiprocessing 分叉到一个可能会崩溃的进程,而无需关闭您的主进程,但我对此表示怀疑。
我为 Sublime Text 3 开发了一个插件,我的 python 代码使用 c 类型绑定到 clang。有时调用 libclang 会出现 libclang: crash detected during reparsing
段错误(我还不明白原因,但这与这个问题无关)。这会导致插件主机崩溃。
所以问题是:python 中是否有任何方法可以从底层 c 绑定失败中恢复?我很乐意在遇到崩溃的这个特定文件上跳过此操作。
谢谢!
UPD:评论中进行了简短的讨论,进一步阐述缺乏合适的小型可重现示例是有意义的。这不是因为我的懒惰,我确实试图让我希望得到帮助的人尽可能容易地理解这个问题。但在这种情况下,它真的很难。最初的问题是由 libclang 在一些我还没有确定的奇怪情况下的段错误引起的。它可能与一个库在没有 c++11 支持的情况下编译而另一个在使用 c++11 支持编译时使用它有关,但我想强调 - 这与问题无关。这里的问题是 python 正在调用的东西中存在段错误,这个段错误导致 Sublime Text plugin_host 退出。所以这里有一个简单的例子,但并不是因为缺乏尝试。如果您对如何构建一个有想法,我也愿意接受建议。很抱歉这个问题的质量很差,这是目前我最好的。
根据我掌握的详细信息,我有理由相信您的问题可以归结为 "can Python handle errors that occurred when using the foreign function interface."
我很确定答案是 "no",我整理了以下测试场景来解释原因:
这是我们的测试 C++ 模块(带有一些用于名称修改目的的 C),它会在我们面前爆炸,test.cc
:
#include <iostream>
#include <signal.h>
class Test{
public:
void test(){
std::cout << "Whosebug" << 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
Whosebug
[1] 55992 abort python test.py
这会按预期崩溃 Python,并在 OS X:
上生成一个很好的 "report error" 详细信息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
我复制并粘贴了它,因为它 cleaner/easier 比 strace 更易于解析(另外,我很懒;)。
对 __kill
的调用是我们崩溃的地方;我们从来没有看到 return 到 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
Whosebug
[1] 56297 abort python test_handle_exception.py
不幸的是,据我所知,我们无法在Python层捕获exception/crash,因为它发生在"beneath"字节码的控制之下。非特定的 Exception
子句将尝试捕获发生的 any 异常,其中以下语句是捕获到异常时采取的操作。 If you're reading this, we survived somehow.
从未发送到标准输出,我们崩溃了,这意味着 Python 没有机会做出反应。
如果可以,请在您的 C++ 代码中处理此异常。您也许可以发挥创意并使用 multiprocessing 分叉到一个可能会崩溃的进程,而无需关闭您的主进程,但我对此表示怀疑。