Zugriffsfehler auf "std::map" in " _Orphan_ptr(const _Nodeptr _Ptr)"



  • @elmut19 Für mich sieht einfach nach wie vor so aus, als ob dir die manuelle Speicherverwaltung um die Ohren fliegt. Kombiniert mit Multithreading ein großer Spaß.

    Da wir deinen Ringspeicher überhaupt nicht kennen, können wir nichts dazu sagen. Vlt ist ein Fehler in dem Ringspeicher, oder du benutzt den anders, als dein Vorgänger das vorgesehen hat?



  • @elmut19 sagte in Zugriffsfehler auf "std::map" in " _Orphan_ptr(const _Nodeptr _Ptr)":

    Ein "delete" auf ein Element ergibt Heap-Fehler. (Habs auch mit "delete[]" probiert.)
    Und wenn ich auf die "deletes" verzichte (Ich mache ja jeweils ein explizites "delete" auf jeden einzelnen dieser Ringspeicher, welches auch funktioniert, jedenfalls ohne Absturz!),

    😖

    Und wenn ich auf die "deletes" verzichte

    Mal eine Frage: Hast du das delete also auskommentiert, nutzt aber trotzdem noch new?



  • @Schlangenmensch sagte in Zugriffsfehler auf "std::map" in " _Orphan_ptr(const _Nodeptr _Ptr)":

    Für mich sieht einfach nach wie vor so aus, als ob dir die manuelle Speicherverwaltung um die Ohren fliegt. Kombiniert mit Multithreading ein großer Spaß.

    Den Verdacht habe ich auch. Manuelle Speicherverwaltung, übermäßige Zeigerverwendung, offene Architektur fliegen hier vermutlich um die Ohren.



  • @Quiche-Lorraine @Schlangenmensch @wob
    Ich blick ja schon lange nix mehr.
    Mit der "std::map" hatte ich eher weniger Probleme.
    Kann gut sein, dass ich ins neue auch irgend einen Scheiss reingebaut habe, zusätzlich.
    Aber das Problem scheint trotzdem ähnlich zu sein.

    Aber im Ringspeicher wird viel "alloc()", " HeapCreate()" und auch "HeapLock()" verwendet und natürlich auch wieder freigegeben.
    Aber vielleicht verträgt sich das nicht mit den zusätzlichen, neuen "new()".

    Und wenn ich im Vorgänger-Programm nachsehe, dann wurde dieser RIngspeicher schon vor knapp 30 Jahren zusammengebastelt
    und unverändert ins neue und jetzige Projekt (auch schon bald 20 Jahre alt) übernommen.



  • Hast du denn jetzt mal eine statische Codeanalyse gemacht?



  • @DocShoe
    CppCheck lässt sich leider nicht installieren.
    Es gibt unter dem Menü "Analysieren" in VS2019 den Punkt "Codeanalyse".
    Der brachte keine Erkenntnis.

    In stackoverflow habe ich auch gelesen, dass man dieses "HeapZeugs" am Besten meidet.
    Es wird ja ein wirklich separater Speicher allokiert.



  • @elmut19 sagte in Zugriffsfehler auf "std::map" in " _Orphan_ptr(const _Nodeptr _Ptr)":

    In stackoverflow habe ich auch gelesen, dass man dieses "HeapZeugs" am Besten meidet.

    Wie meinst du das? Du willst doch dynamisch Speicher allozieren. Wie würdest du das denn ohne Heap machen?



  • @Schlangenmensch sagte in Zugriffsfehler auf "std::map" in " _Orphan_ptr(const _Nodeptr _Ptr)":

    @elmut19 sagte in Zugriffsfehler auf "std::map" in " _Orphan_ptr(const _Nodeptr _Ptr)":

    In stackoverflow habe ich auch gelesen, dass man dieses "HeapZeugs" am Besten meidet.

    Wie meinst du das? Du willst doch dynamisch Speicher allozieren. Wie würdest du das denn ohne Heap machen?

    Er meint spezielle Heap-Funktionen aus der WinApi, die allokieren nochmal woanders als der Standard-C++ Heap.



  • @Tyrdal Ah, danke 🙂 Ich nutze zu selten direkt die WinAPI und vergesse gerne das der Thread hier unter WinAPI liegt.



  • @elmut19 sagte in Zugriffsfehler auf "std::map" in " _Orphan_ptr(const _Nodeptr _Ptr)":

    In stackoverflow habe ich auch gelesen, dass man dieses "HeapZeugs" am Besten meidet.
    Es wird ja ein wirklich separater Speicher allokiert.

    Wieso das? HeapAlloc/HeapFree verhaltem sich das doch genauso wie new/delete.
    Du könnest damit anfangen, diese manuelle Speicherverwaltung in smart pointer zu verpacken. Dummerweise muss man bei HeapAlloc einen Heap angeben, dessen Speicher man anfordert. Wenn in eurer Anwendung nur der Programmheap benutzt wird könnte man da so umsetzen (ungetestet):

    struct HeapDeleter
    {
       HeapDeleter() = default;
    
       // TO DO:
       // Member für Heap-Handle mitführen, falls ein anderer Heap als der ProcessHeap benutzt wird.
       void operator()( void* mem )
       {
          ::HeapFree( ::GetProcessHeap(), 0, mem );
       }
    };
    
    void f()
    {
       DWORD const mem_size = 1024;
       std::shared_ptr<void> mem = std::shared_ptr<void>( ::HeapAlloc( ::GetProcessHeap(), 0,  mem_size ), HeapDeleter() );
    }
    


  • @DocShoe
    Danke für die Idee mit dem Smartpointer. Ich muss mich damit befassen.
    Vielleicht hilft das ja.
    Ob ich den Ringspeicher derart umschreiben kann oder das schon auf das Objekt anwenden kann, in dem der Ringspeicher drin ist (Zusammenfassung meiner Übertragungsdaten), muss ich auch überlegen.
    Ich sehe das Problem nun hauptsächlich hierin, dass die "std::map" innerhalb ihrer Verwaltung diesen externen Speicher sieht, den sie an besagter Stelle nicht mehr kennt.
    Und dieser Ringspeicher ist/war für mich bisher einfach eine Blackbox.
    Ich sehe auch wenig Möglichkeiten, das Ding umzuprogrammieren.
    Aber es wäre auch noch eine Idee.
    Vielleicht kann ich mit Smartpointer auch sowas wie einen Wrapper konstruieren, damit die "map" das gar nicht sieht.



  • @elmut19

    Das Problem scheint zu sein, dass irgendwo Memory Corruption stattfinded, weil an allen möglichen Stellen Resourcen angefordert und freigegeben werden. Ein doppeltes delete/free/HeapFree an einer Stelle kann dazu führen, dass dir an einer anderen Stelle alles um die Ohren fliegt. Diesen Wildwuchs solltest du erstmal zähmen, und da sind smart pointer ein starkes Werkzeug. Wenn dann natürlich wieder rohe Zeiger durch die Gegend gereicht werden helfen dir auch smart pointer nicht weiter, musst mal gucken, ob das mit euren Schnittstellen funktioniert.
    Ein Memory Profiler sollte das eigentlich erkennen, aber wenn du schon cppCheck nicht installieren kannst hast du vermutlich auch keinen Profiler.



  • @elmut19. Warum kannst du cppcheck nicht installieren? Es muß ja nicht auf deinem Entwicklungsrechner sein - irgendein Rechner, auf dem du auf die Sourcen zugreifen kannst (bzw. kopieren kannst - oder per USB-Stick o.ä.)...



  • @Th69 sagte in Zugriffsfehler auf "std::map" in " _Orphan_ptr(const _Nodeptr _Ptr)":

    Es muß ja nicht auf deinem Entwicklungsrechner sein - irgendein Rechner, auf dem du auf die Sourcen zugreifen kannst (bzw. kopieren kannst - oder per USB-Stick o.ä.)...

    Hmm, der Entwicklungsrechner sollte es schon sein.

    Denn man könnte ja auch Tools ala Application Verifier oder Dr. Memory über das Programm laufen lassen.

    Und Application Verifier funktioniert am besten unter dem Visual Studio Debugger.


  • Mod

    Ist das überhaupt zielführend? Es wird rauskommen, dass es an der wilden manuellen Speicherverwaltung liegt. Das wissen wir auch jetzt schon. Es hilft ja nicht, dass dann spezifisch auf eine oder mehrere Stellen gezeigt wird, wegen denen es schief geht. Denn was will man dann machen? Nur diese korrigieren? Und vor allem: Wie genau? Dazu braucht man tiefgehendes Verständnis wie manuelle Speicherverwaltung richtig geht, das offensichtlich nicht vorhanden ist.

    Das ganze manuelle Gefrickel gehört einfach komplett raus. Optimalerweise auch nicht nur indem man bloß alles durch Smartpointer ersetzt (auch wenn das wahrscheinlich schon viel helfen würde), sondern indem man mal gründlich über das Datenmodell nachdenkt, welche Ressourcen man wirklich braucht und wo die Verantwortlichkeiten liegen. Das braucht natürlich viel Wissen und auch einiges an Arbeit. Aber immer noch deutlich weniger Wissen und Arbeit als wenn man das gleiche manuell richtig machen würde.

    Wenn man da jetzt mit wenig Wissen oberflächlich an den unmittelbaren Fehlern schnell herumpfuscht bis es irgendwie läuft, fällt die Bruchbude doch beim nächsten leisen Husten gleich wieder um.



  • @elmut19 sagte in Zugriffsfehler auf "std::map" in " _Orphan_ptr(const _Nodeptr _Ptr)":

    Der gezeigte Code ist grausig. Die Verantwortung für die Speicherverwaltung muss klar definiert sein, und ein paar Regel sind einzuhalten.

    • Rohe Zeiger werden nur als Referenz verwendet. D.h. wenn man diese aus einer Klasse heraus herumreicht, wird darüber niemals Speicherfreigabe, Reallokation oder Allokation gemacht.
    • Wenn es unbedingt notwendig ist, schreibt man Wrapper Klassen, die den Speicher verwalten. Das sollte eigentlich mit C++11 nicht mehr notwendig sein, weil …
    • Resourcen über SmartPointer alloziert und verwaltet werden. Man kann mit eigenen Funktionen Objekte in speziellem Speicher allozieren und die Verantwortung an den SmartPointer übertragen. Das betrifft auch das korrekte Löschen der Objekte mit einer speziellen Funktion. D.h. man kann Objekte im RingSpeicher allozieren, und der SamrtPointer räumt sie dort korrekt ab.
    • Falls notwendig schreibt man für Container der Standardlibrary einen CustomAllocator, der Speicher des Ringspeichers nutzt. Ja, auch das geht.


  • @SeppJ sagte in Zugriffsfehler auf "std::map" in " _Orphan_ptr(const _Nodeptr _Ptr)":

    Ist das überhaupt zielführend? Es wird rauskommen, dass es an der wilden manuellen Speicherverwaltung liegt. Das wissen wir auch jetzt schon.

    Aber wissen dies die Verantwortlichen?

    Wir wissen ja nicht ob das Programm in einer Gruppe entwickelt wird. Und wenn man da sagt, man benötigt ein Code Reengineering, so kommt schnell ein Argument ala "Es hat ja bisher funktioniert, warum sollte da also ein Fehler sein?".

    Und ist dies der Fall, so benötigt @elmut19 erst einmal Argumente für ein umfassendes Code Reengineering. Wenn da ein namenhaftes Tool ala Application Verifier Probleme meldet, so ist dies ein Argument hierfür.

    Ein weiteres Argument dürfte auch der neue Programmierstil (mehr STL, keine Pointer,...) sein, welcher per se weniger fehleranfällig ist.

    Alles Dinge welche ich zuvor schon in diesem Thread sagte.



  • @Quiche-Lorraine sagte in Zugriffsfehler auf "std::map" in " _Orphan_ptr(const _Nodeptr _Ptr)":

    Wenn da ein namenhaftes Tool ala Application Verifier Probleme meldet, so ist dies ein Argument hierfür.

    So eine Argumentation finde ich echt immer furchtbar.


  • Mod

    @wob sagte in Zugriffsfehler auf "std::map" in " _Orphan_ptr(const _Nodeptr _Ptr)":

    @Quiche-Lorraine sagte in Zugriffsfehler auf "std::map" in " _Orphan_ptr(const _Nodeptr _Ptr)":

    Wenn da ein namenhaftes Tool ala Application Verifier Probleme meldet, so ist dies ein Argument hierfür.

    So eine Argumentation finde ich echt immer furchtbar.

    Jupp. Lass uns einem Werkzeug mehr vertrauen als dem zuständigen Entwickler! Wenn der Entwickler so wenig Vertrauen genießt (was ja evtl. sogar gerechtfertigt sein kann, z.B. Neueinsteiger), sollte man ihn nicht alleine an den Code lassen. Wenn ein eventueller Chefentwickler diesen Code als okay befindet, dann ist das ein Problem des Chefentwicklers. Dies ist keine subtil versteckte Designschwäche im Code, über die Experten streiten würden. Dies ist ein Problem das man spätestens nach dem 5. Kapitel eines jeden Anfängerlehrbuchs besser wissen muss.

    Aber auch mit einer dummen Managemententscheidung zum Festhalten an den Fehlern im Code gilt:

    @SeppJ sagte in Zugriffsfehler auf "std::map" in " _Orphan_ptr(const _Nodeptr _Ptr)":

    Denn was will man dann machen? Nur diese korrigieren? Und vor allem: Wie genau?

    Den ganzen Thread lang versuchen wir schon das Problem einzugrenzen oder zu beheben und es gab keinen erkennbaren Fortschritt. Falsche Ressourcenverwaltung ist nun einmal eine Problemklasse, die man nicht so einfach beheben kann.



  • @SeppJ sagte in Zugriffsfehler auf "std::map" in " _Orphan_ptr(const _Nodeptr _Ptr)":

    Jupp. Lass uns einem Werkzeug mehr vertrauen als dem zuständigen Entwickler! Wenn der Entwickler so wenig Vertrauen genießt (was ja evtl. sogar gerechtfertigt sein kann, z.B. Neueinsteiger), sollte man ihn nicht alleine an den Code lassen. Wenn ein eventueller Chefentwickler diesen Code als okay befindet, dann ist das ein Problem des Chefentwicklers. Dies ist keine subtil versteckte Designschwäche im Code, über die Experten streiten würden. Dies ist ein Problem das man spätestens nach dem 5. Kapitel eines jeden Anfängerlehrbuchs besser wissen muss.

    Du versteht das Problem dahinter nicht. EIn Bilderbuch-Projekt habe ich in der Praxis noch nie gesehen. Folgende Dinge fallen mir an vielen Stellen auf:

    • Die Code-Base ist völlig veraltet.
    • Der Kenntniss-Stand der Entwickler ist veraltet, sofern sie überhaupt Entwickler sind.
    • Gute Entwickler findet man nicht oft.
    • Der Kenntniss-Stand des Projektleiters ist veraltet, sofern er überhaupt Entwickler ist.
    • Zeitdruck (TIme To Market) ist oftmals kontraproduktiv bezüglich dem Design.
    • "Nichts hält länger als ein Provisorium." Vorgehensweise (Issue: Code schöner machen sofern Zeit).
    • Wer fremden Code anfasst, muss mit Emotionalitäten rechnen.
    • Wer neue Tools einführen möchte, muss mit Emotionalitäten rechnen.
    • Outsourcing ist oftmals kontroproduktiv bezüglich der Qualität.

    EIn fiktives Beispiel:

    "Ein Maschinenbauer schreibt ein Programm in C in einem katastrophalen Stil. Der Code wird seit Jahren erfolgreich eingesetzt und soll nun erweitert und in einem erweiterten Bereich eingesetzt werden.

    Deswegen soll nun ein unerfahrerer Software-Entwickler diesen Code übernehmen. Und dieser kommt nach ein paar Tage wütend und sagt: Der Code ist scheiße und voller Fehler" Was glaubst du wird ein Projektleiter antworten?

    "Komm beruhige dich. Aber der Code funktioniert seit Jahren problemlos bzw. fehlerfrei, also warum sollte dieser schlecht sein? Und außerdem, der letzte Software-Entwickler wollte dies auch schon ändern und hat dabei einen großen Bock geschossen."


    Wenn du in einer solchen Situation bist, benötigst du eine Menge Erfahrung und eine gewisse Reputation um dagegen zu argumentieren. Und wenn du dann etwas unerfahren bist, musst du halt mittels Tools ala Application Verifier unterstützend argumentieren.

    Und solche Situationen habe ich schon in einigen Bereichen/Firmen gesehen.

    PS:
    Gerade nutze ich das Nordic SDK, eine C SDK zur Ansteuerung von Funk-Chips. Das Merkmal dieser SDK ist das fehlen einer einheitlichen Schnittstelle. Neuere Versionen sind inkompatibel zu vorhergehenden Versionen. Konfiguriert wird das SDK mittels einer 500 kByte großen sdk_config.h Datei, in welchem nur #define Anweisungen stehen, Funktionalitäten sind stellenweise überdimensioniert, da die Hardware sehr limitiert ist (z.B. Programmgröße < 80 kByte),...

    Was soll man da tun? Das potenzielle beheben der Fehler ist nicht einfach, da das SDK riesig ist und manche Funktionen, gerade in Bezug auf Bluetooth LE, nicht trivial sind.


Anmelden zum Antworten