LazarusIDE i Win10 – aplikacja raz na jakiś czas rzuca SIGSEGV przy rozruchu

0

Witam
Używam Lazarusa v1.8.2 z FPC 3.0.4 32bit na Win10 home 64bit.
Przy pisaniu apek mam wyłączoną opcje "Aplikacja graficzna win32" by mieć coś w rodzaju konsoli do wyrzucania wyników poprzez writeln.
Apki to przeważnie jakieś proste interfejsy do baz danych.
Przy uruchomieniu apek (F9) raz jest wszystko OK - apka sie uruchamia - albo nie (jeśli coś pokaszaniłem) - albo wyrzuca błąd :
"Project project1 raised exeption class 'External:SIGSEGV'."
Nawet mimo braku błędów w apce.
Zdarzenie jest nieprzewidywalne i np.: na 4 x F9 bez zmiany kodu 2 razy się normalnie uruchomi a 2 razy się wywali jak zacytowałem wyżej.
Może ktoś coś wie jak z tym sobie poradzić ?

1

Bez kodu trudno wskazać gdzie masz błąd.
SIGSEGV wskazuje na błąd w dostępie do pamięci, czyli np. odwołanie do niezainicjowanego obiektu, wyjście poza rozmiar tablicy ...

0

Bez kodu trudno wskazać gdzie masz błąd.<

Problem w tym iż nie jest tak przy jednej apce tylko przy różnych ale z taką sama konfiguracją

0

Pokaż coś, bo nikt nie będzie zgadywał co masz w kodzie. Podałem dwie najczęstsze przyczyny wyjątku SIGSEGV .

0

Oki :

unit Unit1;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, odbcconn, sqldb, pqconnection, DB, FileUtil, Forms,
  Controls, Graphics, Dialogs, StdCtrls, ComCtrls, CheckLst, Grids, DateUtils,
  LConvEncoding, Windows, Types,base64;

type
  pracownik = record
    id:integer;
    name:string;
    lastlog:string;
    hidden:boolean;
  end;

  { TCardRotate }

  TCardRotate = class(TForm)
    Button1: TButton;
    PQDSource: TDataSource;
    PQTrans: TSQLTransaction;
    PQuery: TSQLQuery;
    wskazplikMSA: TButton;
    importMSA: TButton;
    ENDAPP: TButton;
    MSADSource: TDataSource;
    MSAodbc: TODBCConnection;
    PageControl1: TPageControl;
    podajplikbazy: TOpenDialog;
    MSAQuery: TSQLQuery;
    MSATrans: TSQLTransaction;
    PQCon: TPQConnection;
    listaimportu: TStringGrid;
    zakladkaimportu: TTabSheet;
    TabSheet2: TTabSheet;
    procedure Button1Click(Sender: TObject);
    procedure listaimportuClick(Sender: TObject);
    procedure listaimportuDblClick(Sender: TObject);
    procedure wskazplikMSAClick(Sender: TObject);
    procedure importMSAClick(Sender: TObject);
    procedure ENDAPPClick(Sender: TObject);
    procedure FormShow(Sender: TObject);

  private
    { private declarations }
  public
    { public declarations }
    procedure connect;
    procedure ostatniimport;
    procedure wyswietlpracownikow;
    procedure zmienstan(id:integer;stan:boolean);
  end;

var
  CardRotate: TCardRotate;

implementation

{$R *.lfm}

{ TCardRotate }

procedure TCardRotate.FormShow(Sender: TObject);
begin
  writeln('start');
  importMSA.Enabled:=false;
  connect;
  wyswietlpracownikow;
  ostatniimport;

end;

//----------------------------------------------------
procedure TCardRotate.connect;
 var
  plik: TextFile;
  buildPath,oldPath,conffilepatch,daneconf: string;
begin
  conffilepatch:=GetCurrentDir + '\config.txt';
  oldPath := SysUtils.GetEnvironmentVariable('PATH');
  buildPath := oldPath + ';dll\';
  SetEnvironmentVariable('PATH', PChar(buildPath));
  writeln(conffilepatch);
  AssignFile(plik, conffilepatch);
  try
    try
      reset(plik);
      readln(plik, daneconf);
      PQCon.HostName := daneconf;
      readln(plik, daneconf);
      PQCon.UserName := daneconf;
      readln(plik, daneconf);
      PQCon.Password := daneconf;
      readln(plik, daneconf);
      PQCon.DatabaseName := daneconf;
    finally
      PQCon.Connected := True;
      CloseFile(plik);
    end;
  except
     ShowMessage('Problem z połączeniem z bazą lub plikiem konfiguracyjnym. Plik powinien znajdować się tu: '
      + conffilepatch);
    Application.Terminate;
  end;
end;


//----------------------------------------------------
procedure TCardRotate.importMSAClick(Sender: TObject);
var
  zapMSA,zapPQ: string;
  i, ilewiersz: integer;
  pracownicyMSA: array of pracownik;
begin

  MSATrans.Active:=true;
  zapMSA := 'SELECT e.employeeid as id ,(select iif(max(arisetime)<>null,max(arisetime),2) from eventrecord where e.employeeid=employeeid ) as lastlog, e.employeename as name  FROM Employee e order by e.employeeid';
  MSAQuery.SQL.Text := zapMSA;
  MSADSource.DataSet := MSAQuery;
  MSADSource.DataSet.Active := True;
  MSADSource.DataSet.Refresh;
  ilewiersz := MSADSource.DataSet.RecordCount;
  SetLength(pracownicyMSA,ilewiersz+1);
  writeln('dorekordu');
  for i := 0 to ilewiersz  do
    begin
       writeln('rek',i);
         pracownicyMSA[i].name:=CP1250ToUtf8(MSADSource.DataSet.FieldByName('name').Value);
         pracownicyMSA[i].id:=MSADSource.DataSet.FieldByName('id').Value;
         pracownicyMSA[i].lastlog:=FormatDateTime('YYYY-MM-DD',MSADSource.DataSet.FieldByName('lastlog').Value);
         MSADSource.DataSet.Next;
    end;
  MSAQuery.Clear;
  MSAQuery.Close;
  MSADSource.DataSet.Active:=false;
  MSADSource.DataSet.Close;

  for i := 0 to ilewiersz  do
  begin
    zapPQ:='INSERT INTO recorder.user (iduser, iddep, name, hidden, lastlog) VALUES ('+inttostr(pracownicyMSA[i].id)+', '+inttostr(0)+', '''+pracownicyMSA[i].name+''',false,'''+pracownicyMSA[i].lastlog+''') ON CONFLICT (iduser) DO UPDATE SET name = excluded.name, iddep = excluded.iddep, lastlog = excluded.lastlog;';
    PQTrans.Active := False;
    PQuery.sql.Text := zapPQ;
    PQTrans.StartTransaction;
    PQuery.ExecSQL;
    PQTrans.Commit;
    PQuery.Clear;
    PQuery.Close;
  end;
  wyswietlpracownikow;
end;

procedure TCardRotate.ostatniimport;
var
  i,ilewiersz:integer;
  czekstr,zapPQ:string;
begin
    zapPQ:='select count(*) as ilo,max(data) as lastrec from  recorder.record';
    PQuery.sql.Text := zapPQ;
    PQDSource.DataSet := PQuery;
    PQDSource.DataSet.Active := True;
    PQDSource.DataSet.Refresh;
    if (PQDSource.DataSet.FieldByName('ilo').Value>0) then
        begin
        writeln(PQDSource.DataSet.FieldByName('lastrec').Value);
        end;
    PQDSource.DataSet.Active:=false;
    PQDSource.DataSet.Close;
    PQuery.Clear;
    PQuery.Close;
end;


procedure TCardRotate.wyswietlpracownikow;
var
  i,ilewiersz:integer;
  czekstr,zapPQ:string;
begin
    writeln('Z PQ');
    zapPQ:='select * from recorder.user order by name';
    PQuery.sql.Text := zapPQ;
    PQDSource.DataSet := PQuery;
    PQDSource.DataSet.Active := True;
    PQDSource.DataSet.Refresh;
    ilewiersz := PQDSource.DataSet.RecordCount;
    writeln(ilewiersz);
    listaimportu.RowCount:=ilewiersz+1;
    for i := 1 to ilewiersz  do
    begin
      czekstr:='◻';
      if (PQDSource.DataSet.FieldByName('hidden').Value=true) then czekstr:='🗹';
      listaimportu.Cells[0,i]:=czekstr;   //'🗹◻  '
      listaimportu.Cells[1,i]:=PQDSource.DataSet.FieldByName('name').Value;
      listaimportu.Cells[2,i]:=datetostr(PQDSource.DataSet.FieldByName('lastlog').Value);
      listaimportu.Cells[3,i]:=inttostr(PQDSource.DataSet.FieldByName('iduser').Value);
      listaimportu.Cells[4,i]:=booltostr(PQDSource.DataSet.FieldByName('hidden').Value);
      PQDSource.DataSet.Next;
    end;
    PQDSource.DataSet.Active:=false;
    PQDSource.DataSet.Close;
    PQuery.Clear;
    PQuery.Close;
end;

procedure TCardRotate.wskazplikMSAClick(Sender: TObject);
var
    dbconstr:string;
begin
  if podajplikbazy.Execute then
  begin
    dbconstr:='DBQ='+podajplikbazy.FileName;
    MSAodbc.Params.Add(dbconstr);
    MSAodbc.Connected:=true;
   importMSA.Enabled:=true;
  end;
end;

procedure TCardRotate.listaimportuDblClick(Sender: TObject);
var
grid: TStringgrid absolute Sender;
wyb,gridcli: integer;
stan:boolean;
czekstr:string;
begin
  grid := Sender as TStringgrid;
  gridcli := grid.Row;
  writeln(grid.Row,' ',grid.Col);
  wyb:=strtoint(listaimportu.Cells[3,gridcli]);
  stan:=strtobool(listaimportu.Cells[4,gridcli]);
  zmienstan(wyb,stan);
end;


procedure TCardRotate.listaimportuClick(Sender: TObject);
var
grid: TStringgrid absolute Sender;

begin

  grid := Sender as TStringgrid;
  writeln(grid.Row,' ',grid.Col);
end;

procedure TCardRotate.Button1Click(Sender: TObject);
var
  zapMSA,zapPQ: string;
  strtemp:string;
  i, ilewiersz: integer;
  khem,id,cid,oid,dim,tim:string;
begin
  if podajplikbazy.FileName <> '' then
  begin
    MSATrans.Active:=true;
    zapMSA := 'select top 40 EventType,EmployeeID,CtrlID,Ordinal,AriseTime,format(arisetime,''short date'') as sdata,format(arisetime,''long time'') as stime from EventRecord where EventType=1   order by AriseTime';
    MSAQuery.SQL.Text := zapMSA;
    MSADSource.DataSet := MSAQuery;
    MSADSource.DataSet.Active := True;
    MSADSource.DataSet.Refresh;
    ilewiersz := MSADSource.DataSet.RecordCount;

    writeln('dorekordu ', ilewiersz);
    for i := 0 to ilewiersz-1  do
      begin
           id:=inttostr(MSADSource.DataSet.FieldByName('EmployeeID').Value);
           cid:=inttostr(MSADSource.DataSet.FieldByName('CtrlID').Value);
           oid:=inttostr(MSADSource.DataSet.FieldByName('Ordinal').Value);
           tim:=FormatDateTime('hh:nn:ss',MSADSource.DataSet.FieldByName('stime').Value);
           dim:=datetimetostr(MSADSource.DataSet.FieldByName('sdata').Value);
           khem:=inttostr(DateTimeToFileDate (MSADSource.DataSet.FieldByName('AriseTime').Value));

           strtemp:=khem+id+cid+oid;
           writeln('TS: ',khem,' D: ',dim,' T: ',tim,' ID U: ',id,' C ID: ',cid,' Ord: ',oid,' zlep: ',strtemp,' = ',EncodeStringBase64(strtemp));

           MSADSource.DataSet.Next;
      end;
    MSAQuery.Clear;
    MSAQuery.Close;
    MSADSource.DataSet.Active:=false;
    MSADSource.DataSet.Close;
     {
    for i := 0 to ilewiersz  do
    begin
      zapPQ:='INSERT INTO recorder.user (iduser, iddep, name, hidden, lastlog) VALUES ('+inttostr(pracownicyMSA[i].id)+', '+inttostr(0)+', '''+pracownicyMSA[i].name+''',false,'''+pracownicyMSA[i].lastlog+''') ON CONFLICT (iduser) DO UPDATE SET name = excluded.name, iddep = excluded.iddep, lastlog = excluded.lastlog;';
      PQTrans.Active := False;
      PQuery.sql.Text := zapPQ;
      PQTrans.StartTransaction;
      PQuery.ExecSQL;
      PQTrans.Commit;
      PQuery.Clear;
      PQuery.Close;
    end;
    wyswietlpracownikow;
      }

  end
  else showmessage('Nie wskazano pliku bazy do importu !!');

end;



procedure TCardRotate.zmienstan(id:integer;stan:boolean);
var
zapPQ:string;
sortCol:integer;
sortOrder:TSortOrder;
begin
   zapPQ:='UPDATE recorder.user SET hidden='+booltostr(not(stan),true)+' WHERE iduser='+inttostr(id);
     PQTrans.Active := False;
     PQuery.sql.Text := zapPQ;
     PQTrans.StartTransaction;
     PQuery.ExecSQL;
     PQTrans.Commit;
     PQuery.Clear;
     PQuery.Close;
     sortCol:=listaimportu.SortColumn;
     if (sortCol=-1) then sortCol:=1;
     sortOrder:=listaimportu.SortOrder;
     writeln('s: ',sortCol,' t: ',sortOrder);
     wyswietlpracownikow;
     listaimportu.SortColRow(true,sortCol);
     listaimportu.SortOrder:=sortOrder;
end;

function iledniwm(miesiac, rok: integer): integer;
begin
  if ((miesiac = 4) or (miesiac = 6) or (miesiac = 9) or (miesiac = 11)) then
  begin
    iledniwm := 30;
  end
  else if (miesiac = 2) then
  begin
    if (((rok mod 4) = 0) and ((rok mod 100) <> 0)) or ((rok mod 400) = 0) then
    begin
      iledniwm := 29;
    end
    else
    begin
      iledniwm := 28;
    end;
  end
  else
  begin
    iledniwm := 31;
  end;
end;

procedure TCardRotate.ENDAPPClick(Sender: TObject);
begin
  MSAodbc.Connected:=false;
  PQCon.Connected := false;
  Application.Terminate;   // -------------------------------exit
end;

function DecodeStringBase64(const s:string):String;

var instream,outstream : TStringStream;
    decoder : TBase64DecodingStream;
begin
  instream:=TStringStream.Create(s);
  try
    outstream:=TStringStream.Create('');
    try
      decoder:=TBase64DecodingStream.create(instream,bdmmime);
      try
         outstream.copyfrom(decoder,decoder.size);
         outstream.position:=0;
         result:=outstream.readstring(outstream.size);
      finally
        decoder.free;
        end;
    finally
     outstream.free;
     end;
  finally
    instream.free;
    end;
end;

function EncodeStringBase64(const s:string):String;

var outstream : TStringStream;
    encoder : TBase64EncodingStream;
begin
  outstream:=TStringStream.Create('');
  try
    encoder:=TBase64EncodingStream.create(outstream);
    try
      encoder.write(s[1],length(s));
    finally
      encoder.free;
      end;
    outstream.position:=0;
    result:=outstream.readstring(outstream.size);
  finally
    outstream.free;
    end;
end;

end.
0

Szukał bym błędu w dostępie do tablicy pracownicyMSA.
Nie podoba mi się ten fragment kodu, dlaczego rozmiar tablicy jest większy o jeden od liczby wierszy w datasetcie ?

  SetLength(pracownicyMSA,ilewiersz+1);
  writeln('dorekordu');
  for i := 0 to ilewiersz  do
    begin
       writeln('rek',i);
         pracownicyMSA[i].name:=CP1250ToUtf8(MSADSource.DataSet.FieldByName('name').Value);
         pracownicyMSA[i].id:=MSADSource.DataSet.FieldByName('id').Value;
         pracownicyMSA[i].lastlog:=FormatDateTime('YYYY-MM-DD',MSADSource.DataSet.FieldByName('lastlog').Value);
         MSADSource.DataSet.Next;
    end;

EDT1
masz kilka akcji podpięte pod buttony, napisz która z nich generuje wyjątek

EDT2
podobnie jest ze stringgridem listaimportu, którego liczba wierszy też jest większa o jeden od liczby wierszy w datasecie

0
  1. Ponieważ ilość wierszy jest liczone od 0 a SetLength od 1 (zdaje się ) , ale
    2.Tak jak zauważyłeś większość zdarzeń dzieje się po interakcji z użytkownikiem - a wyjątek występuje zanim zobaczę wygenerowane okno apki (widać tylko pustą konsole)
0

Ad. 1
Nie widzisz że liczba iteacji pętli for jest większa o jeden od liczby wierszy w datasecie ?
Zrób tak :

  SetLength(pracownicyMSA,ilewiersz);
  writeln('dorekordu');
  for i := 0 to high(pracownicyMSA)  do
    begin
       writeln('rek',i);
         pracownicyMSA[i].name:=CP1250ToUtf8(MSADSource.DataSet.FieldByName('name').Value);
         pracownicyMSA[i].id:=MSADSource.DataSet.FieldByName('id').Value;
         pracownicyMSA[i].lastlog:=FormatDateTime('YYYY-MM-DD',MSADSource.DataSet.FieldByName('lastlog').Value);
         MSADSource.DataSet.Next;
    end;

Ogólnie to masz bałagan w indeksowaniu zarówno tablic jak i wierszy w StringGridzie, raz wypełniasz od elementu o indeksie zero, innym razem od elementu o indeksie jeden

0

Ad.1. Właściwie efekt jest taki sam - ale jak wspomniałem - ta operacja jest wykonywana po interwencji użytkownika.
Ad.2. to co wyżej - listaimportu ma wiersz z nagłówkami
Ponownie dodam : wyjątek występuje zanim zobaczę wygenerowane okno apki (widać tylko pustą konsole)

Dodatkowo jest taki oto kod na początku

procedure TCardRotate.FormShow(Sender: TObject);
begin
  writeln('start'); 

Ja tego start nie widze

0

więc szukaj błędu w metodzie FormShow

0

procedure TCardRotate.FormShow(Sender: TObject);
begin
writeln('start');

moment, to masz apkę konsolową czy okienkową ?
nie wiem jak jest w LAzarusie, ale pod Delphi zwykłe konsolowe writeln() w standardowej apce okienkowej generuje błąd I/O
Na początek umiesc na formie komponent Tmemo i pozamieniaj

writeln('cośtam');

na

memo1.lines.add('cośtam');
0

wyjątek występuje zanim zobaczę wygenerowane okno apki (widać tylko pustą konsole)

masz puste okno ponieważ FormShow wywala się na writeln() i dalsza część kodu metody nie wykonuje się

0

To apka stricte okienkowa - tak jak pisałem - wyłączyłem opcje "Aplikacja graficzna win32" by widzieć konsole.
Z dodatkowych ciekawostek które dopiero przed chwilą zauważyłem. Jeśli nie zmienię kodu a użyje F9 , IDE sprawdza kod ale nie kompiluje nowego EXE, mimo to potrafi się wywalić, więc problem leży po stronie IDE i(lub) Win 10 oraz praw (?) do uruchomienia apki - samą apke mogę uruchamiać bezpośrednio n-razy i nic - działa.

0
grzegorz_so napisał(a):

moment, to masz apkę konsolową czy okienkową ?
nie wiem jak jest w LAzarusie, ale pod Delphi zwykłe konsolowe writeln() w standardowej apce okienkowej generuje błąd I/O

A w Lazarusie mamy taki ficzer, że jeśli stworzymy dowolną aplikację okienkową, ale w ustawieniach projektu odznaczy się opcję Win32 gui application (przełącznik -WG), to aplikacja nadal będzie okienkowa, ale przy rozruchu dodatkowo zostanie otwarta konsola.

Dzięki temu można bez problemu sprawdzać wykonanie programu okienkowego, i jednocześnie móc za pomocą WriteLn wyświetlać w konsoli dane przydatne podczas testów manualnych. Bardzo fajna sprawa. ;)


@titako: jeśli o Twój problem chodzi, to uruchom ten program pod debuggerem (włącz w ustawieniach projektu opcję Generate debugging info for GDB i wyłącz optymalizacje), a następnie sprawdź w którym miejscu leci wyjątek.

0

A w Lazarusie mamy taki ficzer, że jeśli stworzymy dowolną aplikację okienkową, ale w ustawieniach projektu odznaczy się opcję Win32 gui application (przełącznik -WG), to aplikacja nadal będzie >okienkowa, ale przy rozruchu dodatkowo zostanie otwarta konsola.

W delphi tez tak można, ale standardowa okienkowa apka nie ma takiej właściwości.
Aby ją uzyskać należy w kodzie głównym projektu dodać

{$APPTYPE CONSOLE}
0

@furious programming - postąpiłem według twoich wskazówek - jak na razie się nie wywala :) - optymalizacja była na poziomie 3

0

Sam nigdy nie używam innych optymalizacji niż tej podstawowej (przyjaznej debuggerowi – -O1). Być może problem leży właśnie w procesie optymalizacji i generowany jest wadliwy kod – trzeba by potestować, sprawdzić co się tam dzieje (dość czasochłonne).

0

A masz jakiś sprawdzony preset np. na wersje produkcyjną kompilacji - co by plik zbyt duży nie był ?
Przeważnie pisze małe apki "pomocnicze" i fajnie by było, by mała "funkcjonalnie" apka miała mniej niż 15MB .

1

To że (nie)optymalizacja pomaga oznacza tyle że błąd masz, tylko nie wiadomo gdzie.
Dodałbym tracing skoro to nieduża apka (w każdej funkcji logowanie na wejściu i na wyjściu).
Dzięki temu będziesz wiedział w której funkcji program się wywala.
Namierzenie wadliwej linijki będzie wtedy dziecinnie proste.

0

No to spróbowałem tak jak napisał @vpiotr - efekt jest mizerny - troszkę zmodyfikowałem procedure by tracker miał co robić zanim użyje WriteLn - jak widać na obrazku błąd wyskakuje zanim cokolwiek ruszy :/

Przechwytywanie4.png

Dlatego wnioskuje iż to chyba jakieś zgrzyty między IDE a W10

2

Chyba warto przeczytać ten wątek:
https://forum.lazarus.freepascal.org/index.php?topic=6225.0

(tam jeden z problemów to był Comodo Firewall, tylko to stary wątek)

1
titako napisał(a):

Przeważnie pisze małe apki "pomocnicze" i fajnie by było, by mała "funkcjonalnie" apka miała mniej niż 15MB .

Aplikacje zajmują dużo (np. 17MB), dlatego że 90% objętości stanowią symbole debuggera. Wersja produkcyjna nie powinna zawierać informacji dotyczących debugowania (ani symboli, ani kodu).

W oknie ustawień projektu odznacz opcję generowania danych dla debuggera – rozmiar apki spadnie do 2MB. Symbole te można też usunąć za pomocą strip.exe, automatycznie lub całkowicie ręcznie. Po drugie, usuń z kodu wszystkie niepotrzebne rzeczy – fragmenty służące do testowania, niepotrzebne moduły, stałe, zmienne, funkcje, klasy itd. Możesz też użyć agresywnej optymalizacji, mającej na celu zmniejszyć objętość kodu wynikowego.

Dodatkowo, jeśli baaardzo zależy Ci na małych plikach wynikowych, możesz pousuwać zbędne zasoby za pomocą edytora zasobów, a także skompresować plik wykonywalny np. za pomocą UPX. Ten dość sporo potrafi ścisnąć (opcja -9). Ale ma też wady, również związane z nadgorliwością antywirusów, z czym musisz się liczyć.

A masz jakiś sprawdzony preset np. na wersje produkcyjną kompilacji - co by plik zbyt duży nie był ?

Oprócz tego co napisałem wyżej – choć nie wszystkiego używam – nie.

Średniej wielkości aplikacja okienkowa (~50kLoC) w wersji produkcyjnej zajmuje nieco ponad 2MB (debug nieco ponad 23MB), więc nie ma sensu jej zmniejszać. Dwa megabajty dziś to tyle co nic. ;)

1 użytkowników online, w tym zalogowanych: 0, gości: 1