Лучший способ обнаружить сбой приложения и перезапустить его? - PullRequest
9 голосов
/ 17 сентября 2008

Каков наилучший способ обнаружить сбой приложения в XP (каждый раз выдает одну и ту же пару окон с ошибками - каждое с одинаковым заголовком окна), а затем перезапустить его?

Мне особенно интересно услышать о решениях, которые используют минимальные системные ресурсы, поскольку рассматриваемая система довольно старая.

Я думал об использовании языка сценариев, такого как AutoIt (http://www.autoitscript.com/autoit3/),, и, возможно, запуск сценария «детектора» каждые несколько минут?

Будет ли это лучше сделать на Python, Perl, PowerShell или чем-то еще целиком?

Любые идеи, советы или мысли высоко ценятся.

РЕДАКТИРОВАТЬ: На самом деле не происходит сбой (т.е. выход / завершение - спасибо @tialaramex). Он отображает диалоговое окно, ожидающее пользовательского ввода, затем другое диалоговое окно, ожидающее дальнейшего пользовательского ввода, а затем фактически завершается. Именно эти диалоги я бы хотел обнаружить и разобраться.

Ответы [ 5 ]

12 голосов
/ 17 сентября 2008

Лучший способ - использовать именованный мьютекс .

  1. Запустите ваше приложение.
  2. Создайте новый именованный мьютекс и вступите во владение им
  3. Запустите новый процесс (процесс, а не поток) или новое приложение, что вы предпочитаете.
  4. Из этого процесса / приложения попытайтесь получить мьютекс. Процесс заблокирует
  5. Когда приложение завершит работу, освободите мьютекс (подайте сигнал)
  6. «Управляющий» процесс получит мьютекс только в том случае, если приложение завершит работу или произойдет сбой приложения.
  7. Проверка полученного состояния после получения мьютекса. Если приложение потерпело крах, оно будет WAIT_ABANDONED

Объяснение: Когда поток завершает работу, не освобождая мьютекс, любой другой процесс, ожидающий его, может получить его, но он получит WAIT_ABANDONED в качестве возвращаемого значения, что означает, что мьютекс отменен и, следовательно, состояние раздела это было защищено, может быть небезопасно.

Таким образом, ваше второе приложение не будет потреблять циклов ЦП, поскольку будет продолжать ожидать мьютекс (и это полностью обрабатывается операционной системой)

3 голосов
/ 24 апреля 2009

Я думаю, что главная проблема в том, что доктор Ватсон отображает диалог и поддерживает ваш процесс в живых.

Вы можете написать свой собственный отладчик, используя Windows API и запустите сбой приложения оттуда. Это не позволит другим отладчикам поймать сбой ваше приложение, и вы также можете перехватить событие Exception.

Поскольку я не нашел пример кода, я написал это Python быстрый и грязный образец. Я не уверен, насколько он крепок особенно можно улучшить объявление DEBUG_EVENT.

from ctypes import windll, c_int, Structure
import subprocess

WaitForDebugEvent = windll.kernel32.WaitForDebugEvent    
ContinueDebugEvent = windll.kernel32.ContinueDebugEvent
DBG_CONTINUE = 0x00010002L    
DBG_EXCEPTION_NOT_HANDLED = 0x80010001L

event_names = {    
    3: 'CREATE_PROCESS_DEBUG_EVENT',
    2: 'CREATE_THREAD_DEBUG_EVENT',
    1: 'EXCEPTION_DEBUG_EVENT',
    5: 'EXIT_PROCESS_DEBUG_EVENT',
    4: 'EXIT_THREAD_DEBUG_EVENT',
    6: 'LOAD_DLL_DEBUG_EVENT',
    8: 'OUTPUT_DEBUG_STRING_EVENT', 
    9: 'RIP_EVENT',
    7: 'UNLOAD_DLL_DEBUG_EVENT',
}
class DEBUG_EVENT(Structure):
    _fields_ = [
        ('dwDebugEventCode', c_int),
        ('dwProcessId', c_int),
        ('dwThreadId', c_int),
        ('u', c_int*20)]

def run_with_debugger(args):
    proc = subprocess.Popen(args, creationflags=1)
    event = DEBUG_EVENT()

    while True:
        if WaitForDebugEvent(pointer(event), 10):
            print event_names.get(event.dwDebugEventCode, 
                    'Unknown Event %s' % event.dwDebugEventCode)
            ContinueDebugEvent(event.dwProcessId, event.dwThreadId, DBG_CONTINUE)
        retcode = proc.poll()
        if retcode is not None:
            return retcode

run_with_debugger(['python', 'crash.py'])
3 голосов
/ 17 сентября 2008

Как насчет создания приложения-оболочки, которое запускает неисправное приложение в детстве и ждет его? Если код выхода дочернего элемента указывает на ошибку, перезапустите ее, иначе выйдите.

2 голосов
/ 04 июня 2009

Вот немного улучшенная версия.

В моем тесте предыдущий код выполнялся в бесконечном цикле, когда неисправный exe генерировал «нарушение доступа».

Я не совсем удовлетворен своим решением, потому что у меня нет четких критериев, чтобы знать, какое исключение следует продолжать, а какое - нет (ExceptionFlags не поможет).

Но это работает на примере, который я запускаю.

Надеюсь, это поможет, Вивиан Де Смедт

from ctypes import windll, c_uint, c_void_p, Structure, Union, pointer
import subprocess

WaitForDebugEvent = windll.kernel32.WaitForDebugEvent
ContinueDebugEvent = windll.kernel32.ContinueDebugEvent
DBG_CONTINUE = 0x00010002L
DBG_EXCEPTION_NOT_HANDLED = 0x80010001L

event_names = {
    1: 'EXCEPTION_DEBUG_EVENT',
    2: 'CREATE_THREAD_DEBUG_EVENT',
    3: 'CREATE_PROCESS_DEBUG_EVENT',
    4: 'EXIT_THREAD_DEBUG_EVENT',
    5: 'EXIT_PROCESS_DEBUG_EVENT',
    6: 'LOAD_DLL_DEBUG_EVENT',
    7: 'UNLOAD_DLL_DEBUG_EVENT',
    8: 'OUTPUT_DEBUG_STRING_EVENT',
    9: 'RIP_EVENT',
}

EXCEPTION_MAXIMUM_PARAMETERS = 15

EXCEPTION_DATATYPE_MISALIGNMENT  = 0x80000002
EXCEPTION_ACCESS_VIOLATION       = 0xC0000005
EXCEPTION_ILLEGAL_INSTRUCTION    = 0xC000001D
EXCEPTION_ARRAY_BOUNDS_EXCEEDED  = 0xC000008C
EXCEPTION_INT_DIVIDE_BY_ZERO     = 0xC0000094
EXCEPTION_INT_OVERFLOW           = 0xC0000095
EXCEPTION_STACK_OVERFLOW         = 0xC00000FD


class EXCEPTION_DEBUG_INFO(Structure):
    _fields_ = [
        ("ExceptionCode", c_uint),
        ("ExceptionFlags", c_uint),
        ("ExceptionRecord", c_void_p),
        ("ExceptionAddress", c_void_p),
        ("NumberParameters", c_uint),
        ("ExceptionInformation", c_void_p * EXCEPTION_MAXIMUM_PARAMETERS),
    ]

class EXCEPTION_DEBUG_INFO(Structure):
    _fields_ = [
        ('ExceptionRecord', EXCEPTION_DEBUG_INFO),
        ('dwFirstChance', c_uint),
    ]

class DEBUG_EVENT_INFO(Union):
    _fields_ = [
        ("Exception", EXCEPTION_DEBUG_INFO),
    ]

class DEBUG_EVENT(Structure):
    _fields_ = [
        ('dwDebugEventCode', c_uint),
        ('dwProcessId', c_uint),
        ('dwThreadId', c_uint),
        ('u', DEBUG_EVENT_INFO)
    ]

def run_with_debugger(args):
    proc = subprocess.Popen(args, creationflags=1)
    event = DEBUG_EVENT()

    num_exception = 0

    while True:
        if WaitForDebugEvent(pointer(event), 10):
            print event_names.get(event.dwDebugEventCode, 'Unknown Event %s' % event.dwDebugEventCode)

            if event.dwDebugEventCode == 1:
                num_exception += 1

                exception_code = event.u.Exception.ExceptionRecord.ExceptionCode

                if exception_code == 0x80000003L:
                    print "Unknow exception:", hex(exception_code)

                else:
                    if exception_code == EXCEPTION_ACCESS_VIOLATION:
                        print "EXCEPTION_ACCESS_VIOLATION"

                    elif exception_code == EXCEPTION_INT_DIVIDE_BY_ZERO:
                        print "EXCEPTION_INT_DIVIDE_BY_ZERO"

                    elif exception_code == EXCEPTION_STACK_OVERFLOW:
                        print "EXCEPTION_STACK_OVERFLOW"

                    else:
                        print "Other exception:", hex(exception_code)

                    break

            ContinueDebugEvent(event.dwProcessId, event.dwThreadId, DBG_CONTINUE)

        retcode = proc.poll()
        if retcode is not None:
            return retcode

run_with_debugger(['crash.exe'])
2 голосов
/ 17 сентября 2008

Я понимаю, что вы имеете дело с Windows XP, но для тех, кто находится в аналогичной ситуации под Vista, доступен новый API восстановления после сбоев . Вот хорошее введение в то, что они могут сделать.

...