Analyse de Crash avec Fabric (Ex Crashlytics)

MoKeSMoKeS Membre
août 2015 modifié dans API UIKit #1

Hello tout le monde, 


 


Je viens vers vous concernant une petite question un peu "délicate", je vais essayer de m'expliquer au mieux. 


Comme tous les problèmes que je rencontre, ils sont liés à  une application (iPhone/iPad) que je n'ai pas réalisé mais qu'on m'a chargé d'améliorer et de maintenir.


 


Nos utilisateurs rencontrent beaucoup de crash, et qui sont aléatoires. Nous avons installé il y a un moment un framework intégré dans l'application qui, lors d'un crash renvoie les logs et les présente sous une forme lisible, en ajoutant une couche de statistiques (quel os utilisé, quel type de device, la RAM au moment du crash, etc)


Le framework s'appelle CRASHLYTICS (Racheté par Fabric il y a quelques mois, https://get.fabric.io/ )


 


Comme vous pouvez voir sur les captures d'écran ci dessous : 


 


Réponses

  • CéroceCéroce Membre, Modérateur
    août 2015 modifié #2
    Hum, c'est bizarre, je ne vois pas d'appel à  +stringWithFormat: dans le code.

    Le code me parait correct, mais il renvoie une CGImage, dont la gestion de la mémoire est manuelle. Il faut que le code appelant appelle CGImageRelease() à  un moment ou un autre, sinon, ça va faire exploser la conso mémoire et planter. Commence par vérifier ce point.

    Si c'est bien le problème, alors lance un coup d'Instruments/Allocation, parce que ça veut dire que ça n'a jamais été fait.
  • Merci pour ta réponse rapide.


     


    C'est justement ça le problème, je ne comprends pas en quoi les logs ont un rapport avec cette fonction en particulier.


     


    Voici la fonction appelant la fonction incriminée : 



    + (UIImage *) resizedImageWithImage : (SwingImage *)image withSize:(CGSize)newSize withWidthMultiple:(SwingInteger)widthMultiple
    {
    float scale = GetScaleForProportionalResize( image.size, newSize, false, false );

    if ( widthMultiple > 1 && (SwingInteger)roundf( scale * image.size.width ) % widthMultiple != 0 )
    {
    // Make width a multiple of widthMultiple
    SwingDouble factorWidth = roundf( ( scale * image.size.width ) / widthMultiple);
    scale = widthMultiple * factorWidth / image.size.width;
    }

    CGImageRef cgImage = CreateCGImageFromUIImageScaled( image, scale );

    UIImage *newImage = nil;

    if( cgImage )
    {
    // Alloc new image in the same zone as source image
    newImage = [[[UIImage allocWithZone:[image zone]] initWithCGImage:cgImage] autorelease];
    CGImageRelease( cgImage );
    }

    return newImage;
    }


    Comme tu peux le voir CGImageRelease() est bien appelé pour nettoyer la mémoire


  • CéroceCéroce Membre, Modérateur
    Le seul cas où on génère une string est dans -[NSException description].

    Saurais-tu générer une exception ? Par exemple, en mettant width à  0 avec le débogueur, tu peux peut-être y parvenir.
  • J'ai rajouté le @try @catch après avoir constaté tout ce bordel autour de cette fonction, je doute que ce soit la génération de l'exception ou son affichage qui soit ici le problème :-/ 

  • CéroceCéroce Membre, Modérateur
    Tu as une idée du cas dans lequel on a ce bug ?
    Est-ce qu'il apparait sur toutes les versions d'iOS, et surtout, sur tous les modèles ou non ?

    Essaie avec une image très grande.
  • AliGatorAliGator Membre, Modérateur
    Déjà  je ne sais pas d'où sort la fonction CreateCGImageFromUIImageScaled du framework SwingBusiness (qui l'a écrite, toi, ton prédécesseur, une tierce partie, ...) mais son nom ne respecte pas spécialement les standards de nommage.

    Ca peut paraà®tre idiot et un détail, mais ça ne l'est certainement pas, car ça a des impacts sur la gestion de mémoire avec ARC.

    En effet, une méthode qui retourne une référence que l'appelant aura la responsabilité de relâcher (autrement dit une fonction qui retourne un objet CoreFoundation créé par la fonction mais dont c'est à  la charge de l'appelant de faire un CFRelease dessus quand il en aura fini avec la valeur retournée) doit se terminer par "Create". C'est ce que l'on appelle la "Create Rule" de la gestion mémoire des objets CoreFoundation.

    Alors je sais que la règle n'est pas exactement "doit se terminer par Create", et que c'est plutôt "doit contenir le mot Create" donc si ça se trouve ça respecte bien la règle, mais le fait que ce mot "Create" soit au tout tout début du nom ici me fait quand même douter...

    L'impact si on ne respecte pas la Create Rule, c'est que ARC ne détecte alors pas automatiquement que cette méthode retourne un objet retenu. Et du coup va potentiellement appliquer des règles de gestion mémoire qui ne collent pas avec la réalité, et faire ce genre de crash. Après, comme dit plus haut, y'a bien le mot "Create" donc si ça se trouve ça respecte bien la Create Rule et le problème ne vient pas de là , mais ça vaut quand même le coup de creuser cette piste.

    La solution serait soit de renommer ta fonction pour être sûr qu'elle respecte la Create Rule de CoreFoundation, soit de l'annoter avec "CF_RETURNS_RETAINED" pour indiquer au compilateur explicitement (plutôt que de le laisser deviner en fonction du nom de la fonction) que ta fonction retourne un objet retained (et donc que c'est à  l'appelant de faire le release).
  • Hello Ali, 


     


    Le projet sur lequel je travaille, n'utilise pas la gestion de la mémoire avec ARC, tout est fait à  la mano étant donné qu'il a été créé et implémenté bien avant qu'ARC pointe le bout de son petit nez. 


    Du coup, je comprends bien ton explication avec les conventions de nommage (tu m'apprends un truc ici avec cela) mais est-ce que cela s'applique également pour une gestion de mémoire à  la mano ? 


     


    Cette fonction CreateCGImageFromUIImageScaled a été implémentée par mon prédécesseur et comme je l'ai dit dans mon message initial, tout porte à  croire qu'elle a été prise du web, mais je n'arrive pas à  retrouver son origine. Si tu tapes juste le nom de cette méthode sur google, tu peux voir qu'elle est présente à  divers endroit avec exactement le même contenu. 

  • AliGatorAliGator Membre, Modérateur
    Ah oui si le projet n'utilise pas ARC, effectivement le risque de crash dus à  une mauvaise gestion mémoire est d'autant plus important. Par contre sans ARC mais avec une gestion manuelle, les conventions de nommage ont un peu moins d'impact effectivement, du moment que " que les conventions de nommages te permettent de le voir tout de suite avec le nom ou que ce ne soit pas le cas mais que tu le saches finalement en regardant le code que c'est à  toi de faire le release " la balance retain/release soit respectée.

    Déjà  évidemment, le premier réflexe à  avoir de toute façon est de lancer une analyse statique de ton code (Product > Analyze) et de corriger tous les warnings remontés. C'est même pas la peine d'essayer d'aller débuguer plus loin si tu as des warnings jaunes et des erreurs bleues remontées par l'analyzer, commence déjà  par les régler avant d'essayer d'aller plus loin.

    Car sinon tu vas perdre du temps à  essayer de débuguer un truc alors qu'en fait ça pourrait être un effet de bord d'autre chose que tu n'as pas respecté, remonté par un warning ou par l'analyseur statique mais jamais corrigé par tes prédécesseurs. Et un effet de bord du genre, surtout quand il s'agit de gestion mémoire, est si vite arrivé (genre un objet que tu release trop tôt et du coup le code crash mais bien plus tard dans une méthode qui ne semble rien avoir à  voir avec celle où tu as fait ton release en trop, car une zone une mémoire corrompue ne fait crasher l'application que quand tu essayes plus tard d'y accéder) et c'est certainement ce qui t'arrive ici.
    Et puis l'analyseur statique saura te dire dans la plupart des cas (surtout avec les dernières versions de Xcode où il est de plus en plus fin et performant) si justement tu as fait un Release en trop ou en a oublié un.


    Si, après avoir corrigé tous les warnings et réglé tous les problèmes de l'analyseur statique, tu as toujours le crash, il y aura d'autres méthodes pour creuser d'avantage (genre activer les zombies), mais on verra ça à  ce moment là .
  • Ali,


     


    Ok, j'ai déjà  corrigé tous les warnings jaunes (il y en avait plus de 300 quand je suis arrivé)... Je vais faire de même pour les "erreurs bleues" et y'en a un paquet (163 à  la première analyse)


     


    Merci du conseil en tout cas, j'reviendrais un peu plus tard et je vous tiendrais informé mais ça peut être une bonne piste ... 

Connectez-vous ou Inscrivez-vous pour répondre.