Variable verändert sich

Für Fehler in Lazarus, um diese von anderen verifizieren zu lassen.
Antworten
Mathias
Beiträge: 6194
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Variable verändert sich

Beitrag von Mathias »

Jetzt habe ich ein ganze merkwürdiges Verhalten gefunden.

Code: Alles auswählen

  procedure read_name(w: TWidget; client_data: TXtPointer; call_data: TXtPointer); cdecl;
  var
    push_button: TWidget;
    n: integer;
    Text: PChar;
    cbs: PXmSelectionBoxCallbackStruct;
  begin
    push_button := TWidget(client_data);
    WriteLn('push  ', PtrInt(push_button));
    WriteLn('data  ', PtrInt(client_data));
//    cbs := PXmSelectionBoxCallbackStruct(call_data);
    XtVaGetValues(w, XmNuserData, @n, nil);
    WriteLn('push  ', PtrInt(push_button));
    WriteLn('data  ', PtrInt(client_data));      
Wen ich obigen Code ziemlich früh vom Hauptprogramm aus aufrufe, habe ich folgende Ausgabe.

Code: Alles auswählen

    toplevel := XtVaAppInitialize(@app, 'Demos', nil, 0, @argc, argv, nil, nil);
    read_name(toplevel, Pointer(1234), nil);
// push  1234
// data  1234
// push  1234
// data  1234    
Wir es aber später durch einen CallBack von einem ButtonClick ausgeführt, bekomme ich folgendes:

Code: Alles auswählen

    XtAddCallback(dialog, XmNokCallback, @read_name, w);
// push  23966032
// data  23966032
// push  0
// data  23966032
Und jetzt kommt da interessanteste. Dieser Fehler tritt ein, wen ich Code-Optimierung auf Stufe "0" oder "1" habe.
Stelle ich aber auf Stufe "2" oder höher, funktioniert alles wie es sein soll. Die umgekehrte Variante wär eher zu erwarten, da eine aggressive Optimierung eher zu Fehler führt.

Nur woran das liegen kann, keine Ahnung.
Es ist recht schwierig den Code abzuspecken, da auch noch externe Libs eingebunden sind.

Aber ich wollte es hier posten, wen einer von Euch auch mal fast verzweifelt, weil es Werte unerklärlich verändert.

Wen einer es nachvollziehen will, kann dieses Beispiel ausprobieren, dazu muss aber die Motif-Pakage installiert werden.
https://github.com/sechshelme/Lazarus-M ... modify_btn
Zuletzt geändert von Mathias am Mi 19. Apr 2023, 12:39, insgesamt 2-mal geändert.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

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

Re: Variable verändetr sich

Beitrag von Mathias »

Ich habe es jetzt runtergespeckt, soweit es geht.

Code: Alles auswählen

program project1;
uses
  XmXmStrDefs,
  XmPushB,
  XmSelectioB,
  XTComposite,
  XTIntrinsic;

  procedure read_name(w: TWidget; client_data: TXtPointer; call_data: TXtPointer); cdecl;
  var
    push_button: TWidget;
    n: integer;
  begin
    push_button := TWidget(client_data);
    WriteLn('push  ', PtrInt(push_button));
    WriteLn('data  ', PtrInt(client_data));
    XtVaGetValues(w, XmNuserData, @n, nil);
    WriteLn('push  ', PtrInt(push_button));
    WriteLn('data  ', PtrInt(client_data));
    WriteLn('User data: ', n);
  end;

  procedure pushed(w: TWidget; client_data: TXtPointer; call_data: TXtPointer); cdecl;
  var
    dialog: TWidget;
  begin
    dialog := XmCreatePromptDialog(w, 'notice_popup', nil, 0);
    XtVaSetValues(dialog, XmNuserData, 321);
    XtAddCallback(dialog, XmNokCallback, @read_name, w); // Mit Fehler

    XtManageChild(dialog);
    XtPopup(XtParent(dialog), XtGrabNone);
  end;

var
  toplevel, button1: TWidget;
  app: TXtAppContext;
begin
  toplevel := XtVaAppInitialize(@app, 'Demos', nil, 0, @argc, argv, nil, XmNwidth,320, XmNheight,200,nil);

  read_name(toplevel, Pointer(1234), nil); // Kein Fehler

  button1 := XtVaCreateManagedWidget('PushMe 1', xmPushButtonWidgetClass, toplevel, nil);
  XtAddCallback(button1, XmNactivateCallback, @pushed, nil);

  XtRealizeWidget(toplevel);
  XtAppMainLoop(app);
end.
Nachtrag: Habe es noch auf dem Raspi getestet, da läuft alles Fehlerfrei, egal mit welcher Optimierungs-Einstellung.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

PascalDragon
Beiträge: 830
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: Variable verändetr sich

Beitrag von PascalDragon »

Soweit ich das von anderen Beispielen sehen kann, musst du bei XtNUserData die Liste an Werten mit einem Nil terminieren (so wie beim XtVaGetValues). Andernfalls zieht der Code Werte vom Stack heran, was dann zu unerwartetem Verhalten führt, das sich auch je nach Optimierung ändern kann.
FPC Compiler Entwickler

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

Re: Variable verändetr sich

Beitrag von Mathias »

Soweit ich das von anderen Beispielen sehen kann, musst du bei XtNUserData die Liste an Werten mit einem Nil terminieren
Stimmt, da hast du voll kommen recht, nur leider hat es nichts gebracht. Das nil ging beim abspecken verloren. :roll:
Aber meistens wird dies mit einem SIGSEV quittiert.
Somit muss in meinem Fall das Problem wo anders liegen. Beim original-Code von C habe ich auch mit den -Ox rumgespielt, da hat es keinen Einfluss.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

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

Re: Variable verändert sich

Beitrag von Mathias »

Da bin ich auch etwas schlauer geworden. Das ganze sieht mit nach einem Stack-Problem aus.

Code: Alles auswählen

  var
    push_button: TWidget;
    n: int32;
  begin
    push_button := TWidget(client_data);
    WriteLn('push  ', PtrInt(push_button));  // -> 35274800
    WriteLn('data  ', PtrInt(client_data));  // -> 35274800
    XtVaGetValues(w, XmNuserData, @n, nil);  // -> 0 !
    WriteLn('push  ', PtrInt(push_button));  // -> 35274800
    WriteLn('data  ', PtrInt(client_data)); 
Ersetze ich int32 durch int64, oder noch bessert durch PtrInt, läuft es fehlerfrei.

Das sieht mir danach aus, wie die Variablen nicht in 64Bit Schritten ausgerichtet werden.
Jetzt ist die Frage, ob es C oder Pascal-Seitig ?
Ich denke, eher in C, da die Libs recht alt sind. Wobei der Fehler kommt ja nur wen die Kompileroption kleiner -O2 ist. Und in C läuft es auch ohne Fehler.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

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

Re: Variable verändert sich

Beitrag von Mathias »

Es sieht effektiv nach einem Überschreiben des Stacks aus.
Wen ich userData folgendes mitgebe.

Code: Alles auswählen

    XtVaSetValues(dialog, XmNuserData, $000000FF33333333, nil);
Es wird anstelle von 0, 255 ausgeben, dies würde einem $FF enstsprechen.
Und bei $0000FFFF33333333, kommt 65535 raus, dies wäre das $0000FFFF vorne.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

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

Re: Variable verändert sich

Beitrag von Mathias »

So nun habe ich den Code auf ein Minimum abgespeckt. Es ist nichts mehr vorhanden, was C anbelangt. Und der Fehler ist immer noch da. Bei -O grösser 2, tritt der Fehler nicht auf.

Code: Alles auswählen

program project1;

  procedure GetTest(p: Pointer);
  begin
    PPtrInt(p)^ := 0;
  end;

  procedure read_name(i: PtrUInt);
  var
    p: Pointer;
    n: integer;
  begin
    p := Pointer(i);
    WriteLn(PtrInt(p));
    GetTest(@n);
    WriteLn(PtrInt(p));
    WriteLn('User data: ', n);
  end;

begin
  read_name(1000000000000000000);
end.
Ausgabe:

Code: Alles auswählen

1000000000000000000
999999997191651328
User data: 0
Und jetzt zur Frage, kann es sein, das sich p und n überschneiden ?
Ich dachte immer, bei einem 64Bit OS, werden den einzelnen Variablen im ein 64Bit-Block reserviert.
Ausgenommen bei packed und bitpacked Recorden/Arrays.
Oder habe ich da sonst irgendwas übersehen ?

Nachtrag:
Ich habe noch folgenden test gemacht:

Code: Alles auswählen

    WriteLn('addr n: ',PtrInt(@n)); //-> addr n: 140728453664428
    WriteLn('addr p: ',PtrInt(@p)); //-> addr p: 140728453664432
Die Abweichung ist 4Byte und nicht wie ich immer erwartet habe 8Byte.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Antworten