5
votes

Crash dans [NSWindow orderFrontRegardless] après la mise à jour vers macOS Mojave

Obtenir ce crash bizarre après la mise à jour vers Mojave.

Ne rien faire de spécial, il suffit de créer une NSWindow et d'appeler orderFrontRegardless

A toujours bien fonctionné avant.

NSWindow *window =    [[NSWindow alloc] initWithContentRect:windowRect
styleMask:windowStyle
backing:NSBackingStoreBuffered
defer:NO];

// Since Snow Leopard, programs without application bundles and Info.plist
// files don't get a menubar and can't be brought to the front unless the
// presentation option is changed
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];

 [NSApp activateIgnoringOtherApps:YES];
 [window makeKeyAndOrderFront:nil];

Code (c'est une application console):

1   libsystem_platform.dylib            0x00007fff6610ab5d _sigtramp + 29
2   ???                                 0x0000000000000000 0x0 + 0
3   CoreFoundation                      0x00007fff39b00bb6 __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 12
4   CoreFoundation                      0x00007fff39b00b30 ___CFXRegistrationPost_block_invoke + 63
5   CoreFoundation                      0x00007fff39b00a9a _CFXRegistrationPost + 404
6   CoreFoundation                      0x00007fff39b08f48 ___CFXNotificationPost_block_invoke + 87
7   CoreFoundation                      0x00007fff39a71994 -[_CFXNotificationRegistrar find:object:observer:enumerator:] + 1642
8   CoreFoundation                      0x00007fff39a70d47 _CFXNotificationPost + 732
9   Foundation                          0x00007fff3bdab217 -[NSNotificationCenter postNotificationName:object:userInfo:] + 66
10  AppKit                              0x00007fff3720538b -[NSWindow _setFrameCommon:display:stashSize:] + 3090
11  AppKit                              0x00007fff37204766 -[NSWindow _setFrame:display:allowImplicitAnimation:stashSize:] + 192
12  AppKit                              0x00007fff3720469f -[NSWindow setFrame:display:] + 51
13  AppKit                              0x00007fff3727aca9 -[NSWindow _reallyDoOrderWindowAboveOrBelow:relativeTo:findKey:forCounter:force:isModal:] + 1336
14  AppKit                              0x00007fff372792a0 -[NSWindow _doOrderWindow:relativeTo:findKey:forCounter:force:isModal:] + 283
15  AppKit                              0x00007fff37a0dce9 -[NSWindow orderFrontRegardless] + 40


7 commentaires

Peut-être une notification envoyée à un objet libéré / mort. Avez-vous un échantillon de code minimum?


@WarrenBurton J'ai ajouté le code.


Le commentaire est-il pertinent? C'est-à-dire est-ce que cette application est sans plist ni bundle? . Je ne peux pas reproduire le plantage dans un modèle de base "Cocoa App".


Oui, c'est une application console.


Cette dernière trame de pile de _sigtramp suggère que vous avez un gestionnaire de signal installé - peut-être un journaliste de crash tiers. D'où vient cette trace de pile? Celles-ci gâchent souvent ReportCrash d'Apple, ce qui rend impossible la capture de la trace de pile en panne d'origine.


@Mattie J'utilise sigaction () et backtrace_symbols_fd (). J'ai posté une réponse, c'était un bug de mémoire sans aucun rapport avec cette trace de pile. Connaissez-vous un meilleur moyen d'obtenir des traces de pile valides?


Vous ne pouvez pas utiliser backtrace_symbols_fd en toute sécurité à partir d'un gestionnaire de signaux. Consultez man sigaction pour plus de détails. Je vais enchaîner avec une réponse plus détaillée.


3 Réponses :


4
votes

Comment initialisez-vous l'application? NSApplication a-t-il été initialisé avant d'utiliser AppKit ?

Quelque chose comme ces étapes devrait être nécessaire dans main.m:

@autoreleasepool {
    NSApplication* application = NSApplication.sharedApplication;

    AppDelegate* delegate = [[AppDelegate alloc] init];
    application.delegate = delegate;

    [application run];
}

De plus, votre délégué pourrait être désalloué, car NSApp en contient une faible référence.


1 commentaires

Merci, j'ai essayé le pool @autorelease, toujours des plantages pour 10% des utilisateurs. Je pense qu'il y a un bogue de mémoire quelque part en profondeur, je vais le déboguer avec Valgrind sous Linux.



0
votes

Il s'avère que j'ai eu un grave bogue de mémoire dans un endroit complètement différent, même pas mentionné dans la trace.

J'étais en train de déréférencer un pointeur non initialisé.

C'est la deuxième fois que cela se produit.

Ne faites pas confiance aux traces d'Apple lors du débogage des erreurs de mémoire.

Même avec libgmalloc.


1 commentaires

J'ai posté une réponse que j'espère utile. Je parierais que si vous reproduisiez ce crash sans votre gestionnaire de signaux, vous trouveriez les backtraces que vous obtenez soit d'un débogueur soit de ReportCrash (le mécanisme de rapport automatique de crash d'Apple) pour être beaucoup plus utiles.



1
votes

Vous avez indiqué que vous déréférençiez un pointeur non initialisé. Mais, je n'ai pas assez d'informations dans le rapport que vous avez publié pour savoir si cela était (peut-être par chance) nul, ou simplement de la mémoire. Je suppose qu'à un moment donné, vous avez planté avec un EXC_BAD_ACCESS (l'équivalent du signal est SIGBUS ou SIGSEGV , selon).

L'information critique ici était que vous aviez installé un gestionnaire de signaux.

Les gestionnaires de signaux s'exécutent généralement (mais pas toujours) sur le thread qui plante en utilisant la même pile. Le noyau injecte le gestionnaire en utilisant cette fonction _sigtramp . Lors de la livraison du signal, l'état actuel de la pile contenait les informations dont vous aviez besoin pour détecter le mauvais accès à la mémoire. Mais, votre gestionnaire de signaux a été appelé à la place. Donc, il a fonctionné, mutant la pile comme il l'a fait.

Ensuite, votre gestionnaire de signaux s'est terminé d'une manière ou d'une autre. Il est possible de configurer un gestionnaire de signal en utilisant sigaction de telle sorte que l'état du processus soit restauré au moment juste avant l'événement de crash. Je ne sais pas comment votre gestionnaire de signaux a été configuré. Mais, en fin de compte, je vais supposer que le processus a été autorisé à se terminer.

À ce stade, le ReportCrash d'Apple aurait été déclenché et capturerait les backtraces pour tous les threads quel que soit l'état que votre gestionnaire de signaux les a laissé . Ceci est critique, car ce n'est pas nécessairement l'état de plantage.

Ajoutant de la complexité, backtrace_symbols_fd n'est pas du tout sûr à utiliser à partir d'un gestionnaire de signaux. La sécurité asynchrone est un défi et l'exécution de code à partir d'un gestionnaire de signaux est très difficile à obtenir correctement. Il y a très peu de choses que vous pouvez faire en toute sécurité. Je suis sûr, en plus, que backtrace_symbols_fd alloue de la mémoire. Donc, si votre plantage se trouvait quelque part dans l'allocateur de mémoire, et qu'il semble que c'était le cas, vous couriez certainement un risque de blocage. À en juger par le retour en arrière, il semble que c'est exactement ce qui aurait pu se passer. Consultez man sigaction pour plus de détails.

Pire encore, dérouler la pile sur une trame de gestionnaire de signaux est particulièrement difficile à cause de la magie que fait le noyau pour exécuter votre gestionnaire. C'est pourquoi ce cadre ??? est là.

Un résumé:

Sans un gestionnaire de signal installé, ReportCrash d'Apple aurait produit un retour arrière correct (et probablement utile) pour le thread en panne.

La trace de pile que vous avez incluse n'est pas excellente, mais il est difficile de savoir exactement pourquoi. Il semble que backtrace_symbols_fd n'a pas fait un bon travail de déroulement, peut-être parce qu'il était inapproprié à utiliser à partir d'un gestionnaire de signaux, peut-être parce qu'il n'est pas soutenu par un mécanisme de déroulement de pile suffisant pour cette situation. Mais, sans plus d'informations, il m'est difficile de savoir. Je suis surpris que le cadre supérieur soit _sigtramp , cependant. Cela n'a pas beaucoup de sens. Cela me fait penser que quelque chose ne va pas dans le gestionnaire de signaux lui-même. Il est possible de planter une deuxième fois dans votre gestionnaire.

Les backtraces d'Apple (générées par ReportCrash, backtrace_symbols_fd ou callStackReturnAddresses de NSThread, par exemple) peuvent certainement être fiables, à condition que vous soyez prudent de les utiliser dans des contextes sûrs.


4 commentaires

C'est super utile. Merci beaucoup d'avoir passé le temps. C'était un pointeur non initialisé pointant vers des ordures, car j'avais une vérification! = NULL, et cela n'a pas aidé. Je développe cette application dans mon propre langage qui compile en C, et j'ai ajouté l'initialisation automatique du pointeur pour chaque champ pour éviter que cela ne se reproduise :)


Je vais éviter d'appeler backtrace_symbols_fd à partir du gestionnaire sig à partir de maintenant.


Vous êtes les bienvenus! Le projet semble cool, et je comprends pourquoi vous


Vous êtes les bienvenus! Le projet semble cool, et je comprends pourquoi vous voudriez cela. Il est possible de faire un déroulement de pile dans un gestionnaire de signaux, c'est juste beaucoup plus de travail manuel - en particulier sur macOS.