Heftiger Bug in RAD Studio 2007 mit auto_ptr und Delphi-Klassen?
-
Am sichersten wäre es sogar, wenn du im Konstruktor pattern den this-Zeiger zuordnest und im Destruktor prüfst, ob pattern == this ist.
Das Problem hatte ich übrigens auch, als ich eine eigenschaft definiert hab, der ich als index- Element ein Klassenobjekt übergebe. Im klassenobjekt wurde Speicher dynamisch allokiert.
Codeguard meinte immer, das im Destruktor des Klassenobjekts auf bereits freigegebenen Speicher zugegriffen wird. Nach genauen Verfolgen über den Debugger hab ich rausbekommen, das eine Kopie des Klassenobjektes in der Eigenschaft angelegt wird, dies erfolgt aber nicht über einen Kopierkonstruktor (obwohl er definiert wurde), sondern einfach durch simples Kopieren des Klassenmembers. Als dann die Kopie zerstört wurde, wurde demzufolge auch der vom Originalobjekt angelegte Speicher mit zerstört (weil nämlich der Destruktor der Kopie aufgerufen wurde), so dass das Originalobjekt also dann auf bereits freigegeben Speicher zugriff.
Mit Hilfe eines solchen "Kennzeichens" wie dein Pattern habe ich das dann so gelöst:Vorher:
[cpp] __fastcall TMyObjekt::TMyObjekt(void *data,int len) { m_data = new BYTE[len]; memmove(m_data,data,len); } __fastcall TMyObjekt::~TMyObjekt() { delete []m_data; } [/cpp]
Nach Änderung:
[cpp] __fastcall TMyObjekt::TMyObjekt(void *data,int len) : m_assigned(this) { m_data = new BYTE[len]; memmove(m_data,data,len); } __fastcall TMyObjekt::~TMyObjekt() { if (m_assigned == this) { delete []m_data; } } [/cpp]
-
7H3 N4C3R schrieb:
Das "lustige" ist ja auch noch, dass 61487 und 61519 auf fixed stehen, aber kein Resolved in Version angegeben ist und mein RAD-Studio das December Update + den April Hotfix enthält und die Fehler allesamt reproduzierbar sind. Ich find's erschreckend, wie man sich damit an die Öffentlichkeit trauen kann.
Wenn die Build-Nummer nicht da steht, bedeutet das i.d.R., daß das Problem für die nächste, noch nicht öffentliche Version behoben wurde; es ist somit nur für die Öffentlichkeit nicht sichtbar. Dementsprechend sollte das Problem in C++Builder 2009 behoben sein.
-
Äähm... soll das heißen, dass für den C++ Builder 2007 kein Fix zu erwarten ist?
-
Das steht zu befürchten.
-
Holy Shit...
in dem Falle kommt eigentlich nur noch BackPort auf BCB 5 in Frage... in der Hoffnung, dass der Fehler dort nicht auftritt. An den Pimpls kann ich was machen, aber nicht an den anderen Klassen. Es kracht auch noch an weiteren Stellen. Wenn es tatsächlich ein Fehler in der Anwendung sein sollte, habe ich keine Ahnung, wie ich mit dem [Frust]unfähigen[/Frust] Debugger noch was finden soll.
-
Burkhi schrieb:
Am sichersten wäre es sogar, wenn du im Konstruktor pattern den this-Zeiger zuordnest und im Destruktor prüfst, ob pattern == this ist.
Das Problem hatte ich übrigens auch, als ich eine eigenschaft definiert hab, der ich als index- Element ein Klassenobjekt übergebe. Im klassenobjekt wurde Speicher dynamisch allokiert.
Codeguard meinte immer, das im Destruktor des Klassenobjekts auf bereits freigegebenen Speicher zugegriffen wird. Nach genauen Verfolgen über den Debugger hab ich rausbekommen, das eine Kopie des Klassenobjektes in der Eigenschaft angelegt wird, dies erfolgt aber nicht über einen Kopierkonstruktor (obwohl er definiert wurde), sondern einfach durch simples Kopieren des Klassenmembers. Als dann die Kopie zerstört wurde, wurde demzufolge auch der vom Originalobjekt angelegte Speicher mit zerstört (weil nämlich der Destruktor der Kopie aufgerufen wurde), so dass das Originalobjekt also dann auf bereits freigegeben Speicher zugriff.
Mit Hilfe eines solchen "Kennzeichens" wie dein Pattern habe ich das dann so gelöst:Vorher:
[cpp] __fastcall TMyObjekt::TMyObjekt(void *data,int len) { m_data = new BYTE[len]; memmove(m_data,data,len); } __fastcall TMyObjekt::~TMyObjekt() { delete []m_data; } [/cpp]
Nach Änderung:
[cpp] __fastcall TMyObjekt::TMyObjekt(void *data,int len) : m_assigned(this) { m_data = new BYTE[len]; memmove(m_data,data,len); } __fastcall TMyObjekt::~TMyObjekt() { if (m_assigned == this) { delete []m_data; } } [/cpp]
Ist das der vollständige Code? Ich sehe da weder Copy Constructor noch Assignment Operator. Wird von der Klasse abgeleitet? Kannst du vielleicht ein Minimalbeispiel posten, das den Fehler reproduziert?
Gruß,
Doc
-
7H3 N4C3R schrieb:
Holy Shit...
in dem Falle kommt eigentlich nur noch BackPort auf BCB 5 in Frage...
Nicht so hastig
Übrigens läßt ein wesentlicher Teil des Exception-Handling in der im Quelltext mitgelieferten RTL anpassen. Möglicherweise kann der Fehler sogar dort behoben werden.
7H3 N4C3R schrieb:
Wenn es tatsächlich ein Fehler in der Anwendung sein sollte, habe ich keine Ahnung, wie ich mit dem [Frust]unfähigen[/Frust] Debugger noch was finden soll.
Mit dem Debugger bist du doch recht weit gekommen. Nimm dir einen Branch des Projektes, in dem es auftritt, und entferne nach und nach so viel wie möglich. Sobald du hinreichend wenig eigenen Code drin hast, schau ich gerne mal rein.
@Burkhi: für ein Minimalbeispiel deines Problems wäre ich auch dankbar.
-
Naja, ist leider etwas schwierig. In ein paar Wochen soll die neue Version rausgehen und wir kämpfen mit so einem üblen Fehler, und auch leider nicht der einzige und letzte. Die Möglichkeiten die mir bleiben sind nur, entweder den Grund des Fehlers zu finden (um dann vielleicht festzustellen, dass ich nichts tun kann), oder den Code so oder so wieder ans Laufen zu kriegen.
Das mit der RTL klingt mal recht gut. Zumindest ein gewisses Grundwissen über SEH habe ich, das könnte ich mir mal anschauen.
Und der Debugger...
Der Debugger blieb bei der Access Violation immer in einem falschen Stackframe hängen. Darauf, dass dort ein totes Objekt zerstört wird, bin ich nur durch eine Portion Glück und einen "zufälligen" Haltepunkt im Destruktor gekommen.
Btw - weißt du eigentlich, wie ich den Debugger dazu bekommen kann, im "C++-Mode" mir auch Delphi-Sourcen anzuzeigen und mich durch diese durchsteppen zu lassen? Oder ist das überhaupt nicht möglich? Diese Möglichkeit vermisse ich aus meinen Delphi-Zeiten ziemlich.Wie dem auch sei, wenn ich was neues rausfinde, werd ich's hier reinschreiben.
-
Hallo,
7H3 N4C3R schrieb:
Und der Debugger...
Der Debugger blieb bei der Access Violation immer in einem falschen Stackframe hängen. Darauf, dass dort ein totes Objekt zerstört wird, bin ich nur durch eine Portion Glück und einen "zufälligen" Haltepunkt im Destruktor gekommen.
Der Stackframe sollte eigentlich richtig sein, nur landest du nach einer AV meist in irgendeiner Kernel-Funktion. In diesem Fall gehe ich gewöhnlich in die erste von mir implementierte oder im Quelltext einsehbare Funktion des Stacks, setze einen Breakpoint auf die Zeile vor der markierten, lasse das Programm nochmal ablaufen und lokalisiere die AV durch Single-Stepping.
7H3 N4C3R schrieb:
Btw - weißt du eigentlich, wie ich den Debugger dazu bekommen kann, im "C++-Mode" mir auch Delphi-Sourcen anzuzeigen und mich durch diese durchsteppen zu lassen? Oder ist das überhaupt nicht möglich? Diese Möglichkeit vermisse ich aus meinen Delphi-Zeiten ziemlich.
Versuche mal, die Runtime-Packages zu deaktivieren; das reichte in C++Builder 2006 aus. Evtl. gibt es sogar in den Projektoptionen eine Einstellung dafür (schau mal nach "Use debug DCUs" oder ähnlichem); im Zweifelsfall setze den Library-Pfad im Debug-Mode, sofern er das noch nicht ist, auf $(BDS)\lib\debug.
-
audacia|off schrieb:
Der Stackframe sollte eigentlich richtig sein, nur landest du nach einer AV meist in irgendeiner Kernel-Funktion. In diesem Fall gehe ich gewöhnlich in die erste von mir implementierte oder im Quelltext einsehbare Funktion des Stacks, setze einen Breakpoint auf die Zeile vor der markierten, lasse das Programm nochmal ablaufen und lokalisiere die AV durch Single-Stepping.
Ist er leider nicht.
Der Stacktrace sieht eigentlich immer so aus:
:200079e0 rtl100.@System@@LStrClrqqrpv + 0xc :32823b68 ; C:\\WINDOWS\\system32\\CC3280MT.DLL [ca. 8 mal] :008fab45 __ExceptionHandler + 0x1E :7c91378b ntdll.RtlConvertUlongToLargeInteger + 0x46 :7c91eafa ntdll.KiUserExceptionDispatcher + 0xe :32823cee ; C:\\WINDOWS\\system32\\CC3280MT.DLL :32825bf1 CC3280MT.@_ThrowExceptionLDTCqpvt1t1t1uiuiuipuct1 + 0x31
Das ist wie's aussieht eine Bereinigungsfunktion von AnsiString.
"LStrClr$qqrpv" (ich bekomme nicht mal demangled names... :() wird allerdings von meinentwegen A::~A -> B::~B aufgerufen, das wird aber nicht angezeigt. Auch ein Singlestep nach der Exception durchläuft diese Destruktoren nicht. Mit einem Breakpoint, wenn man weiß wo, hält der Debugger aber vor dem Absturz wieder an. Reinsteppen tut er von alleine nicht. Debug kompiliere ich natürlich immer, nur um das auszuschließen.Ich habe mal die Option "langsame Exception-Epiloge" aktiviert, die wohl ein Inlining des Exception-Codes verhindern soll, hat allerdings nix gebracht.
Selbst die Funktion, die das throw gemacht hat, erscheint nichtmal im Stacktrace, sondern ihr Aufrufer. Die IDE bleibt trotz fehlendem Eintrag im Stacktrace auf dem throw stehen - nach der AV allerdings auf dem Aufrufer der werfenden Funktion, die mit der AV nix zu tun hat. Also irgendwie garnicht hilfreich.
audacia|off schrieb:
]Versuche mal, die Runtime-Packages zu deaktivieren; das reichte in C++Builder 2006 aus. Evtl. gibt es sogar in den Projektoptionen eine Einstellung dafür (schau mal nach "Use debug DCUs" oder ähnlichem); im Zweifelsfall setze den Library-Pfad im Debug-Mode, sofern er das noch nicht ist, auf $(BDS)\lib\debug.
Runtime-Packages, ja die könnte ich mal deaktivieren. Den alten "Use debug DCUs" habe ich nicht gefunden, allerdings gibt es die Linker-Option "mit vollständigen Debug-Informationen linken", die im Debug-Build sowieso immer an ist. Der Library-Pfad im Debug-Compile enthält lib\debug ja auch schon von alleine.
-
7H3 N4C3R schrieb:
Der Stacktrace sieht eigentlich immer so aus:
:200079e0 rtl100.@System@@LStrClrqqrpv + 0xc :32823b68 ; C:\\WINDOWS\\system32\\CC3280MT.DLL [ca. 8 mal] :008fab45 __ExceptionHandler + 0x1E :7c91378b ntdll.RtlConvertUlongToLargeInteger + 0x46 :7c91eafa ntdll.KiUserExceptionDispatcher + 0xe :32823cee ; C:\\WINDOWS\\system32\\CC3280MT.DLL :32825bf1 CC3280MT.@_ThrowExceptionLDTCqpvt1t1t1uiuiuipuct1 + 0x31
Und sonst kommt nichts mehr?
Falls ja: was passiert, wenn du in _ThrowExceptionLDTC einen Breakpoint setzt und dann die gleiche Situation herbeiführst; bekommst du dann einen ausführlicheren Stacktrace mit den aufrufenden Funktionen?Weiter ist es hier möglicherweise hilfreich, die dynamische RTL zu deaktivieren, dann bekommst du im Debug-Mode AFAIK auch Symbolnamen für die RTL-Funktionen. So könntest du z.B. in _CatchCleanup() mal einen Breakpoint setzen und schauen, wo und wieso genau dein Objekt ohne Notwendigkeit destruiert wird.
-
audacia|off schrieb:
Falls ja: was passiert, wenn du in _ThrowExceptionLDTC einen Breakpoint setzt und dann die gleiche Situation herbeiführst; bekommst du dann einen ausführlicheren Stacktrace mit den aufrufenden Funktionen?
Vor _ThrowExceptionLDTC ist ein korrekter Stacktrace da (bis auf die Funktion, die die Exception wirft - die fehlt). Sorry, wenn ich da unverständlich war. (Ich meinte den Übergang von CC3280MT.DLL (__get_lock_level) zu LStrClr, die verantwortlichen Destruktoren dazwischen erscheinen nicht im Trace)
audacia|off schrieb:
Weiter ist es hier möglicherweise hilfreich, die dynamische RTL zu deaktivieren, dann bekommst du im Debug-Mode AFAIK auch Symbolnamen für die RTL-Funktionen. So könntest du z.B. in _CatchCleanup() mal einen Breakpoint setzen und schauen, wo und wieso genau dein Objekt ohne Notwendigkeit destruiert wird.
Okay, das habe ich soweit gemacht. Ich sehe nun auch die richtigen Symbolnamen. Allerdings zeigt mir der Debugger die zugehörigen Sourcen nicht an. Ich habe bereits ($BDS)\source\cpprtl\Source\except und weitere Kandidaten in den Suchpfad des Debuggers gesetzt.
Allerdings ist mir unklar, was ich sonst vielleicht noch machen muss, um den Debugger davon zu überzeugen, mir den Source anstatt des ASM-Listings anzuzeigen. Weißt du, was ich hier tun muss?
Edit 3000: Ich baue mal gerade eine Debug-Version der RTL. Anscheinend ist die garnicht per Default vorhanden...
Edit 4000: HA - mit einer händisch gebauten Debug-RTL sehe ich auch die Sourcen und kann steppen.Anbei ein neuer Stacktrace:
:200079e0 rtl100.@System@@LStrClr$qqrpv + 0xc :0090B848 callDestructor(dst=:0012F488, dstType=:00402E28, dstFlags=0, dtorAddr=:0090F160, dtorMask=3, dtVbases=1) :0090C47B destroyOneObject(varAddr=:0012F488, varType=:00402E28, flags=1, vbFlag=0, errPtr=:0012F40C) :0090C5E8 destroyVariable(varAddr=:0012F488, varType=:00402E28, flags=0, dtorCnt=1, vbFlag=1, newBP=1242532, errPtr=:0012F40C) :0090C7DC destroyVariable(varAddr=:0012F488, varType=:0047455C, flags=0, dtorCnt=1, vbFlag=0, newBP=1242532, errPtr=:0012F40C) :0090C4F7 destroyBases(varAddr=:00000001, flags=0, nblLast=:0046EF20, nblFirst=:0046EF20, count=1242248, isVirt=0, newBP=1242532, errPtr=:0012F40C) :0090C6E9 destroyVariable(varAddr=:0012F488, varType=:0046EEE8, flags=0, dtorCnt=1, vbFlag=1, newBP=1242532, errPtr=:0012F40C) :0090CDD8 dtorCleanup(dttPtr=:00967A18, dtcMin=1, errPtr=:0012F40C, newBP=1242532) :0090C20D local_unwind(errPtr=:0012F40C, target=84) :0090DD95 ___ExceptionHandler(excPtr=:0000005C, errPtr=:0012F40C, ctxPtr=:0012E600, _dspPtr=:0012E5A0, OSEsp=1238244, =1238276, =0, =0, =0, flags=3) :00901229 __ExceptionHandler + 0x1E :7c91378b ntdll.RtlConvertUlongToLargeInteger + 0x46 :7c91eafa ntdll.KiUserExceptionDispatcher + 0xe :0090B9CE tossAnException(tpid=:004733D4, throwAddr=:24DF94B8, friendList=NULL, cctrAddr=:00473434, cctrMask=1, flags=0, throwLine=639, throwFile=:0094211D, throwPC=4665968, reThrow=0, errPtr=:0012EA38, userRegisters=:0012E9DC) :0090D889 _ThrowExceptionLDTC(tpid=:0012EEAC, throwAddr=:03415E40, friendList=:00000001, cctrAddr=:0012EC08, cctrMask=9441803, flags=9715880, throwLine=1239876, throwFile=NULL, errPtr=:24DF5AB0)
-
Was hast du nun in dieser Frage eigentlich unternommen?
Kann es außerdem sein, daß dein Problem in diesem QC-Report beschrieben wird? Falls ja, dann ist es in C++Builder 2009 behoben (wie dieser Liste zu entnehmen ist).
-
Ah, schon wieder ganz verdrängt das Thema. Work work.
Ich kann dir leider nicht sagen, ob das wirklich der Fehler ist (die Beschreibung klingt aber ziemlich danach), da ich weder über's Web noch über die QualityCentral-Anwendung an möglicherweise vorhandene Attachments komme.
Was ich unternommen habe... tja, einen unkonventionellen Workaround. Das Problem war letztendlich wie folgt:
A X::f() { // do something that may throw } ... void Y::g() { X x; A a = x.f(); ///<<< }
In der kommentierten Zeile wurde der Destruktor für A aufgerufen, obwohl dort nie ein A gelebt hat.
Mein Workaround sieht jetzt so aus:
bool X::tryF() { bool result = true; try { // something that may throw } catch( ...) { result = false; } return result; } ... void Y::g() { X x; if( tryF() { A a = x.f(); } }
Gruselig - funktioniert aber. Da ich nie ein reproduzierbares Minimalbeispiel zusstande bekommen habe, ist hier nix weiter passiert - da der Workaround es nun auch tut. Ich weiß, ich weiß, ganz ganz böse - aber was soll man machen, wenn es keine Aussicht auf einen Fix gibt...
-
7H3 N4C3R schrieb:
aber was soll man machen, wenn es keine Aussicht auf einen Fix gibt...
Wie gesagt, ist das Problem in C++Builder 2009 behoben. Falls ihr euer Projekt nicht gleich dorthin migrieren wollt, so sei darauf hingewiesen, daß es, anders als beim Delphi-Compiler, möglich sein könnte, den C++-Compiler (bcc32.exe und comp32x.dll) aus C++Builder 2009 in C++Builder 2007 einzusetzen. Zumindest ließ sich damals der 2007er-Compiler in C++Builder 2006 verwenden.
-
Naja, bei den Erfahrungen, die wir mit dem 2007er Builder gemacht haben, werde ich hier wohl sicherlich auf Granit beißen, mal den 2009er auszuprobieren. Außerdem müsste der dann natürlich auch erstmal angeschafft werden.
-
7H3 N4C3R schrieb:
Naja, bei den Erfahrungen, die wir mit dem 2007er Builder gemacht haben
Was war denn da noch, abgesehen von dem in diesem Thread diskutierten Bug?
-
Unter anderem war das das DBCtrlGrid, das einen Bug mit DBCheckboxen hatte bzw. hat.
Das Verhalten von AnsiString.SubString( 0, ...) hat sich geändert. Im BCB5 gab das den String zurück, im 2007er Builder war der String nun leer. Die Zeile ist sicherlich nicht die Ausgeburt von sauberem Code (ist auch nicht von mir...), allerdings hat die Suche nach diesem Problem ziemlich lange gedauert. Etwas ähnliches gab es AFAIR auch mit AnsiString.Pos.
Dazu kommt dann die miserable Hilfe. Größtenteils benutze ich noch die Hilfe vom BCB5. Ich glaube, die Beschreibung für z.B. die MaskEdit-Masken ist erst im vierten HelpUpdate hinzugekommen. Exotischere Sachen wie z.B. alles aus der TypInfo.hpp ist so allgemein geschrieben, dass ich es auch eh schon vorher wusste.
Die Abstürze des Builders werden anscheinend auch von Release zu Release schlimmer (u.A. mitten im Betrieb ohne jegliche Meldung). CodeCompletion benutze ich fast garnicht - in ca. 50% geht sie entweder garnicht erst auf oder liefert einen internen Compiler-Fehler.
Und, sie ist tierisch langsam. Natürlich ist CodeCompletion in C++ eine verdammt schwierige Sache, aber die Konkurrenz meistert das IMHO um Längen besser.
Finde ich schon ziemlich enttäuschend für ein kommerzielles Produkt. Wobei ich durchaus mal gerne den 2009er ausprobieren würde. Aber nun ja.
-
7H3 N4C3R schrieb:
Dazu kommt dann die miserable Hilfe. Größtenteils benutze ich noch die Hilfe vom BCB5.
So ging es auf der Microsoftschiene einigen auch bei der MSDN (Ich hatte die vom VS6 und vom VS2005 parallel laufen).
Zudem habe ich in der Codegear Hilfe (2007) von Zeit zu Zeit in der Hilfe Fälle von "Link not found"...
7H3 N4C3R schrieb:
Die Abstürze des Builders werden anscheinend auch von Release zu Release schlimmer (u.A. mitten im Betrieb ohne jegliche Meldung).
Laut meinen Chef sind sie weniger geworden, aber ich schlage mich auch mit einigen problemen rum (Die von dir erwähnte CodeCompletion, diverse sporadische Fehlermeldungen [Gerade bei der von mir häufig verwendeten Suche, da ich das Projekt noch nicht kenne] und auch mal den ein oder anderen Absturz; In einigen Fällen war anschließend der Rechner neu zu starten damit etwas funktioniert).
7H3 N4C3R schrieb:
Natürlich ist CodeCompletion in C++ eine verdammt schwierige Sache, aber die Konkurrenz meistert das IMHO um Längen besser.
Das würde ich zwar nicht komplett behaupten (auch wenn ich Intellisense im Vergleich tatsächlich etwas besser finde), was mir aber fehlt sind auch externe Anbieter für solche Funktionalitäten (für den Visual Studio kann man notfalls das benötigte wie Refactoring, Codekomplettierung... nachrüsten - für den C++ Builder habe ich noch nichts entsprechendes gefunden, wohl zu kleiner Markt).
7H3 N4C3R schrieb:
Finde ich schon ziemlich enttäuschend für ein kommerzielles Produkt. Wobei ich durchaus mal gerne den 2009er ausprobieren würde. Aber nun ja.
Den 2009 würde ich auch gerne ausprobieren, mir kommt es aber nicht auf die "neuen" Features an (die sind zwar nett, aber für und kein Umstiegsgrund), wichtiger wären mir Veränderungen unter der Haube (Stabilität, vielleicht auch mal zumindestens ein funktionierendes Refactoring...). Nur zu solchen Änderungen habe ich leider keine Aussage gefunden (Mein Chef wäre zu einem Umstieg bereit, sieht es aber genauso wie ich, das die genannten neuen Features jetzt nicht den Preis rechtfertigen, sondern man dann doch lieber erst die darauffolgende Version wieder anschaut... Zwar einigermaßen aktuell sein, aber nicht immer jede Version mitnehmen).
Wobei ohnehin abzuwarten ist wie die Zukunft vom C++ Builder wird...
cu André
-
Das mit den neuen Features sehe ich ähnlich. Das jetzige Projekt kriegen wir eh nicht auf Unicode hochgezogen, das würde einer Neuentwicklung entsprechen.
Insbesondere die Verbesserungen und Bugfixes am Compiler und hinsichtlich Stabilität würden mich aber auch interessieren. Der 2007er Compiler ist leider immernoch ein gutes Stück von Standardkonformität weg.
Mit der SOAP-Implementierung hatten wir übrigens auch Ärger, da hat die SOAP-Runtime das Parameter-Matching der Antwort mit einem Return-Parameter und mehreren Out-Parametern nicht richtig hinbekommen. Als Workaround funktioniert zumindest das Deaktivieren des Return-Parameters.