Domanda

Attualmente sto lavorando per aggiungere eccezioni e gestione delle eccezioni alla mia applicazione OSS. Le eccezioni sono state l'idea generale sin dall'inizio, ma volevo trovare un buon quadro di eccezione e, in tutta onestà, comprendere un po 'meglio le convenzioni di gestione delle eccezioni C ++ e gli idiomi prima di iniziare a usarle. Ho molta esperienza con C#/. Net, Python e altre lingue che usano eccezioni. Non sono estraneo all'idea (ma lontano da un maestro).

In C# e Python, quando si verifica un'eccezione non gestita, l'utente ottiene una bella traccia dello stack e in generale un sacco di molto utile Informazioni di debug inestimabili. Se stai lavorando a un'applicazione OSS, far incollare gli utenti tali informazioni nei rapporti sui problemi è ... beh, diciamo solo che ho difficoltà a vivere senza quello. Per questo progetto C ++, ottengo "l'applicazione si è schiantata" o da utenti più informati ", ho fatto x, y e z, e poi si è schiantato". Ma voglio anche quelle informazioni di debug!

Ho già (e con grande difficoltà) ho fatto la mia pace con il fatto che non vedrò mai un modo a piattaforma multipla e incrociata di ottenere una traccia di eccezione C ++, ma so di poter ottenere il nome della funzione e gli altri informazioni rilevanti.

E ora lo voglio per le mie eccezioni non gestite. sto usando Boost :: Eccezione, e hanno questo molto carino diagnostic_information ThingAmajig che può stampare il nome della funzione (UnMangled), file, linea e, soprattutto, altre informazioni specifiche per eccezione che il programmatore ha aggiunto a tale eccezione.

Naturalmente, gestirò le eccezioni all'interno del codice ogni volta che posso, ma non sono così ingenuo nel pensare che non lascerò passare un paio (involontariamente, ovviamente).

try blocco con a catch Ciò crea una finestra di dialogo speciale che informa l'utente che si è verificato un errore nell'applicazione, con informazioni più dettagliate presentate quando l'utente fa clic su "più" o "informazioni di debug" o altro. Ciò conterrebbe la stringa da diagnostic_information. Potrei quindi istruire gli utenti a incollare queste informazioni nei rapporti sui problemi.

Ma una sensazione fastidiosa mi sta dicendo che avvolgere tutto in un blocco di prova è davvero una cattiva idea. Quello che sto per fare stupido? Se lo è (e anche se non lo è), qual è un modo migliore per ottenere ciò che voglio?

È stato utile?

Soluzione

Avvolgendo tutto il tuo codice in uno try/catch Il blocco è a-ok. Non rallenterà l'esecuzione di nulla al suo interno, per esempio. In effetti, tutti i miei programmi hanno (codice simile a) questo framework:

int execute(int pArgc, char *pArgv[])
{
    // do stuff
}

int main(int pArgc, char *pArgv[])
{
    // maybe setup some debug stuff,
    // like splitting cerr to log.txt

    try
    {
        return execute(pArgc, pArgv);
    }
    catch (const std::exception& e)
    {
        std::cerr << "Unhandled exception:\n" << e.what() << std::endl;
        // or other methods of displaying an error

        return EXIT_FAILURE;
    }
    catch (...)
    {
        std::cerr << "Unknown exception!" << std::endl;

        return EXIT_FAILURE;
    }
}

Altri suggerimenti

Mettere un blocco di prova/cattura in main () va bene, non causa alcun problema. Il programma è comunque morto con un'eccezione non gestita. Tuttavia, non sarà affatto utile nella tua ricerca per ottenere l'importantissima traccia dello stack. Quelle informazioni sono gonzo quando il blocco catch intrappola l'eccezione.

Catturare un'eccezione C ++ non sarà neanche molto utile. Le probabilità che il programma muoia su un'eccezione derivata da std :: l'eccezione sono piuttosto scarse. Anche se potrebbe accadere. Molto più probabile in un'app C/C ++ è la morte a causa delle eccezioni hardware, l'accessolazione è numerica Uno. Intrappolare quelli richiedono le parole chiave __try e __xcept nel tuo metodo principale (). Ancora una volta, è disponibile pochissimo contesto, in pratica hai solo un codice di eccezione. Un AV ti dice anche quale posizione di memoria esatta ha causato l'eccezione.

Questo non è solo un problema multipiattaforma btw, non è possibile ottenere una buona traccia di stack su nessuna piattaforma. Non esiste un modo affidabile per camminare sullo stack, ci sono troppe ottimizzazioni (come l'omissione del framepointer) che rendono questo un pericoloso viaggio. È il modo C/C ++: rendilo il più velocemente possibile, non lasciare idea cosa è successo quando soffia.

Quello che devi fare è eseguire il debug di questo tipo di problemi nel modo C/C ++. Devi creare un minidum. È approssimativamente analogo alla "dump core" del vecchio, un'istantanea dell'immagine di processo nel momento in cui si verifica l'eccezione. Allora, in realtà hai ricevuto una discarica completa del nucleo. Ci sono stati progressi, al giorno d'oggi è "mini", in qualche modo necessario perché una discarica completa richiederebbe quasi 2 gigabyte. In realtà funziona abbastanza bene per diagnosticare lo stato del programma.

Su Windows, che si inizia chiamando setUnDledExceptionFilter (), si fornisce un puntatore della funzione di callback a una funzione che verrà eseguita quando il programma muore su un'eccezione non gestita. Qualsiasi eccezione, C ++ e SEH. La tua prossima risorsa è DBGHELP.DLL, disponibile negli strumenti di debug per il download di Windows. Ha un punto di ingresso chiamato MinIdumpWritedump (), crea un minidump.

Una volta che hai ottenuto il file creato da MinIdumpWritedump (), sei piuttosto dorato. Puoi caricare il file .dmp in Visual Studio, quasi come se fosse un progetto. Premere F5 e VS macina per un po 'cercando di caricare i file .pdb per le DLL caricate nel processo. Ti consigliamo di impostare il server dei simboli, questo è molto Importante per ottenere buone tracce dello stack. Se tutto funziona, otterrai una "pausa di debug" nella posizione esatta in cui è stata lanciata l'eccezione ". Con una traccia dello stack.

Cose che devi fare per far funzionare questo senza intoppi:

  • Usa un server build per creare i binari. Deve spingere i simboli di debug (file .pdb) su un server di simboli in modo che siano prontamente disponibili quando si esegue il debug del minidum.
  • Configurare il debugger in modo che possa trovare i simboli di debug per tutti i moduli. Puoi ottenere i simboli di debug per Windows da Microsoft, i simboli per il tuo codice devono provenire dal server di simboli sopra menzionato.
  • Scrivi il codice per intrappolare l'eccezione non gestita e creare il minidum. Ho menzionato setUnHandleDExceptionFilter () ma il codice che crea il minidump non dovrebbe essere nel programma che si è schiantato. Le probabilità che possa scrivere il minidum con successo sono abbastanza sottili, lo stato del programma è indeterminato. La cosa migliore da fare è eseguire un processo di "guardia" che tiene d'occhio un mutex chiamato. Il tuo filtro di eccezione può impostare il mutex, la guardia può creare il minidump.
  • Crea un modo per il Minidump di essere trasferito dalla macchina del client alla tua. Usiamo il servizio S3 di Amazon per questo, Terabyte a un ritmo ragionevole.
  • Collegare il gestore di minidum nel database di debug. Usiamo JIRA, ha un servizio Web che ci consente di verificare il secchio da crash contro un database di crash precedenti con la stessa "firma". Quando è unico o non ha abbastanza colpi, chiediamo al codice Crash Manager di caricare il minidum su Amazon e creare la voce del database BUG.

Bene, è quello per cui ho fatto per l'azienda per cui lavoro. Ha funzionato molto bene, ha ridotto la frequenza del secchio da crash da migliaia a dozzine. Messaggio personale ai creatori del componente FFDShow open source: ti odio con passione. Ma non stai più in crash la nostra app! CUNGGER.

No, non è stupido. È un'ottima idea e non costa praticamente nulla in fase di esecuzione fino a quando non si raggiunge un'eccezione non gestita, ovviamente.

Tieni presente che esiste già un gestore di eccezioni che avvolge il tuo thread, fornito dal sistema operativo (e un altro dal C-Runtime, penso). Potrebbe essere necessario trasmettere determinate eccezioni a questi gestori per ottenere un comportamento corretto. In alcune architetture, l'accesso ai dati non allineati è gestito da un gestore di eccezioni. Quindi potresti voler un caso speciale EXCEPTION_DATATYPE_MISALIGNMENT E lascialo passare al gestore delle eccezioni di livello superiore.

Includo i registri, la versione dell'app e il numero di build, il tipo di eccezione e un dump dello stack in esadecimente annotato con nomi di moduli e offset per valori esadecimali che potrebbero essere indirizzi per il codice. Assicurati di includere il numero di versione e il numero/data di build dell'EXE.

Puoi anche usare VirtualQuery Per trasformare i valori dello stack in "Modulename+Offset" abbastanza facilmente. E questo, combinato con un file .map, spesso ti dirà esattamente dove ti sei schiantato.

Ho scoperto che avrei potuto formare i beta tester a inviare il mio testo abbastanza facilmente, ma all'inizio quello che ho ottenuto era una foto della finestra di dialogo Error piuttosto che del testo. Penso che sia perché molti utenti non sanno che puoi fare clic con il pulsante destro del mouse su qualsiasi controllo di modifica per ottenere un menu con "Seleziona tutto" e "copia". Se avessi intenzione di farlo di nuovo, aggiungerei un pulsante che copiava quel testo negli appunti in modo che possa essere facilmente incollato in un'e -mail.

Ancora meglio se vuoi andare al problema di avere un pulsante "Invia un rapporto di errore", ma solo dare agli utenti un modo per inserire il testo nelle proprie e -mail ti porta la maggior parte delle cose lì e non solleva alcuna bandiera rossa su "Quali informazioni sto condividendo con loro?"

In fact, boost::diagnostic_information has been designed specifically to be used in a "global" catch(...) block, to display information about exceptions which should not have reached it. However, note that the string returned by boost::diagnostic_information is NOT user-friendly.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top