String in PRecord und heaptrc verursacht SIGSEV

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Mathias
Beiträge: 6919
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

String in PRecord und heaptrc verursacht SIGSEV

Beitrag von Mathias »

Ich wollte mal ausprobieren, wie sich ein String in einem PRecord verhält.
Dabei habe ich es mit folgendem Code getestet. Wen ich nur PChar verwende, funktioniert es wie erwartet auch mit heaptrc.
Aber sobald ein String ins Spiel kommt, dann knallt es, sobald man heaptrc verwendet.

Wieso knallt dies ?

Mir ist bewusst, das man bei späteren Verwendung etwas beachten muss, aber das es schon bei der Zuweisung knallt, verwundert mich.

Das ich das ausprobieren wollte, hängt zusammen, das man vielfach user_data an gebunden C-Funktionen übergeben kann.
Dabei wollte ich das Verhalten mit einem Pascalstring testen.

Code: Alles auswählen

program project1;

uses
  strings, heaptrc;

type
  THuman = record
    Name: pchar;
    s: string;
  end;
  PHuman = ^THuman;

  var
    p:Pointer;

  procedure setHuman;
  var
    h: PHuman;
  begin
    h := GetMem(SizeOf(THuman));
    h^.Name := strnew('Max');
    WriteLn('Hier bumm');
    h^.s := 'bumm'; // Hier knallts mit heaptrc
    WriteLn('io.');
    p:=h;
  end;

procedure printAndFreeHuman;
var
  h: PHuman;
begin
  h:=p;
  WriteLn('Name: ', h^.Name);
  WriteLn('s: ', h^.s);
  Freemem(h^.Name);
  Freemem(h);
  end;

begin
  setHuman;
  printAndFreeHuman;
end.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 1647
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Lazarus Fixes FPC Stable
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: String in PRecord und heaptrc verursacht SIGSEV

Beitrag von fliegermichl »

Wenn man in der Procedure SetHuman statt h := GetMem(SizeOf(THuman))
New(h)
schreibt, dann geht es.

Benutzeravatar
Zvoni
Beiträge: 376
Registriert: Fr 5. Jul 2024, 08:26
OS, Lazarus, FPC: Windoof 10 Pro (Laz 2.2.2 FPC 3.2.2)
CPU-Target: 32Bit
Wohnort: BW

Re: String in PRecord und heaptrc verursacht SIGSEV

Beitrag von Zvoni »

Huh?
Seit wann soll/kann/darf man heaptrc in Uses verwenden?
Ich dachte dass wäre ein grobes no-go, weil heaptrc einen eigenen Memory-Manager hat (welcher dir deshalb wahrscheinlich alles durcheinander bringt)
Ein System sie alle zu knechten, ein Code sie alle zu finden,
Eine IDE sie ins Dunkel zu treiben, und an das Framework ewig zu binden,
Im Lande Redmond, wo die Windows drohn.

Benutzeravatar
Jorg3000
Lazarusforum e. V.
Beiträge: 365
Registriert: So 10. Okt 2021, 10:24
OS, Lazarus, FPC: Win64
Wohnort: NRW

Re: String in PRecord und heaptrc verursacht SIGSEV

Beitrag von Jorg3000 »

fliegermichl hat geschrieben: Do 2. Jan 2025, 14:21 Wenn man in der Procedure SetHuman statt h := GetMem(SizeOf(THuman))
New(h)
schreibt, dann geht es.
Ja richtig, New() ist zu bevorzugen, weil es den reservierten Speicher mit Nullen füllt.
Hingegen kann nach GetMem() der reservierte Speicher unvorhersehbare Werte enthalten.
https://www.freepascal.org/docs-html/rt ... etmem.html

Wenn man dann einen String setzen will, der ein Managed Type ist, wird intern zuerst versucht eine vermeintlich alte String-Zuweisung zu de-referenzieren.
Dabei knallt's, wenn der Speicher nicht zuvor mit Null-Bytes gefüllt worden war.

Mathias
Beiträge: 6919
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: String in PRecord und heaptrc verursacht SIGSEV

Beitrag von Mathias »

fliegermichl hat geschrieben: Do 2. Jan 2025, 14:21 Wenn man in der Procedure SetHuman statt h := GetMem(SizeOf(THuman))
New(h)
schreibt, dann geht es.
Jetzt gehts.
Ja richtig, New() ist zu bevorzugen, weil es den reservierten Speicher mit Nullen füllt.
Manuell scheint es auch zu gehen.

Code: Alles auswählen

    h := GetMem(SizeOf(THuman));
    FillChar(h^, SizeOf(THuman), 0); 
Jetzt sehe ich durch, da Pascal ein komplexe string Verwaltung hat. Mein das Programm, das schon ein String zugewiesen ist, welchen es gar nicht gibt. und will den löschen, daher der Knall.

Was mich trotzdem noch verwundert.
Haben hatte ich jetzt New(h); und unten habe ich es mit FreeMem(h) frei gegeben. Ich habe gar nicht gewusst, das sich die verträgt. New gibt man normalerweise mit Dispose frei.

Das New den das ganze mit "'0" befüllt, dies muss ich mir merken.

Dann nehme ich GetMem nur wen man ein C ähnliches Array befüllen will,
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Mathias
Beiträge: 6919
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: String in PRecord und heaptrc verursacht SIGSEV

Beitrag von Mathias »

Seit wann soll/kann/darf man heaptrc in Uses verwenden?
Das war schon immer so, nur muss man darauf achten, wo es in uses steht.

viewtopic.php?p=146138&hilit=heaptrc#p146138
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Benutzeravatar
Zvoni
Beiträge: 376
Registriert: Fr 5. Jul 2024, 08:26
OS, Lazarus, FPC: Windoof 10 Pro (Laz 2.2.2 FPC 3.2.2)
CPU-Target: 32Bit
Wohnort: BW

Re: String in PRecord und heaptrc verursacht SIGSEV

Beitrag von Zvoni »

Mathias hat geschrieben: Do 2. Jan 2025, 15:54
Seit wann soll/kann/darf man heaptrc in Uses verwenden?
Das war schon immer so, nur muss man darauf achten, wo es in uses steht.

viewtopic.php?p=146138&hilit=heaptrc#p146138
Mir ist klar, dass man heaptrc in Uses verwenden kann, aber ich bin von dem hier ausgegangen: https://wiki.freepascal.org/heaptrc
Warning: Do not add the Heaptrc unit manually. The Heaptrc unit needs to be loaded before Lineinfo and only the compiler can do that. Heaptrc is also a memory manager, so do not try to use any memory manager – including Heaptrc itself – in the uses clause, because that may lead to memory corruption and false results. This topic contains a note with example code to handle such a case in a generic way.
Ein System sie alle zu knechten, ein Code sie alle zu finden,
Eine IDE sie ins Dunkel zu treiben, und an das Framework ewig zu binden,
Im Lande Redmond, wo die Windows drohn.

Benutzeravatar
theo
Beiträge: 10874
Registriert: Mo 11. Sep 2006, 19:01

Re: String in PRecord und heaptrc verursacht SIGSEV

Beitrag von theo »

Mathias hat geschrieben: Do 2. Jan 2025, 15:48 Manuell scheint es auch zu gehen.
So geht's auch:

Code: Alles auswählen

var
  h: PHuman;
begin
  h := GetMem(SizeOf(THuman));
  Initialize(h^);
  h^.Name := strnew('Max');
  h^.s := 'bumm'; // Hier knallts mit heaptrc
  Finalize(h^);
  Freemem(h);
end;

Mathias
Beiträge: 6919
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: String in PRecord und heaptrc verursacht SIGSEV

Beitrag von Mathias »

Initialize und Finalize kenne ich gar nicht. Nicht mal Google findet es. Sind die Funktionen fpc 3.3 vorenthalten ?
Gibt es einen Beschrieb dazu ?

Wen ich es richtig verstehen, würde initialize folgendes Problem auch lösen. viewtopic.php?t=16178
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Benutzeravatar
theo
Beiträge: 10874
Registriert: Mo 11. Sep 2006, 19:01

Re: String in PRecord und heaptrc verursacht SIGSEV

Beitrag von theo »

Mathias hat geschrieben: Do 2. Jan 2025, 19:18 Initialize und Finalize kenne ich gar nicht. Nicht mal Google findet es.
Wieso, ist doch alles da:
https://www.freepascal.org/docs-html/rt ... alize.html
https://www.freepascal.org/docs-html/rt ... alize.html

Mathias
Beiträge: 6919
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: String in PRecord und heaptrc verursacht SIGSEV

Beitrag von Mathias »

Wen ich es richtig verstehe, räumt finalize auch strings innerhalb des records sauber auf. Wen ich FreeMem aufrufe, gibt es wahrscheinlich nur den record selbst frei ?
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Warf
Beiträge: 2122
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: String in PRecord und heaptrc verursacht SIGSEV

Beitrag von Warf »

Als erstes einmal, GetMem, ReallocMem und FreeMem führen keine Management operationen aus, im gegensatz zu New und Dispose.
New füllt nicht nur mit 0, New führt den Initialization Operator aus. Genauso muss man am Ende Dispose verwenden um den Finalization Operator auszuführen.
Wenn man GetMem und FreeMem benutzt, muss man es manuell machen:

Code: Alles auswählen

h:=GetMem(SizeOf(h^));
Initialize(h^);
// H benutzen
//...
// Freen:
Finalize(h^);
FreeMem(h);
Und FillChar ist kein venünftiges Substitut für Initialize, denn Managed types müssen nicht unbedingt mit 0 Initialisiert werden. Das String und Dynamische Arrays dies tun ist eher ein Technisches Detail

PascalDragon
Beiträge: 955
Registriert: Mi 3. Jun 2020, 07:18
OS, Lazarus, FPC: L 2.0.8, FPC Trunk, OS Win/Linux
CPU-Target: Aarch64 bis Z80 ;)
Wohnort: München

Re: String in PRecord und heaptrc verursacht SIGSEV

Beitrag von PascalDragon »

Jorg3000 hat geschrieben: Do 2. Jan 2025, 15:22
fliegermichl hat geschrieben: Do 2. Jan 2025, 14:21 Wenn man in der Procedure SetHuman statt h := GetMem(SizeOf(THuman))
New(h)
schreibt, dann geht es.
Ja richtig, New() ist zu bevorzugen, weil es den reservierten Speicher mit Nullen füllt.
Das beschreibt zwar, was man hier sieht, ist jedoch falsch: New führt GetMem gefolgt von Initialize aus, welches letztlich die Felder mit vewaltetem Typ (und nur die!) auf deren initiale Werte setzt (sei es durch setzen auf 0, Nil, etc. oder durch ausführen des Initialize-Operators bei managed Records).
Mathias hat geschrieben: Do 2. Jan 2025, 15:48
Ja richtig, New() ist zu bevorzugen, weil es den reservierten Speicher mit Nullen füllt.
Manuell scheint es auch zu gehen.

Code: Alles auswählen

    h := GetMem(SizeOf(THuman));
    FillChar(h^, SizeOf(THuman), 0); 
Bitte mach das nicht. Wenn du schon GetMem für einen Typ mit verwalteten Pascal-Typen nutzt, dann nimm bitte Initialize, da nicht alle verwalteten Typen ein Äquivalent zu 0 als Ausgangswert haben.
Mathias hat geschrieben: Do 2. Jan 2025, 15:48 Was mich trotzdem noch verwundert.
Haben hatte ich jetzt New(h); und unten habe ich es mit FreeMem(h) frei gegeben. Ich habe gar nicht gewusst, das sich die verträgt. New gibt man normalerweise mit Dispose frei.
New ist letztlich GetMem plus Initialize und Dispose ist Finalize plus FreeMem. Ein FreeMem auf Speicher, der mit New angelegt wurde, wird also letztlich nur zu einem Speicherleck führen (aber keinem Absturz) und das auch nur, wenn du die Felder auch tatsächlich auf anderen Speicher verweisen (sprich wenn zum Beispiel ein Stringfeld nicht leer ist).
FPC Compiler Entwickler

Mathias
Beiträge: 6919
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: String in PRecord und heaptrc verursacht SIGSEV

Beitrag von Mathias »

Und FillChar ist kein venünftiges Substitut für Initialize, denn Managed types müssen nicht unbedingt mit 0 Initialisiert werden.
Wen ich das so lese, kann man den Bezeichner noch anders befüllen, als nur mit $00 ?
ZB. Das ein Vector aus 3 single 1,, 0, 1 hat ?

Da ist mir auch noch etwas aufgefallen, wieso kommt da nicht 3mal 0 raus, sondern die Zufallszahlen ?

Code: Alles auswählen

  procedure Test;
  var
    v: TVec;
  begin
    v := [Random, Random, Random];
    Initialize(v);
    WriteLn(v[0]: 4: 2,'  ', v[1]: 4: 2,'  ', v[2]: 4: 2);
  end;
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Warf
Beiträge: 2122
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: String in PRecord und heaptrc verursacht SIGSEV

Beitrag von Warf »

Initialize initialisiert nur managed Typen, auf felder die nicht managed sind hat's keinen Effekt.

Außerdem ist initialize auf einem bereits initialisiertem record aufzurufen potenziell ein Fehler, z.b. da dabei existierende Strings mit 0 überschrieben werden ohne das der refcount verringert wird.

Für bereits initialisierten Speicher solltest du Default benutzen

Antworten