Stack Trace

zeuszeus Membre
10:58 modifié dans Vos applications #1
Bonjour,

Je cherche désepérément comment on peut imprimer le stack trace dans une application.

Je m'explique je suis pas mal habitué à  Java et dans ce language quand on a une erreur au runtime, on obtient, dans le terminal, la trace d'exécution qui a fait planter le programme.

Alors que là , quand j'ai des exceptions avec objective C, par exemple lorsque j'apelle une méthode qui n'existe pas, j'ai juste un message m'indiquant que sélecteur n'a pas été reconnu. Moi je voudrai bien que pour le même prix on me dise dans quelle classe, à  quelle ligne l'exception a été levée et aussi quelles sont les méthodes qui on fait cet appel.

J'ai bien trouvé quelque chose à  l'adresse http://www.cocoadev.com/index.pl?StackTraces mais je dois dire qu'il me faudrait plutôt un pas à  pas, parceque les explications qui sont sur cette page restent assez mistérieuses pour moi (je suis incapable de comprendre, entre autre, où ajouter les bouts de code qui sont fournis).

Voila j'espère qu'une âme charitable pourra éclairer ma lanterne.

Merci d'avance.

Jérôme

Réponses

  • 10:58 modifié #2
    Mon mix :

    installHandle() pour l'installer à  l'init de ton app
    //

    //

    - (void)startStackTrace:(NSException*)exception
    {
    // http://www.cocoadev.com/index.pl?StackTraces //

    NSLog(@"
    startStackTrace
    ");
    NSLog(@NAME %@",[exception name]);
    NSLog(@REASON %@",[exception reason]);

    NSString *stackTrace=[[exception userInfo] objectForKey:NSStackTraceKey];
    if (stackTrace==nil)
    return;

    NSString *str=[NSString stringWithFormat:@/usr/bin/atos -p %d %@ | tail -n +3 | head -n +%d | c++filt | cat -n",
    getpid(),
    stackTrace,
    ([[stackTrace componentsSeparatedByString:] count] - 4)];
    FILE *F=popen([str UTF8String],"r");

    if (F)
    {
    char buffer[512];
    size_t length;
    NSMutableString *ms=[NSMutableString string];

    while(length=fread(buffer,1,sizeof(buffer),F))
    {
    [ms appendString:[NSString stringWithCString:buffer length:length]];
    }
    pclose(F);
    NSArray *rows=[ms componentsSeparatedByString:@\n];
    int i;
    for (i=0;i<[rows count];i++)
    NSLog(@%@",[rows objectAtIndex:i]);
    }
    NSLog(@"
    ");
    }

    - (BOOL)exceptionHandler:(NSExceptionHandler *)sender shouldHandleException:(NSException *)exception mask:(unsigned int)aMask
    {
    NSLog(@exceptionHandler\n\n);
    NSLog(@sender %@",sender);
    if (controlException)
    [self startStackTrace:exception];
    NSLog(@\n\nexceptionHandler);

    if ([appController DEBUGMODE]==0)
    [crashReport showCrashReport:NO];

    return NO;
    }

    - (void)setControlException:(BOOL)v { controlException=v; }

    - (void)installHandle
    {
    NSExceptionHandler *handler = [NSExceptionHandler defaultExceptionHandler];
    [handler setExceptionHandlingMask:NSLogAndHandleEveryExceptionMask];
    [handler setDelegate:self];
    }
    //

    //

  • zeuszeus Membre
    10:58 modifié #3
    Merci pour l'aide mais je ne vois toujours pas où ajouter ce code. Le problème c'est que je n'ai pas a proprement parler de classe application, les seul éléments que j'ai sont des délégués qui s'occupe d'affichage.

    Jérôme
  • BruBru Membre
    10:58 modifié #4
    dans 1140363832:

    Je m'explique je suis pas mal habitué à  Java et dans ce language quand on a une erreur au runtime, on obtient, dans le terminal, la trace d'exécution qui a fait planter le programme.

    Alors que là , quand j'ai des exceptions avec objective C, par exemple lorsque j'apelle une méthode qui n'existe pas, j'ai juste un message m'indiquant que sélecteur n'a pas été reconnu. Moi je voudrai bien que pour le même prix on me dise dans quelle classe, à  quelle ligne l'exception a été levée et aussi quelles sont les méthodes qui on fait cet appel.


    Là  s'arrête la comparaison entre Java (tes anciennes habitudes de travail) et Obj-C (tes nouvelles).

    Java est interprété et, de ce fait, possède une très forte relation entre byte-code et source qui a généré ce byte-code.

    Obj-C est compilé, donc sans outil de debugging et environnement de test adapté, il n'y plus aucune relation entre le programme compilé et le source qui l'a créé.

    Il est possible d'obtenir un stacktrace sur réception d'une exception, mais cela ne te donnera rien de plus que les adresses mémoire des différents appels antérieurs : c'est inutilisable.

    .
  • 10:58 modifié #5
    dans 1140371642:

    Il est possible d'obtenir un stacktrace sur réception d'une exception, mais cela ne te donnera rien de plus que les adresses mémoire des différents appels antérieurs : c'est inutilisable.


    Il donne surtout les objects et les methodes qu'ils ont appelés du main à  l'exception ! Je trouve pas ça inutilisable !
  • BruBru Membre
    10:58 modifié #6
    dans 1140374815:

    Il donne surtout les objects et les methodes qu'ils ont appelés du main à  l'exception ! Je trouve pas ça inutilisable !


    Je suis d'accord avec toi, mais en utilisant la commande atos, qui est un outil externe...
    Et qui dans certains cas de plantage sévère ne peut pas fonctionner.

    .
  • zeuszeus Membre
    10:58 modifié #7
    OK j'ai bien saisi que le stack trace ne sera pas aussi utile qu'avec du Java. Mais j'ai un cas très concret à  vous exposer. J'ai écrit un peu de code qui fonctionne, j'ai changer d'avis sur la manière d'implanter une ou deux routines, et du coup je me retrouve avec une erreur
    -[TLLEntry _fastCStringContents:]: selector not recognized [self = 0x4d4a850]

    Alors la grosse question qui se cache la derrière c'est: Je fais comment pour déverminer ce code? Parce que je ne sais pas qui à  fait l'appel fautif, je peux pas vraiment déduire quelque chose de ce message d'erreur si ce n'est que, pour une raison qui m'est inconue, le runtime a décidé d'appeler une méthode sur un de mes objets qui n'existe pas. J'ai bien essayé de mettre des breakpoints avec GDB mais visiblement c'est une méthode qui est appelée par un quelconque élément graphique sur lequel je n'ai pas de pouvoir direct. Donc on fait comment dans ce cas, on plante des breakpoints au petit bonheur la chance pour espérer trouver la source de l'appel?

    Ceci dit je n'ai toujours pas compris ou je devais mettre le code proposé par Supermic.

    Jérôme
  • BruBru Membre
    10:58 modifié #8
    Une méthode assez simple est d'utiliser GDB.

    Dans Xcode, ajoute un fichier source source NSApplication.m. En fait, on ajoute une catégorie à  NSApplication, qui permet de personnaliser la méthode reportException:. On a pas besoin d'un fichier header.

    Dans ce fichier source, insère le code suivant :
    <br />@implementation NSApplication(err)<br />- (void)reportException:(NSException *)anException<br />{<br />&nbsp; &nbsp; NSLog(@&quot;%@&quot;, anException);<br />}<br />@end<br />
    


    Ensuite, insère un breakpoint sur la ligne [tt]NSlog[/tt] du fichier NSApplication.m.

    Enfin, build-and-debug dans Xcode (menu Build > Build and Debug).
    Normalement, GDB va s'arrêter sur ton point d'arrêt dès réception d'une exception, et dans la section Thread du debugger, tu n'as plus qu'à  étudier les stack-frames...

    .
  • zeuszeus Membre
    février 2006 modifié #9
    Merci bien Bru j'ai pu avoir accès au stack trace avec ta méthode. Et je confirme que quand on sort de Java le stacktrace est nettement moins parlant. Le deuxième point plus marrant est que mon erreur au runtime a disparu magiquement (je sais bien que rien n'est magique en informatique, j'en ai fais suffisament pour le savoir).

    J'explique: Histoire de voire la tête d'un stacktrace différent de celle de mon erreur j'ai mis une bonne grosse erreur dans mon code (j'ai releasé un objet et je l'ai utilisé ensuite). J'ai ensuite enlevé la bonne grosse erreur et là  il n'y avait plus la petite erreur initiale non plus. Je vous met le run log pour prouver mes dire.

    <br />[Session started at 2006-02-19 21:17:42 +0100.]<br />2006-02-19 21:17:44.694 TLL[467] *** -[TLLEntry _fastCStringContents:]: selector not recognized [self = 0x351820]<br />2006-02-19 21:17:44.694 TLL[467] *** -[TLLEntry _fastCStringContents:]: selector not recognized [self = 0x351820]<br /><br />[Session started at 2006-02-19 21:22:41 +0100.]<br /><br />TLL has exited due to signal 11 (SIGSEGV).<br />[Session started at 2006-02-19 21:23:55 +0100.]<br /><br />[Session started at 2006-02-19 21:24:14 +0100.]<br />
    


    Bon alors merci encore une fois a Bru qui m'a magiquement sortit d'embaras.

    Jérôme

    [EDIT] je viens de re-essayer et pof l'erreur est de retour [/EDIT]
  • BruBru Membre
    10:58 modifié #10
    dans 1140381449:

    [EDIT] je viens de re-essayer et pof l'erreur est de retour [/EDIT]


    Que dit le stack-trace ?

    .
  • zeuszeus Membre
    10:58 modifié #11
    stacktrace2ay.jpg

    Voila le résultat du stack trace. Autant dire que l'on en sait pas vraiment plus avant qu'après. Mais ce qui est le plus bizzarre c'est que vraiment cette erreur fait des vas et viens. Dès fois elle est là , je bricole deux truc dans le code puis je rétablis la version originale et pouf plus rien. Je me doute bien qu'il doit y avoir quelque chose qui change mais je sais vraiment pas quoi. J'ai bien sûr fait des cleans avant de compiler.

    Jérôme
  • BruBru Membre
    10:58 modifié #12
    La méthode (undocumented) _fastCStringContents fait partie des classes NSString/NSMutableString.

    Cela signifie qu'à  un moment donné, ton objet TLLEntry est pris pour une NSString...
    Un tel cas apparait souvent lors d'un memory-leak.

    Vérifie bien que toutes tes instances TLLEntry sont bien correctement "retain" et ne sont "dealloc" qu'au bon moment.

    .
  • zeuszeus Membre
    10:58 modifié #13
    Je pense que tu as parfaitement raison. Je suis allé un peu fureter sur le web et en effet il semblerai que le fait d'avoir un comportement erratique d'une exécution à  l'autre en plus du fait qu'on prenne mon objet pour un NSString soit du au fait que j'ai du releasé mon objet et donc que d'une exécution à  l'autre je pointe pas toujours à  la même position mémoire.

    Ha Java tu me manques. Je suis définitivement perplexe au sujet de la gestion de la mémoire à  la main.

    Je suis trop crevé pour fouiller ça ce soir mais je m'y recolle demain (espérons que la nuit porte conseil).

    Merci bien pour ton aide.
  • BruBru Membre
    10:58 modifié #14
    dans 1140388404:

    Ha Java tu me manques. Je suis définitivement perplexe au sujet de la gestion de la mémoire à  la main.


    Je suis en plein java actuellement (développement de potlets pour un portail applicatif).
    J'avoue que la gestion des objets où on ne se préoccupe pas de leur allocation mémoire est pratique.
    Mais...
    On est amené à  manipuler des objets "lourds" dans des boucles... Et là , quand le garbage-collector se met en route, c'est la cata au niveau des performances de la JVM.

    La gestion de la mémoire sous cocoa demande un peu plus de rigueur. Mais une fois le concept acquis, tu n'y penses plus (crois-moi sur parole !).

    Bon courage pour la suite.

    .
  • zeuszeus Membre
    10:58 modifié #15
    Voila l'esprit repos,é j'ai eu le fin mot de l'histoire. Le code qui foire c'est:

    <br />- (BOOL)isEqual:(id)anObject{<br />	return ([anObject isMemberOfClass:[TLLEntry class]]) ? [itemName isEqualToString:anObject] : NO;<br />}<br />
    


    qui devrait être:

    <br />- (BOOL)isEqual:(id)anObject{<br />	return ([anObject isMemberOfClass:[TLLEntry class]]) ? [itemName isEqualToString:[anObject name]] : NO;<br />}<br />
    


    Bon dis comme ça, ça parrait évident, je considère que deux éléments sont égaux s'ils ont le même nom. Logiquement il faut donc demander le nom du paramètre anObject. Conclusion, le programmeur, donc moi, est un imbécil fini.

    Là  où je suis décu par Objective-C c'est que même un interceptant l'erreur j'ai jamais pu découvrir dans le stacktrace que l'erreur était initialement issue de la méthode
    isEqual
    
    . Alors que Objective-C soit faiblement typé et que je n'ai pas d'erreur à  la compilation, soit, je fais avec, mais que je ne sois même pas capable de découvrir au runtime d'où sort mon erreur je trouve ça assez nul.

    Au final je me demande si ma méthode du stacktrace est applicable à  Objective-C? Vous auriez fais comment, vous, pour trouver cette erreur.

    Jérôme
  • LeChatNoirLeChatNoir Membre, Modérateur
    février 2006 modifié #16
    Ben perso, j'aurai pas fait l'erreur  ;D ;D ;D ;D ;D ;D

    bon, ca va, je te charrie.

    J'aurai très bien pu la faire et je l'ai certainement faite d'ailleurs.

    Moi, je trouve qu'en général, les messages t'orientent pas trop mal.
    Mais il est vrai que parfois...

    Dans ces cas là , je fonctionne à  l'ancienne, à  savoir, NSLog + GDB avec des points d'arrêts.

    Et quand je sèche, ObjectiveCocoa  :P

    Petite anecdote :
    Concernant le debugger, dans le milieu de l'informatique, on dit souvent que les debugger sont faits pour les mauvais développeurs :-)
    Bien sûr, c'est un peu exagéré ...  ;) Et je l'utilise assez souvent  :-\\
Connectez-vous ou Inscrivez-vous pour répondre.