Exception non prise en charge dans le fil de débogage de Rad Studio

j'ai une application de grande taille qui a récemment commencé à montrer un comportement assez étrange lors de l'exécution dans un débogueur. Tout d'abord, les bases:

OS: Windows 7 64-bit.
Application: Multithreaded VCL app with many dlls, bpls, and other components.
Compiler/IDE: Embarcadero RAD Studio 2010.

le symptôme observé est celui-ci: alors que le débogueur est attaché à mon application, certaines tâches provoquent le plantage de l'application. Les détails sont en outre perplexes: mon application s'arrête avec un message Windows disant, " Yourrapplication a cessé de fonctionner."Et il offre heureusement d'envoyer une minidump à Microsoft.

A noter: l'application ne se bloque pas lorsque le débogueur n'est pas attaché. De plus, le débogueur n'indique aucune exception ou autre problème pendant que l'application est en cours d'exécution.

définir et passer à travers les points de rupture semble affecter le point où l'application s'effondre, mais je soupçonne que c'est un symptôme de débogage d'un thread autre que celui problématique.

ces accidents se produisent aussi sur les ordinateurs de mes collègues, avec le même comportement que j'observe. Cela m'amène à ne pas soupçonner une installation ratée de quelque chose sur mon ordinateur en particulier. Mes collègues confrontés à ce problème exécutent également Windows 7 64-bit. Je n'ai pas de collègues qui ne connaissent pas le problème.

j'ai analysé un certain nombre de décharges complètes des accidents. J'ai découvert que l'échec se produisait au même endroit à chaque fois. Voici les données d'exception des dumps (il est toujours le même, sauf bien sûr the ThreadId):

Exception Information

ThreadId:         0x000014C0
Code:             0x4000001F Unknown (4000001F)
Address:          0x773F2507
Flags:            0x00000000
NumberParameters: 0x00000001
    0x00000000

Google révèle que le Code 0x4000001F est en fait STATUS_WX86_BREAKPOINT. Microsoft le décrit sans ménagement comme " un code d'état d'exception qui est utilisé par le sous-système D'émulation Win32 x86."

Voici les détails de la pile (qui ne semblent pas varier):

0x773F2507: ntdll.dll+0x000A2507: RtlQueryCriticalSectionOwner + 0x000000E8
0x773F3DAB: ntdll.dll+0x000A3DAB: RtlQueryProcessLockInformation + 0x0000020D
0x773D2ED9: ntdll.dll+0x00082ED9: RtlUlonglongByteSwap + 0x00005C69
0x773F3553: ntdll.dll+0x000A3553: RtlpQueryProcessDebugInformationRemote + 0x00000044
0x74F73677: kernel32.dll+0x00013677: BaseThreadInitThunk + 0x00000012
0x77389F02: ntdll.dll+0x00039F02: RtlInitializeExceptionChain + 0x00000063
0x77389ED5: ntdll.dll+0x00039ED5: RtlInitializeExceptionChain + 0x00000036

il vaut la peine de noter qu'il semble y avoir une fonction épilogue à 0x773F24ED, ce qui suggère plutôt que le propriétaire de la section Trquerycritique est un hareng rouge. De même, un fonction epilog jette le doute sur RtlQueryProcessLockInformation. Le 0x5c69 offset jette le doute sur le RtlUlonglongByteSwap. Les autres symboles semblent légitimes.

plus précisément, la note RtlpQueryProcessDebugInformationremote semble légitime. Certaines personnes sur internet (http://www.cygwin.com/ml/cygwin-talk/2006-q2/msg00050.html) semblent penser qu'il est créé par le débogueur de recueillir des informations de débogage. Cette théorie semble judicieuse pour moi, puisqu'il ne semble apparaître lorsque le débogueur est attaché.

comme toujours, quand quelque chose casse, quelque chose change ce qui le casse. Dans ce cas, ce quelque chose charge dynamiquement une nouvelle dll. Je peux faire en sorte que le crash cesse de se produire en ne chargeant pas dynamiquement la dll particulière. Je ne suis pas convaincu que le chargement de la dll soit lié, mais voici les détails, juste au cas où:

la source dll est C. Voici les options de compilation qui ne sont pas définies par défaut:

Language Compliance: ANSI
Merge duplicate strings: True
Read-only strings: True
PCH usage: Do not use
Dynamic RTL: False

(Le Projet Options de dire est Faux par défaut pour la Dynamique de RTL, mais il était Vrai que quand j'ai créé le projet dll.)

la dll est chargée avec LoadLibrary et libérée avec FreeLibrary. Tout semble aller bien avec le chargement et le déchargement du module. Cependant, peu de temps après que la Bibliothèque est déchargée (avec FreeLibrary), le thread susmentionné bloque le programme. Pour le débogage, j'ai supprimé tous les appels réels à la bibliothèque (y compris, pour plus de tests, DllMain). Aucune combinaison d'appels ou pas les appels, ou pas D'appel, ou quoi que ce soit d'autre ont semblé modifier le comportement du crash de quelque manière que ce soit. Il suffit de charger et de décharger la dll invoque le crash plus tard.

de plus, le fait de changer la dll pour utiliser la dynamique RTL entraîne également la fin de l'écrasement du thread du débogueur. Cela n'est pas souhaitable parce que la dll compilée devrait vraiment être utilisable sans le temps D'exécution CodeGear disponible. De plus, la taille de la dll est importante. Le code C contenu dans la dll ne fait pas usage de bibliothèques. (Il comprend pas d'en-têtes, même des en-têtes de bibliothèque standard. Pas de malloc, pas de printf, rien. Il ne contient que des fonctions qui dépendent exclusivement de leurs entrées et ne nécessitent pas de répartition dynamique.) Il n'est pas non plus souhaitable parce que "corriger" un bogue en changeant des choses jusqu'à ce qu'il fonctionne sans comprendre pourquoi il fonctionne n'est vraiment jamais un bon plan. (Cela a tendance à entraîner la réapparition de bogues et d'étranges pratiques de codage. Mais vraiment, à ce stade, si Je ne trouve rien d'autre, je peux admettre la défaite sur ce compter.)

et enfin, ma question peut être liée à l'une de ces questions:

toute idée ou suggestion serait appréciée.

14
demandé sur Community 2011-06-20 20:33:51

4 réponses

j'ai résolu le problème mentionné ci-dessus en utilisant une version modifiée de la solution de contournement PatchINT3, qui a été publiée en 2007 pour BDS 2006:

procedure PatchINT3;
const
  INT3: Byte = $CC;
  NOP: Byte = ;
var
  NTDLL: THandle;
  BytesWritten: DWORD;
  Address: PByte;
begin
  if Win32Platform <> VER_PLATFORM_WIN32_NT then
    Exit;
  NTDLL := GetModuleHandle('NTDLL.DLL');
  if NTDLL = 0 then
    Exit;
  Address := GetProcAddress(NTDLL, 'RtlQueryCriticalSectionOwner');
  if Address = nil then
    Exit;
  Inc(Address, $E8);
  try
    if Address^ <> INT3 then
      Exit;

    if WriteProcessMemory(GetCurrentProcess, Address, @NOP, 1, BytesWritten)
      and (BytesWritten = 1) then
      FlushInstructionCache(GetCurrentProcess, Address, 1);
  except
    //Do not panic if you see an EAccessViolation here, it is perfectly harmless!
    on EAccessViolation do
      ;
  else
    raise;
  end;
end;

appeler cette routine une fois que vous avez chargé la DLL dans votre thread. Le patch corrige un point de rupture de l'utilisateur dans ntdll.dll Version 6.1.7601.17725 et la modifie en NOP.

S'il n'y a pas de point de rupture de l'utilisateur (INT3 (=$CC) opcode) à l'adresse attendue, la routine patch ne fait rien et sort.

j'Espère que aide,

Andreas

Note de bas de page

La source originale de PatchINT3 peut être trouvée here:

http://coding.derkeiler.com/Archive/Delphi/borland.public.delphi.non-technical/2007-01/msg04431.html

Footnote2

La même fonction en C++:

void PatchINT3()
{
   unsigned char INT3   = 0xCC;
   unsigned char NOP    = 0x90;

   if (Win32Platform != VER_PLATFORM_WIN32_NT)
   {
      return;
   }

   HMODULE ntdll = GetModuleHandle(L"NTDLL.DLL");
   if (ntdll == NULL)
   {
      return;
   }

   unsigned char *address = (unsigned char*)GetProcAddress(ntdll,
      "RtlQueryCriticalSectionOwner");
   if (address == NULL)
   {
      return;
   }

   address += 0xE8;

   try
   {
      if (*address != INT3)
      {
         return;
      }

      unsigned long bytes_written = 0;
      if (WriteProcessMemory(GetCurrentProcess(), address, &NOP, 1,
         &bytes_written) && (bytes_written == 1))
      {
         FlushInstructionCache(GetCurrentProcess, address, 1);
      }
   }
   catch (EAccessViolation &e)
   {
      //Do not panic if you see an EAccessViolation
      //here, it is perfectly harmless!
   }
   catch(...)
   {
      throw;
   }
}
9
répondu Andreas Bormann 2013-06-22 09:17:03

Juste une idée...

peut-être que vous devez vous refermer sur le fil qui s'écrase. L'état que vous l'observation semble un peu après l'erreur réelle.

tout d'abord, Vos traces de pile me semblent incomplètes. Quelle est la racine de base de la pile de ce fil? Quelle était l'origine de ce thread?

et, dans le débogueur VS il y a la possibilité de casser sur les exceptions, (Debug->Exceptions...->[Ajouter.)] Puis tous les fils gèleront au moment où le exception se produit. Je ne sais pas à propos de RAD, mais le truc pour le faire programmatically semble être Waitfordedebugevent ().

je pourrais me tromper mais je pense qu'il y a de bonnes chances que le bug soit dans le débogueur pas votre code. Dans ce cas, une solution de contournement uggly est IMHO entièrement pardonnable. Bonne chance!

0
répondu Adam 2011-08-04 10:14:54

Je ne peux pas répondre parce que je ne vois pas le code...

Mais...

1) dans Borland C++ au moins avec C++ de BDS il peut y avoir un problème prouvable avec la fonction realloc dans la bibliothèque multithread. Votre code C++ utilise-t-il realloc?

2) Il est plus que probable que la pile que vous affichez est appelée à cause du fait que votre code a effectivement cliqué sur "CALL BAD_ADRESS" et cela peut se produire à la suite d'un bogue dans votre propre code. En d'autres termes, dans la DLL vous load, il y a probablement une fonction qui fait quelque chose qui écrase le code exécutable dans votre programme avec junk, et puis quand la section maintenant junk s'exécute, elle s'écrase.

une autre façon est si quelque chose dans la dll c++ modifie la pile ci-dessous où elle s'exécute, et puis votre code frappe que plus tard.

3) Vérifiez les paramètres des indicateurs CPU pour votre DLL. Les bibliothèques Borland utilisent parfois des pavillons CPU conflictuels lors de l'entrée et vous pourriez avoir besoin de sauvegarder et restaurer avant d'appeler dans la DLL. Par exemple, si vous appelez un plugin VST fait avec C++ de Delphi et que vous ne positionnez pas les options correctement, vous pouvez obtenir une Division ultérieure par zéro erreur du plugin VST qui a été compilé avec cette exception désactivée.

0
répondu 2012-09-12 19:52:42

Nous avons eu le même problème aujourd'hui. Dans notre cas, le crash se produit si vous avez un point de rupture après appel à TOpenDialog - >Execute () (qui utilise la boîte de dialogue de shell32.dll I think) (Windows 7 x64, C++ Builder XE2)

après avoir désinstallé iCloud (v2.1.0.39), la question a été rejetée.

Malheureusement, nous sommes encore à la recherche dans le même problème nos clients ont parfois avec notre produit de libération sous Windows Vista. Après avoir sélectionné le fichier en utilisant TOpenDialog, le l'application se bloque dans gdiplus.dll avec Violation de L'accès, la suppression de l'iCloud semble également résoudre le problème.

0
répondu Niki 2013-01-25 12:12:26