-->

按Ctrl-C导入scipy.stats后崩溃的Python(Ctrl-C crashes Pyth

2019-07-21 19:32发布

我在Win7上64位运行64位的Python 2.7.3。 我可以这样做可靠的崩溃Python解释器:

>>> from scipy import stats
>>> import time
>>> time.sleep(3)

和睡眠期间按下Control-C。 一个一个KeyboardInterrupt不提高; 解释器崩溃。 以下是印刷:

forrtl: error (200): program aborting due to control-C event
Image              PC                Routine            Line        Source

libifcoremd.dll    00000000045031F8  Unknown               Unknown  Unknown
libifcoremd.dll    00000000044FC789  Unknown               Unknown  Unknown
libifcoremd.dll    00000000044E8583  Unknown               Unknown  Unknown
libifcoremd.dll    000000000445725D  Unknown               Unknown  Unknown
libifcoremd.dll    00000000044672A6  Unknown               Unknown  Unknown
kernel32.dll       0000000077B74AF3  Unknown               Unknown  Unknown
kernel32.dll       0000000077B3F56D  Unknown               Unknown  Unknown
ntdll.dll          0000000077C73281  Unknown               Unknown  Unknown

这使得它不可能中断长时间运行SciPy的计算。

谷歌搜索“forrtl”之类的,我看到的建议,这种问题是由于使用了重写按Ctrl-C处理一个Fortran库。 我没有看到trackerbut给出的SciPy的一个错误,SciPy的是与Python的使用图书馆,我会认为这是一个错误。 它打破了按Ctrl-C的Python的处理。 对此有什么解决办法?

编辑:继@ cgohlke的建议我尝试导入SciPy的后添加自己的处理程序。 这个问题有关相关问题的说明将信号处理程序不起作用。 我尝试使用Windows API SetConsoleCtrlHandler通过pywin32功能:

from scipy import stats
import win32api
def doSaneThing(sig, func=None):
    print "Here I am"
    raise KeyboardInterrupt
win32api.SetConsoleCtrlHandler(doSaneThing, 1)

在此之后,按下Ctrl-C打印“我在这里”,但是Python仍与forrtl错误崩溃。 有时候,我也得到一个消息,说“ConsoleCtrlHandler功能失败”,很快就会消失。

如果我在IPython中运行这个,我可以看到forrtl错误之前正常的Python一个KeyboardInterrupt回溯。 我还看到一个普通的Python回溯其次forrtl错误,如果我提出了一些其他的错误,而不是一个KeyboardInterrupt(如ValueError异常):

ValueError                                Traceback (most recent call last)
<ipython-input-1-08defde66fcb> in doSaneThing(sig, func)
      3 def doSaneThing(sig, func=None):
      4     print "Here I am"
----> 5     raise ValueError
      6 win32api.SetConsoleCtrlHandler(doSaneThing, 1)

ValueError:
forrtl: error (200): program aborting due to control-C event
[etc.]

看来,不管底层的处理程序是干什么的,它不只是直接捕获按Ctrl-C,但反应错误条件(ValueError异常)和崩溃本身。 有什么办法来消除这个?

Answer 1:

这里是你的贴的解决方案,可能工作的变化。 也许有一个更好的办法来解决这个问题 - 或者甚至通过设置环境变量,告诉DLL跳过安装一个处理程序避免这一切在一起。 希望这有助于直到你找到一个更好的办法。

两个time模块 (线868-876)和_multiprocessing模块 (线312-321)调用SetConsoleCtrlHandler 。 在的情况下, time模块,其控制台控制处理程序设置一个Windows事件, hInterruptEvent 。 主线程, time.sleep上从这个事件等待WaitForSingleObject(hInterruptEvent, ul_millis)其中ul_millis是毫秒睡觉除非按Ctrl + C中断的数量。 既然你已经安装了处理程序返回Truetime模块的处理程序不会被调用设置hInterruptEvent ,这意味着sleep不能被打断。

我试着用imp.init_builtin('time')重新初始化time模块,但显然SetConsoleCtrlHandler忽略第二个呼叫。 这似乎处理程序已被删除,然后重新插入。 不幸的是, time模块不导出函数,该函数。 所以,作为一个杂牌,只要确保你导入time模块安装的处理程序之后 。 由于进口scipy也是进口time ,你需要预先加载libifcoremd.dll使用ctypes ,以获得正确的顺序处理。 最后,添加调用thread.interrupt_main确保Python的SIGINT处理程序被调用[1]。

例如:

import os
import imp
import ctypes
import thread
import win32api

# Load the DLL manually to ensure its handler gets
# set before our handler.
basepath = imp.find_module('numpy')[1]
ctypes.CDLL(os.path.join(basepath, 'core', 'libmmd.dll'))
ctypes.CDLL(os.path.join(basepath, 'core', 'libifcoremd.dll'))

# Now set our handler for CTRL_C_EVENT. Other control event 
# types will chain to the next handler.
def handler(dwCtrlType, hook_sigint=thread.interrupt_main):
    if dwCtrlType == 0: # CTRL_C_EVENT
        hook_sigint()
        return 1 # don't chain to the next handler
    return 0 # chain to the next handler

win32api.SetConsoleCtrlHandler(handler, 1)

>>> import time
>>> from scipy import stats
>>> time.sleep(10)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyboardInterrupt

[1] interrupt_main调用PyErr_SetInterrupt 。 该车次Handlers[SIGINT]并呼吁Py_AddPendingCall添加checksignals_witharg 。 反过来,这要求PyErr_CheckSignals 。 由于Handlers[SIGINT]跳闸,这就要求Handlers[SIGINT].func 。 最后,如果funcsignal.default_int_handler ,你会得到一个KeyboardInterrupt异常。



Answer 2:

我已经能够通过这样做是为了得到一个半的解决方法:

from scipy import stats
import win32api
def doSaneThing(sig, func=None):
    return True
win32api.SetConsoleCtrlHandler(doSaneThing, 1)

返回的句柄真正停止处理程序链,以便插手Fortran语言处理程序不再被调用。 然而,这种解决方法仅仅是局部的,原因有二:

  1. 它实际上并没有提出一个一个KeyboardInterrupt,这意味着我不能对其做出反应的Python代码。 它只是降低我回提示。
  2. 它不完全中断在按Ctrl-C在Python通常的方式做事情。 如果在一个新的Python会话我做了time.sleep(3)然后按Ctrl-C,睡眠被立即中止,我得到了一个KeyboardInterrupt。 与上面的解决方法,睡眠不中止,并且控制返回到提示睡眠时间后才是向上。

尽管如此,这仍然比崩溃的整个会话越好。 对我来说,这引起了为什么SciPy的(以及依赖于这些英特尔库中的任何其他Python库)不这样做他们自己的问题。

我要离开这个答案,希望有人能提供真正的解决方案或替代方法未被接受。 通过“真正的”我的意思是一个长期运行的SciPy的计算过程中按下Ctrl-C应该工作就像它没有加载SciPy的时候。 (请注意,这并不意味着它必须立即开展工作,非SciPy的计算像普通的Python sum(xrange(100000000))可能不会立即中止上按Ctrl-C,但至少当他们这样做,他们养一个KeyboardInterrupt。)



Answer 3:

这里的代码打补丁的DLL来删除安装按Ctrl-C处理程序调用:

import os
import os.path
import imp
import hashlib

basepath = imp.find_module('numpy')[1]
ifcoremd = os.path.join(basepath, 'core', 'libifcoremd.dll')
with open(ifcoremd, 'rb') as dll:
    contents = dll.read()

m = hashlib.md5()
m.update(contents)

patch = {'7cae928b035bbdb90e4bfa725da59188': (0x317FC, '\xeb\x0b'),
  '0f86dcd44a1c2e217054c50262f727bf': (0x3fdd9, '\xeb\x10')}[m.hexdigest()]
if patch:
    contents = bytearray(contents)
    contents[patch[0]:patch[0] + len(patch[1])] = patch[1]
    with open(ifcoremd, 'wb') as dll:
        dll.write(contents)
else:
    print 'Unknown dll version'

编辑:这是我如何添加补丁用于x64。 在调试器中运行python.exe,并设置一个断点SetConsoleCtrlHandler直到你得到你想要修补了电话:

Microsoft (R) Windows Debugger Version 6.12.0002.633 AMD64
Copyright (c) Microsoft Corporation. All rights reserved.

CommandLine: .\venv\Scripts\python.exe
...
0:000> .symfix
0:000> bp kernel32!SetConsoleCtrlHandler
0:000> g
Breakpoint 0 hit
KERNEL32!SetConsoleCtrlHandler:
00007ffc`c25742f0 ff252af00400    jmp     qword ptr [KERNEL32!_imp_SetConsoleCtrlHandler (00007ffc`c25c3320)] ds:00007ffc`c25c3320={KERNELBASE!SetConsoleCtrlHandler (00007ffc`bfa12e10)}
0:000> k 5
Child-SP          RetAddr           Call Site
00000000`007ef7a8 00000000`71415bb4 KERNEL32!SetConsoleCtrlHandler
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\SYSTEM32\python27.dll -
00000000`007ef7b0 00000000`7035779f MSVCR90!signal+0x17c
00000000`007ef800 00000000`70237ea7 python27!PyOS_getsig+0x3f
00000000`007ef830 00000000`703546cc python27!Py_Main+0x21ce7
00000000`007ef880 00000000`7021698c python27!Py_InitializeEx+0x40c
0:000> g
Python 2.7.11 (v2.7.11:6d1b6a68f775, Dec  5 2015, 20:40:30) [MSC v.1500 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import numpy
...
Breakpoint 0 hit
KERNEL32!SetConsoleCtrlHandler:
00007ffc`c25742f0 ff252af00400    jmp     qword ptr [KERNEL32!_imp_SetConsoleCtrlHandler (00007ffc`c25c3320)] ds:00007ffc`c25c3320={KERNELBASE!SetConsoleCtrlHandler (00007ffc`bfa12e10)}
0:000> k 5
Child-SP          RetAddr           Call Site
00000000`007ec308 00000000`7023df6e KERNEL32!SetConsoleCtrlHandler
00000000`007ec310 00000000`70337877 python27!PyTime_DoubleToTimet+0x10ee
00000000`007ec350 00000000`7033766d python27!PyImport_IsScript+0x4f7
00000000`007ec380 00000000`70338bf2 python27!PyImport_IsScript+0x2ed
00000000`007ec3b0 00000000`703385a9 python27!PyImport_ImportModuleLevel+0xc82
0:000> g
...
>>> import scipy.stats
...
Breakpoint 0 hit
KERNEL32!SetConsoleCtrlHandler:
00007ffc`c25742f0 ff252af00400    jmp     qword ptr [KERNEL32!_imp_SetConsoleCtrlHandler (00007ffc`c25c3320)] ds:00007ffc`c25c3320={KERNELBASE!SetConsoleCtrlHandler (00007ffc`bfa12e10)}
0:000> k 5
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Users\kevin\Documents\\venv\lib\site-packages\numpy\core\libifcoremd.dll -
Child-SP          RetAddr           Call Site
00000000`007ed818 00007ffc`828309eb KERNEL32!SetConsoleCtrlHandler
00000000`007ed820 00007ffc`828dfa44 libifcoremd!GETEXCEPTIONPTRSQQ+0xdb
00000000`007ed880 00007ffc`828e59d7 libifcoremd!for_lt_ne+0xc274
00000000`007ed8b0 00007ffc`828e5aff libifcoremd!for_lt_ne+0x12207
00000000`007ed8e0 00007ffc`c292ddc7 libifcoremd!for_lt_ne+0x1232f
0:000> ub  00007ffc`828309eb
libifcoremd!GETEXCEPTIONPTRSQQ+0xbb:
00007ffc`828309cb 00e8            add     al,ch
00007ffc`828309cd df040b          fild    word ptr [rbx+rcx]
00007ffc`828309d0 0033            add     byte ptr [rbx],dh
00007ffc`828309d2 c9              leave
00007ffc`828309d3 ff15bf390e00    call    qword ptr [libifcoremd!for_lt_ne+0x40bc8 (00007ffc`82914398)]
00007ffc`828309d9 488d0d00efffff  lea     rcx,[libifcoremd!for_rtl_finish_+0x20 (00007ffc`8282f8e0)]
00007ffc`828309e0 ba01000000      mov     edx,1
00007ffc`828309e5 ff158d390e00    call    qword ptr [libifcoremd!for_lt_ne+0x40ba8 (00007ffc`82914378)]

我们将修补了lea指令使用相对jmp (这是0xeb之后是字节跳数)

0:000> ? 00007ffc`828309eb - 00007ffc`828309d9
Evaluate expression: 18 = 00000000`00000012
0:000> f 00007ffc`828309d9 L2 eb 10
Filled 0x2 bytes
0:000> ub  00007ffc`828309eb
libifcoremd!GETEXCEPTIONPTRSQQ+0xbe:
00007ffc`828309ce 040b            add     al,0Bh
00007ffc`828309d0 0033            add     byte ptr [rbx],dh
00007ffc`828309d2 c9              leave
00007ffc`828309d3 ff15bf390e00    call    qword ptr [libifcoremd!for_lt_ne+0x40bc8 (00007ffc`82914398)]
00007ffc`828309d9 eb10            jmp     libifcoremd!GETEXCEPTIONPTRSQQ+0xdb (00007ffc`828309eb)
00007ffc`828309db 0d00efffff      or      eax,0FFFFEF00h
00007ffc`828309e0 ba01000000      mov     edx,1
00007ffc`828309e5 ff158d390e00    call    qword ptr [libifcoremd!for_lt_ne+0x40ba8 (00007ffc`82914378)]

我不知道该.dll文件是如何在这个过程中映射的,所以我就搜索0d 00 ef ff ff用十六进制编辑的文件中。 它是一个独特的打击,所以我们可以在该.dll补丁计算的位置。

0:000> db  00007ffc`828309d0
00007ffc`828309d0  00 33 c9 ff 15 bf 39 0e-00 eb 10 0d 00 ef ff ff  .3....9.........
00007ffc`828309e0  ba 01 00 00 00 ff 15 8d-39 0e 00 48 8d 0d 0e 9c  ........9..H....
00007ffc`828309f0  09 00 e8 09 2e 0a 00 48-8d 0d 32 9f 09 00 e8 fd  .......H..2.....
00007ffc`82830a00  2d 0a 00 48 8d 0d ca ee-0e 00 e8 51 90 00 00 85  -..H.......Q....
00007ffc`82830a10  c0 0f 85 88 02 00 00 e8-38 fa 0a 00 ff 15 4e 39  ........8.....N9
00007ffc`82830a20  0e 00 89 c1 e8 d7 2d 0a-00 48 8d 05 f8 be 11 00  ......-..H......
00007ffc`82830a30  45 32 e4 c7 05 0b 4a 13-00 00 00 00 00 41 bd 01  E2....J......A..
00007ffc`82830a40  00 00 00 48 89 05 06 4a-13 00 ff 15 30 39 0e 00  ...H...J....09..
0:000> ? 00007ffc`828309d9 -  00007ffc`828309d0
Evaluate expression: 9 = 00000000`00000009
0:000> ? 00007ffc`828309d9 -  00007ffc`828309d0 + 3FDD0
Evaluate expression: 261593 = 00000000`0003fdd9
0:000>

好吧,我在打补丁的DLL 0x3fdd9 。 让我们来看看是什么样子现在:

Microsoft (R) Windows Debugger Version 6.12.0002.633 AMD64
Copyright (c) Microsoft Corporation. All rights reserved.

CommandLine: .\venv\Scripts\python.exe
...
0:000> bp libifcoremd!GETEXCEPTIONPTRSQQ+c9
Bp expression 'libifcoremd!GETEXCEPTIONPTRSQQ+c9' could not be resolved, adding deferred bp
0:000> g
Python 2.7.11 (v2.7.11:6d1b6a68f775, Dec  5 2015, 20:40:30) [MSC v.1500 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import scipy.stats
...
Breakpoint 0 hit
libifcoremd!GETEXCEPTIONPTRSQQ+0xc9:
00007ffc`845909d9 eb10            jmp     libifcoremd!GETEXCEPTIONPTRSQQ+0xdb (00007ffc`845909eb)
0:000> u
libifcoremd!GETEXCEPTIONPTRSQQ+0xc9:
00007ffc`845909d9 eb10            jmp     libifcoremd!GETEXCEPTIONPTRSQQ+0xdb (00007ffc`845909eb)
00007ffc`845909db 0d00efffff      or      eax,0FFFFEF00h
00007ffc`845909e0 ba01000000      mov     edx,1
00007ffc`845909e5 ff158d390e00    call    qword ptr [libifcoremd!for_lt_ne+0x40ba8 (00007ffc`84674378)]
00007ffc`845909eb 488d0d0e9c0900  lea     rcx,[libifcoremd!GETHANDLEQQ (00007ffc`8462a600)]
00007ffc`845909f2 e8092e0a00      call    libifcoremd!for_lt_ne+0x30 (00007ffc`84633800)
00007ffc`845909f7 488d0d329f0900  lea     rcx,[libifcoremd!GETUNITQQ (00007ffc`8462a930)]
00007ffc`845909fe e8fd2d0a00      call    libifcoremd!for_lt_ne+0x30 (00007ffc`84633800)
0:000>

所以,现在是被jmp荷兰国际集团在推栈和函数调用的参数。 因此,它按Ctrl-C处理程序将不会安装。



Answer 4:

环境变量设置FOR_DISABLE_CONSOLE_CTRL_HANDLER1 似乎解决这个问题 。

编辑 :尽管按Ctrl + C不会崩溃蟒蛇了,它也不能停止当前的计算。



Answer 5:

解决方法:补丁SetControlCtrlHandler

import ctypes
SetConsoleCtrlHandler_body_new = b'\xC2\x08\x00' if ctypes.sizeof(ctypes.c_void_p) == 4 else b'\xC3'
try: SetConsoleCtrlHandler_body = (lambda kernel32: (lambda pSetConsoleCtrlHandler:
    kernel32.VirtualProtect(pSetConsoleCtrlHandler, ctypes.c_size_t(1), 0x40, ctypes.byref(ctypes.c_uint32(0)))
    and (ctypes.c_char * 3).from_address(pSetConsoleCtrlHandler.value)
)(ctypes.cast(kernel32.SetConsoleCtrlHandler, ctypes.c_void_p)))(ctypes.windll.kernel32)
except: SetConsoleCtrlHandler_body = None
if SetConsoleCtrlHandler_body:
    SetConsoleCtrlHandler_body_old = SetConsoleCtrlHandler_body[0:len(SetConsoleCtrlHandler_body_new)]
    SetConsoleCtrlHandler_body[0:len(SetConsoleCtrlHandler_body_new)] = SetConsoleCtrlHandler_body_new
try:
    import scipy.stats
finally:
    if SetConsoleCtrlHandler_body:
        SetConsoleCtrlHandler_body[0:len(SetConsoleCtrlHandler_body_new)] = SetConsoleCtrlHandler_body_old


Answer 6:

尝试

import os
os.environ['FOR_IGNORE_EXCEPTIONS'] = '1'
import scipy.stats


文章来源: Ctrl-C crashes Python after importing scipy.stats