Pergunta

Qual é a melhor maneira de detectar uma falha de aplicativo no XP (produz sempre o mesmo par de janelas de 'erro' - cada uma com o mesmo título de janela) e reiniciá-lo?

Estou especialmente interessado em ouvir falar de soluções que utilizam recursos mínimos do sistema, pois o sistema em questão é bastante antigo.

Pensei em usar uma linguagem de script como AutoIt (http://www.autoitscript.com/autoit3/) e talvez acionando um script de 'detector' a cada poucos minutos?

Isso seria melhor feito em Python, Perl, PowerShell ou algo totalmente diferente?

Quaisquer ideias, dicas ou pensamentos serão muito apreciados.

EDITAR:Na verdade, ele não trava (ou seja,sair/encerrar - obrigado @tialaramex).Ele exibe uma caixa de diálogo aguardando a entrada do usuário, seguida por outra caixa de diálogo aguardando outras entradas do usuário e, em seguida, ele realmente sai.São esses diálogos que eu gostaria de detectar e lidar.

Foi útil?

Solução

Que tal criar um aplicativo wrapper que inicia o aplicativo defeituoso quando criança e espera por ele?Se o código de saída do filho indicar um erro, reinicie-o, caso contrário, saia.

Outras dicas

A melhor maneira é usar um nome mutex.

  1. Inicie seu aplicativo.
  2. Crie um novo mutex nomeado e assuma a propriedade dele
  3. Inicie um novo processo (processo, não thread) ou uma nova aplicação, conforme sua preferência.
  4. A partir desse processo/aplicativo tente adquirir o mutex.O processo irá bloquear
  5. Quando o aplicativo terminar, libere o mutex (sinalize-o)
  6. O processo de "controle" só adquirirá o mutex se o aplicativo terminar ou travar.
  7. Teste o estado resultante após adquirir o mutex.Se o aplicativo travou, será WAIT_ABANDONED

Explicação: Quando uma thread termina sem liberar o mutex, qualquer outro processo que esteja aguardando por ela poderá adquiri-lo, mas obterá um WAIT_ABANDONED como valor de retorno, significando que o mutex é abandonado e, portanto, o estado da seção que foi protegida pode ser inseguro.

Dessa forma, seu segundo aplicativo não consumirá nenhum ciclo de CPU, pois continuará aguardando o mutex (e isso é totalmente controlado pelo sistema operacional)

Acho que o principal problema é que o Dr.Watson exibe uma caixa de diálogo e mantém seu processo vivo.

Você pode escrever seu próprio depurador usando a API do Windows e executar o aplicativo travador a partir daí.Isso impedirá que outros depuradores pegem o acidente do seu aplicativo e você também poderá obter o evento de exceção.

Como não encontrei nenhum código de amostra, escrevi esta amostra rápida e direta do Python.Não tenho certeza de quão robusto é especialmente a declaração de debug_event poderia ser melhorada.

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'])

Sei que você está lidando com o Windows XP, mas para pessoas em situação semelhante no Vista, existem novos API de recuperação de falhasestá disponível. Aqui está uma boa introdução para o que eles podem fazer.

Aqui está uma versão ligeiramente melhorada.

No meu teste, o código anterior foi executado em um loop infinito quando o exe defeituoso gerou uma "violação de acesso".

Não estou totalmente satisfeito com minha solução porque não tenho critérios claros para saber qual exceção deve continuar e qual não pode ser (O ExceptionFlags não ajuda em nada).

Mas funciona no exemplo que executo.

Espero que ajude, Vivian de Smedt

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'])
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top