Qual é a maneira mais fácil de travar um programa C ++?


318

Estou tentando criar um programa Python que faça interface com um processo travado diferente (que está fora do meu alcance). Infelizmente, o programa com o qual estou interagindo nem falha de maneira confiável! Então, eu quero criar um programa C ++ rápido que trava de propósito, mas na verdade eu não sei a melhor e mais curta maneira de fazer isso, alguém sabe o que colocar entre os meus:

int main() {
    crashyCodeGoesHere();
}

fazer meu programa C ++ travar de maneira confiável


4
você pode usar o assembly embutido para tentar executar instruções privadas:asm { cli; };
Nate Koppenhaver,

@itchieu Acho que há uma diferença na usabilidade das respostas para cada pergunta. (FYI: Eu não tenho votado qualquer coisa para qualquer questão)
Andrew Barber

algum comentário de lançar exceção enquanto um já propaga? plz chk minha resposta abaixo comentário
antigo

4
Redis usa o *((char*)-1) = 'x';código a seguir para induzir uma falha, a fim de depurar, leia mais em minha resposta aqui #
Shafik Yaghmour

Encontrei esta pergunta procurando um caso de teste para um sistema de relatório de falhas. Eu precisava forçar uma falha durante o tempo de execução normal para chamar o repórter de falha e empilhar o envio de despejo. Obrigado!
Cory Trese

Respostas:


264

A abort()função é provavelmente a sua melhor aposta. Faz parte da biblioteca padrão C e é definido como "causando o encerramento anormal do programa" (por exemplo, um erro fatal ou falha).


14
Observe que uma falha abort()não chama destruidores ou atexitfunções, embora isso provavelmente não importe aqui.
Xeo

139
@ Xeo: Se chamar destruidores atexites, não seria um acidente agora, seria?
Donal Fellows

Como abort()a resposta é correta, 'exit (-1); `deve ser aceitável?
precisa

9
Não, como não causa uma falha, apenas relata que algo não pode ser feito.
boatcoder

Janelas. GCC-5.4.0. Código de saída: 3. Nenhuma caixa de mensagem de erro. Mensagem do console: "Este aplicativo solicitou que o Runtime o encerrasse de maneira incomum. Entre em contato com a equipe de suporte do aplicativo para obter mais informações.".
Vadzim

113

Experimentar:

raise(SIGSEGV);  // simulates a standard crash when access invalid memory
                 // ie anything that can go wrong with pointers.

Encontrado em:

#include <signal.h>

3
É mais do que apenas definido pela implementação - o sinal pode ser capturado signal(). Porém, a maioria dos aplicativos sãos não.
duskwuff -inactive-

12
Ele trava exatamente da mesma maneira que um SIGSEGV normal dentro do aplicativo (que é a maneira como a maioria dos aplicativos trava). Está bem definido o que faz (por padrão, sai do aplicativo e gera um arquivo principal). Sim, você pode definir um manipulador, mas se você tiver um, não deseja testá-lo da mesma maneira !!
Martin York

1
+1 para raise(). Isso permite testar uma tonelada de tipos diferentes de exceções, apenas alterando o argumento.

3
solução favorita, no entanto, depende da plataforma.
Nadim Farhat

@NadimFarhat: De que maneira. Um sinal é um sinal em todas as plataformas.
Martin York

74

Dividir por zero irá travar o aplicativo:

int main()
{
    return 1 / 0;
}

29
Dependendo da inteligência do seu compilador, isso será detectado no momento da compilação. Eu sei que o visual studio 2008 não compilará isso para c ++ ou c #.
precisa saber é o seguinte

2
Eu compilei e executei, você quer que eu lixe o arquivo .exe?
Roee Gavirel

1
Na configuração da versão do Visual Studio, versões recentes como 2010, ele será executado sem problemas. Eu acho que é alguma otimização.
tedyyu

2
IIRC ele não bate no braço
sherpya

2
Esse é um comportamento indefinido e não tem garantia de falha. Frequentemente, os compiladores assumem que o comportamento indefinido é inacessível. Isso pode levar à mainexclusão total do corpo, incluindo as retinstruções. A execução pode cair na seguinte função.
usr

64
*((unsigned int*)0) = 0xDEAD;

54
Não há garantia de falha.
Programador Windows

8
@ Windows programmer: não, não é garantido . Mas qual sistema operacional sã não para um aplicativo que tenta acessar a memória no endereço 0?
Joachim Sauer

29
"Mas qual sistema operacional sã não interrompe um aplicativo que tenta acessar a memória no endereço 0?" - Não é exatamente o que você quer perguntar, mas eu responderei assim mesmo. Em alguns computadores, há memória RAM no endereço 0 e é perfeitamente significativo para um programa armazenar um valor lá. Uma pergunta mais significativa seria "Qual sistema operacional não interrompe um aplicativo que acessa a memória no endereço que uma implementação de C ++ reservou para um ponteiro nulo?" Nesse caso, não conheço nenhum. Mas o programa original é sobre a linguagem C ++ e não sobre sistemas operacionais.
Programador Windows

6
Seu comportamento indefinido. É perfeitamente aceitável que isso não faça nada. Uma máquina que não trava: qualquer coisa executando um processador da série Z80 (presumo (meu Z80a não faz nada)).
Martin York

28
Embora não haja garantia de falha, é um dos tipos mais comuns de falha no C ++. Portanto, se você deseja simular uma falha, esta é uma maneira "autêntica" de fazê-lo :) #
497 Chris Burt-Brown

53

Bem, estamos com excesso de pilha , ou não?

for (long long int i = 0; ++i; (&i)[i] = i);

(Não há garantia de falha por nenhum padrão, mas nenhuma das respostas sugeridas, incluindo a aceita, já que SIGABRTpoderia ter sido detectada de qualquer maneira. Na prática, isso falhará em todos os lugares.)


4
Percebo que isso é engraçado em um sistema com páginas de código não protegidas e você substitui seu programa por algum código que é acidentalmente um loop infinito que não faz nada. Altamente altamente altamente altamente improvável, mas potencialmente possível.
Martin York

@Loki: E se ele acabou de ler todos os 4000 bytes? Seria menos provável que falhasse? Definitivamente menos perigoso.
Mooing Duck

70
Esse algoritmo de colisão não é O (1)!
Anton Barkovsky

@MooingDuck: Estou apenas fazendo um comentário engraçado. Não leve isso a sério :-) Mas seria interessante se alguém encontrasse uma sequência de instruções que fizesse algo engraçado.
Martin York

1
@LokiAstari: você está absolutamente certo. Eu estava pensando em (&i)[i] += !ivez disso, mas temia que o compilador fosse inteligente o suficiente e quisesse otimizar isso. :-)
sam hocevar

35
 throw 42;

Apenas a resposta ... :)


1
Janelas. GCC-5.4.0. Código de saída: 3. Nenhuma caixa de mensagem de erro. Mensagem do console: "encerrar chamado após lançar uma instância de 'int' Este aplicativo solicitou que o Runtime o encerrasse de uma maneira incomum. Entre em contato com a equipe de suporte do aplicativo para obter mais informações.".
Vadzim

15

assert(false); também é muito bom

De acordo com a ISO / IEC 9899: 1999, é garantido um travamento quando o NDEBUG não está definido:

Se NDEBUG for definido, [...] a macro assert é definida simplesmente como

#define assert(ignore) ((void)0)

A macro assert é redefinida de acordo com o estado atual do NDEBUG cada vez que é incluído.

[...]

A macro assertiva coloca testes de diagnóstico em programas; se a expressão (que deve ter um tipo escalar) é falsa [...]. Em seguida, chama a função abortar.


Lembro-me vagamente do VC 2005 se comportando de maneira diferente entre depuração e lançamento com asserts?
Tom Kerr

8
@Tom asserté equivalente a ((void)0)no modo Release.
Seth Carnegie

2
@SethCarnegie Não vejo o que há de errado com isso - apenas se a vontade definida de NDEBUG definida não estiver travada? Dans resposta foi bastante justo IMHO.
Adrian Cornish

@AdrianCornish Eu estava apenas respondendo à pergunta de Tom Kerr, não dizendo que esta resposta estava errada. Não neguei esta resposta.
Seth Carnegie

3
Não sei por que ele faria uma compilação "release" desse código de teste.
Joel B

11

Como uma falha é um sintoma de invocar um comportamento indefinido e, uma vez que a invocação de um comportamento indefinido pode levar a qualquer coisa, incluindo uma falha, não acho que você queira realmente travar seu programa, mas apenas deixá-lo cair em um depurador. A maneira mais portátil de fazer isso é provavelmente abort().

Embora raise(SIGABRT)tenha o mesmo efeito, certamente é mais para escrever. No entanto, as duas maneiras podem ser interceptadas instalando um manipulador de sinais para SIGABRT. Portanto, dependendo da sua situação, você pode querer / precisar gerar outro sinal. SIGFPE, SIGILL, SIGINT, SIGTERMOu SIGSEGVpode ser o caminho a percorrer, mas todos eles podem ser interceptadas.

Quando você pode não ser portável, suas escolhas podem ser ainda mais amplas, como usar SIGBUSno linux.


1
Eu realmente duvido que ele queira um depurador envolvido. Ele parece querer testar o que acontece quando o chamador de um programa com falha recebe uma falha em seu caminho. O que é muito razoável.
Donal Fellows

9

O único flash que tive foi a função abort () :

Ele interrompe o processo com uma finalização anormal do programa. Gera o sinal SIGABRT , que por padrão faz com que o programa finalize retornando um código de erro de finalização malsucedido ao ambiente host. O programa é finalizado sem executar destruidores para objetos de duração de armazenamento estático ou automático , e sem chamar nenhuma função atexit (chamada por exit () antes do programa terminar)). Ele nunca retorna ao chamador.


9

A resposta é específica da plataforma e depende de seus objetivos. Mas aqui está a função de travamento do Mozilla Javascript, que eu acho que ilustra muitos dos desafios para fazer esse trabalho:

static JS_NEVER_INLINE void
CrashInJS()
{
    /*
     * We write 123 here so that the machine code for this function is
     * unique. Otherwise the linker, trying to be smart, might use the
     * same code for CrashInJS and for some other function. That
     * messes up the signature in minidumps.
     */

#if defined(WIN32)
    /*
     * We used to call DebugBreak() on Windows, but amazingly, it causes
     * the MSVS 2010 debugger not to be able to recover a call stack.
     */
    *((int *) NULL) = 123;
    exit(3);
#elif defined(__APPLE__)
    /*
     * On Mac OS X, Breakpad ignores signals. Only real Mach exceptions are
     * trapped.
     */
    *((int *) NULL) = 123;  /* To continue from here in GDB: "return" then "continue". */
    raise(SIGABRT);  /* In case above statement gets nixed by the optimizer. */
#else
    raise(SIGABRT);  /* To continue from here in GDB: "signal 0". */
#endif
}

2
Você deve descartá-lo totalmente e usar o jQuery.
Thomas Weller

1
Esse é um comportamento indefinido e não tem garantia de falha. Frequentemente, os compiladores assumem que o comportamento indefinido é inacessível. Nesse caso, pelo menos a linha com falha será excluída e outro código também.
usr

8

Vejo que há muitas respostas postadas aqui que caem em casos de sorte para concluir o trabalho, mas nenhuma delas é 100% determinística para travar. Alguns travam em um hardware e sistema operacional, outros não. No entanto, existe uma maneira padrão, conforme o padrão oficial do C ++, de fazê-lo falhar.

Citando a norma C ++ ISO / IEC 14882 §15.1-7 :

Se o mecanismo de tratamento de exceções, após concluir a inicialização do objeto de exceção, mas antes da ativação de um manipulador para a exceção, chamar uma função que sai por meio de uma exceção, std :: terminate será chamado (15.5.1).

struct C {
    C() { }
    C(const C&) {
        if (std::uncaught_exceptions()) {
            throw 0; // throw during copy to handler’s exception-declaration object (15.3)
        }
    }
};
int main() {
    try {
    throw C(); // calls std::terminate() if construction of the handler’s
    // exception-declaration object is not elided (12.8)
    } catch(C) { }
}

Eu escrevi um pequeno código para demonstrar isso e pode ser encontrado e testado no Ideone aqui .

class MyClass{
    public:
    ~MyClass() throw(int) { throw 0;}
};

int main() {
  try {
    MyClass myobj; // its destructor will cause an exception

    // This is another exception along with exception due to destructor of myobj and will cause app to terminate
     throw 1;      // It could be some function call which can result in exception.
  }
  catch(...)
  {
    std::cout<<"Exception catched"<<endl;
  }
  return 0;
}

A ISO / IEC 14882 §15.1 / 9 menciona o lançamento sem o bloco try, resultando em uma chamada implícita para abortar:

Se atualmente nenhuma exceção está sendo manipulada, a execução de uma expressão de lançamento sem operando chama std :: terminate ()

Outros incluem: lançamento do destruidor: ISO / IEC 14882 §15.2 / 3


7
*( ( char* ) NULL ) = 0;

Isso produzirá uma falha de segmentação.


10
Não há garantia de falha.
Programador Windows

23
"O que vai acontecer em vez disso?" - Qualquer coisa pode acontecer. O comportamento é indefinido; portanto, a implementação pode atribuir 0 a uma das variáveis ​​do seu programa, ou atribuir 42 a uma das variáveis ​​do seu programa ou formatar o disco rígido e continuar executando o programa.
Programador Windows

7
(mente "programador do Windows" continuando)) Isso pode fazer com que seu computador exploda ou faça com que ele ganhe vida e domine a humanidade. ou ... ele trava em 99,9% e é definido como "comportamento indefinido" porque ninguém quer assumir a responsabilidade.
Roee Gavirel

1
Na verdade, não há garantia de que esse comportamento seja indefinido - ele pode ser completamente definido e funcionar corretamente. Considere este código: pastebin.com/WXCtTiDD (Testado em Linux como root, você poderia fazer isso como um usuário não-root também se você fazer algumas mudanças de configuração wiki.debian.org/mmap_min_addr )
cha0site

2
@ cha0site: É garantido que o comportamento é indefinido pelo Padrão, porque isso está desreferenciando um ponteiro nulo. Seja qual for o comportamento que você observou no Linux é permitido sob a égide do "comportamento indefinido"
Ben Voigt


5

E o estouro de pilha por uma chamada de método recursivo de loop morto?

#include <windows.h>
#include <stdio.h>

void main()
{
    StackOverflow(0);
}

void StackOverflow(int depth)
{
    char blockdata[10000];
    printf("Overflow: %d\n", depth);
    StackOverflow(depth+1);
}

Consulte Exemplo original no Microsoft KB


4
O que impediria um compilador suficientemente inteligente de otimizar a alocação de pilha não utilizada e a chamada final?
JB.

@JB: infelizmente não tenho nenhuma idéia porque não familar com compiladores existente lógica de otimização
SLL

8
Bem, compilado aqui com o gcc 4.6.0 nos níveis de otimização -O2 e acima, ele otimiza muito bem. Ele precisa de -O1 ou inferior para segfault.
JB.

@Abhinav: Basta postar sua resposta com todas estas formas expressa como um exemplo em C ++ :)
SLL

5

Isso trava no meu sistema Linux, porque literais de seqüência de caracteres são armazenados na memória somente leitura:

0[""]--;

A propósito, o g ++ se recusa a compilar isso. Compiladores estão ficando cada vez mais inteligentes :)


4
int i = 1 / 0;

Seu compilador provavelmente o alertará sobre isso, mas compila muito bem no GCC 4.4.3 Isso provavelmente causará um SIGFPE (exceção de ponto flutuante), que talvez não seja tão provável em um aplicativo real quanto o SIGSEGV (violação de segmentação de memória) como as outras respostas causam, mas ainda é um acidente. Na minha opinião, isso é muito mais legível.

Outra maneira, se vamos trapacear e usar signal.h, é:

#include <signal.h>
int main() {
    raise(SIGKILL);
}

Isso é garantido para eliminar o subprocesso, para contrastar com o SIGSEGV.


2
Não há garantia de falha.
Programador Windows

11
A linguagem C ++ não garante que 1/0 cause um SIGFPE. O comportamento é indefinido. A implementação pode dizer que o resultado é 42.
Programador Windows

1
Quando o comportamento é indefinido, a implementação pode fazer o que quiser. A linguagem C ++ não impede nem exige uma implementação para escrever um despejo de memória idioma, o C ++ não impede nem exige uma implementação para atribuir 42, etc.
programador do Windows

2
@Giorgio Se o hardware não tiver uma maneira de interceptá-lo automaticamente, você ainda força os compiladores a emitir pelo menos duas instruções, uma das quais também seria uma ramificação. Isso é aproximadamente o dobro do custo de uma divisão. Todo mundo paga esse custo assim. Se for opcional e você desejar, sempre poderá usar uma função de biblioteca para ela. Se não for opcional e você não quiser, ainda assim pagará o custo.
Flexo

2
@Giorgio: Eu tenho um aplicativo que faz um 100,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000 * 10 ^ 1000000 divisões. Eu sei que 0 deles será uma divisão por zero, embora não haja como o compilador possa saber disso. Definitivamente, não quero que o compilador plante a verificação de uma divisão por zero.
Martin York

4

Esta é uma versão mais garantida do cancelamento apresentada nas respostas acima. Ele cuida da situação em que o sigabrt está bloqueado. Você pode usar qualquer sinal em vez de cancelar que tenha a ação padrão de travar o programa.

#include<stdio.h>
#include<signal.h>
#include<unistd.h> 
#include<stdlib.h>
int main()
{
    sigset_t act;
    sigemptyset(&act);
    sigfillset(&act);
    sigprocmask(SIG_UNBLOCK,&act,NULL);
    abort();
}

3
int* p=0;
*p=0;

Isso também deve travar. No Windows, ele trava com o AccessViolation e deve fazer o mesmo em todos os sistemas operacionais, eu acho.


5
on all OS-esNão, não falha em OS não protegido (por exemplo, MS-DOS.) Na verdade, às vezes não é algo em endereço 0! Para x86 modo real, interrupção Vector Table é o endereço 0.
Ikh

Não travou no Irix. I infelizmente percebeu quando portado esse código para Linux (onde nós ainda não alcançou main().
Scheff

3

Este é o trecho fornecido pelo Google no Breakpad.

  volatile int* a = reinterpret_cast<volatile int*>(NULL);
  *a = 1;

1
Como estou testando o Breakpad, é exatamente isso que eu queria! Descobri que alguns minidumps do Breakpad não produzem um rastreamento de pilha que aponta para a linha no meu código que causa a falha. Este faz, para que eu possa usá-lo como um bom teste poc.
precisa saber é o seguinte

2
int main(int argc, char *argv[])
{
    char *buf=NULL;buf[0]=0;
    return 0;
}

2

Embora esta pergunta já tenha uma resposta aceita ...

void main(){
    throw 1;
}

Ou... void main(){throw 1;}


2

A gravação em uma memória somente leitura causará uma falha de segmentação, a menos que seu sistema não suporte blocos de memória somente leitura.

int main() {
    (int&)main = 0;
}

Eu testei com o MingGW 5.3.0 no Windows 7 e o GCC no Linux Mint. Suponho que outros compiladores e sistemas tenham um efeito semelhante.


1

Ou de outra maneira, já que estamos no vagão da banda.

Uma adorável peça de recursão infinita. Garantido para explodir sua pilha.

int main(int argv, char* argc)
{
   return main(argv, argc)
}

Imprime:

falha de segmentação (despejo de núcleo)


13
Chamar a mainsi mesmo é um comportamento indefinido, caso você não saiba :) Além disso, não é garantido que a recursão da cauda acabe com sua pilha. Se você quiser uma "garantia", precisará fazer algo após a chamada recursiva, caso contrário, o compilador poderá otimizar a recursão em um loop infinito.
Fredoverflow 27/09/12

0

Um que ainda não foi mencionado:

((void(*)())0)();

Isso tratará o ponteiro nulo como um ponteiro de função e depois o chamará. Assim como a maioria dos métodos, não é garantido que isso interrompa o programa, mas as chances do sistema operacional permitir que isso ocorra e que o programa volte sempre são insignificantes.


3
Conheço várias máquinas que isso causará uma reinicialização, pois o código de inicialização do sistema operacional é efetivamente mapeado para o endereço 0. Não assuma que tudo funcionará como o seu PC. Você poderia dizer que houve um travamento, mas não é muito útil, pois você não pode depurá-lo, pois todo o estado é apagado na inicialização.
Martin York

@Loki Astari: Não obstante, direi que isso é uma falha - se isso puder causar, o programa que está sendo depurado também pode, o que significa que é um teste tão bom quanto qualquer outro. Por outro lado, estou curioso para saber qual dessas máquinas pode executar o Python.
Anton Golov

Você não entendeu. Eu vi o código do sistema operacional em 0. Não significa que não há sistemas com código normal bom para executar que funcionem perfeitamente em 0. Ou os bytes em 0 podem ser facilmente o código de operação para retorno.
Martin York

Estou ciente do código do SO em 0; essa é uma das razões pelas quais duvido que os bytes em 0 sejam o código de operação para retorno. O programa claramente não está mais em execução e não saiu da maneira usual, ou seja, travou - se isso não for bom o suficiente para o solicitante, espero que ele faça um comentário.
Anton Golov

2
Você conhece o código do SO apenas para sua máquina. O que estou tentando dizer é que isso pode falhar para você. Mas isso não significa nada. Existem muitos sistemas por aí. Estou certo de que alguns deles podem funcionar (ou seja, como não falhar). Confiar no comportamento específico da máquina / SO é uma má ideia e causa problemas de manutenção a longo prazo. A idéia do site é promover um bom código (não apenas o código que funciona).
Martin York

0
void main()
{

  int *aNumber = (int*) malloc(sizeof(int));
  int j = 10;
  for(int i = 2; i <= j; ++i)
  {
      aNumber = (int*) realloc(aNumber, sizeof(int) * i);
      j += 10;
  }

}

Espero que isso trava. Felicidades.


0
int main()
{
    int *p=3;
    int s;
    while(1) {
        s=*p;
        p++;
    }
}

2
Seria ótimo ter algum esclarecimento :)
olyv

1
o ponteiro p irá além do espaço de endereço do programa, que será um erro de memória, pois um processo não pode acessar a memória de outro processo. Isso resultará na falha do programa. o ponteiro p está apontando para um local aleatório em seu espaço de endereço; se for incrementado e desreferenciado infinitamente em algum momento, apontará para o espaço de endereço de outro programa (processo). então ele falhará após algum tempo.
sc_cs

Ou, hipoteticamente, poderia atingir um excesso de número inteiro e contornar, executando infinitamente. Eu tentaria usar long longou size_te começar com po respectivo valor máximo, ou próximo a ele, para travar mais rápido. Embora ainda não haja garantia de falha, mesmo nesse caso.
Patrick Roberts

0

Uma maneira elegante de fazer isso é uma chamada de função virtual pura:

class Base;

void func(Base*);

class Base
{
public:
   virtual void f() = 0;
   Base() 
   {
       func(this);
   }
};

class Derived : Base
{
   virtual void f()
   {
   }
};

void func(Base* p)
{
   p->f();
}


int main()
{
    Derived  d;
}

Compilado com o gcc, ele imprime:

método virtual puro chamado

terminar chamado sem uma exceção ativa

Abortado (núcleo despejado)


0

Você pode usar a montagem em seu c ++, code mas INT 3 é apenas para sistemas x86 outros sistemas podem ter outras instruções de interceptação / ponto de interrupção.

int main()
{
    __asm int 3;

    return 0;
}

INT 3 causa uma interrupção e chama um vetor de interrupção configurado pelo sistema operacional.


0

Use __builtin_trap () no GCC ou clang, ou __debugbreak () no MSVC. O não manuseio desses pontos de interrupção / interrupções levará a uma exceção / falha não tratada do ponto de interrupção. Outras sugestões que usam abort () ou exit (): podem ser tratadas por outros threads, dificultando a visualização da pilha do thread que propagou a falha.


-2
char*freeThis;
free(freeThis);

Liberar um ponteiro não inicializado é um comportamento indefinido. Em muitas plataformas / compiladores, freeThisterá um valor aleatório (o que estava naquele local da memória antes). A liberação solicitará que o sistema libere a memória nesse endereço, o que geralmente causará uma falha de segmentação e causará uma falha no programa.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.