Lazarus

Programming => General => Topic started by: senglit on October 23, 2020, 09:04:38 am

Title: [SOLVED] SetLength error
Post by: senglit on October 23, 2020, 09:04:38 am
Hi all,

I got a procedure in which SetLength is used. But everytime when it run to SetLength line, the program corrupted. I guess it is a very simple stupid problem. But I just can't figure it out. Who can remind me something about it?

Code: Pascal  [Select][+][-]
  1. type
  2.   PTrafficRecord = ^TrafficRecord;
  3.   TrafficRecord = record
  4.     Time:TDateTime;        
  5.     Interval:integer;      
  6.     Volume:integer;          
  7.     Index:integer;          
  8.   end;          
  9.  
  10. procedure TForm1.ReadHistoryTrafficData(const IndexFrom:integer;const IndexTo:integer);
  11. var
  12.   hd:array of TrafficRecord;
  13.   i,Len:integer;
  14. begin
  15.   if IndexFrom>IndexTo then exit;
  16.   Len:=IndexTo-IndexFrom+1;
  17.   SetLength(hd,Len);                                  //program corrupted here!!  
  18.   for i:=0 to IndexTo-IndexFrom do begin
  19.     //do something
  20.   end;            
  21.   SetLength(hd,0);
  22. end;                  
  23.  
Title: Re: SetLength error
Post by: MarkMLl on October 23, 2020, 09:28:35 am
Corrupted /how/? What's your error message? How do you know that the length is >= 0?

MarkMLl
Title: Re: SetLength error
Post by: senglit on October 23, 2020, 10:16:34 am
Corrupted /how/? What's your error message? How do you know that the length is >= 0?

MarkMLl

The Error looks like the attachment.

if IndexFrom>IndexTo then exit;
  Len:=IndexTo-IndexFrom+1;
  SetLength(hd,Len);

the length must be larger than 0
Title: Re: SetLength error
Post by: senglit on October 23, 2020, 10:52:51 am
The strange thing is I used SetLenth many times in other procedures. But it only crash in this ReadHistoryTrafficData procedure.
Title: Re: SetLength error
Post by: MarkMLl on October 23, 2020, 10:58:15 am
So look very carefully at the parameters being passed into that procedure. Apart from anything else they're signed... are you /sure/ that that check's effective in all possible cases?

MarkMLl
Title: Re: SetLength error
Post by: senglit on October 23, 2020, 11:12:33 am
this line: if IndexFrom>IndexTo then exit;
should ensure the length is larger then 0

It also crashed if I change the line into : SetLength(hd,1000);

And I tried this:

var
cc:array of integer;
begin
 ...
 SetLength(cc,100);

end;

it crashed too! And SetLength only crash in this procedure. In others it works fine. What stupid thing I did?
Title: Re: SetLength error
Post by: MarkMLl on October 23, 2020, 11:35:21 am
I know that check /should/ catch it, but I'm trying to give most of my attention to work at teh moment...

Are you sure that it's crashing where you think it's crashing? Does doing a line-by-line stepthrough show anything interesting? Does putting in WriteLn()s show anything interesting?

MarkMLl
Title: Re: SetLength error
Post by: Paolo on October 23, 2020, 12:20:06 pm
What about if you move hd from procedure to global var ?
Title: Re: SetLength error
Post by: rvk on October 23, 2020, 12:44:23 pm
Are you sure the error is on the SetLength() line?
(How did you check?)

Are you sure the instance of TForm1 is created?

How and where do you call ReadHistoryTrafficData?
Title: Re: SetLength error
Post by: howardpc on October 23, 2020, 12:58:34 pm
As always: code snippets are insufficient to debug programs from which they are just a small part.
Others can only guess at all the unknown other parts of your code.

Submitting a compilable example that reproduces the problem is the best way to elicit help from other programmers when you are stuck.
Title: Re: SetLength error
Post by: senglit on October 23, 2020, 01:16:13 pm
What about if you move hd from procedure to global var ?

I tried. But it happened again. Another strange thing is:

What I tried else includes:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.ReadHistoryTrafficData(const IndexFrom:integer;const IndexTo:integer);
  2. var
  3.   cc:array of integer;
  4.   hd:array of TrafficRecord;
  5. begin
  6.   if IndexFrom>IndexTo then exit;
  7.   Len:=IndexTo-IndexFrom+1;
  8.   SetLength(cc,Len);      //check1
  9.   SetLength(hd,Len);     //check2
  10. end;
  11.  
  12. procedure TForm1.Button1Click(Sender: TObject);
  13. begin
  14.   ReadHistoryTrafficData(SpinEdit1.Value,SpinEdit2.Value);  //try 1
  15.   ReadHistoryTrafficData(10,20);  //try 2
  16. end;
  17.  

If I use "try1" line in Button1Click then both check1 and check2 will crush.
If I use "try2" line in Button1Click then check1 is ok and check2 will crush.
Title: Re: SetLength error
Post by: senglit on October 23, 2020, 01:23:55 pm
Are you sure the error is on the SetLength() line?
(How did you check?)

Are you sure the instance of TForm1 is created?

How and where do you call ReadHistoryTrafficData?

I checked it by debugger Step to Step (F8). And I also check it by adding a lot of lines of MessageDlg() to ensure I know where I was.

The TForm1 Instance is created and The Form showed. It only crash when I click button1. and crashed in the first few lines when I tried to initialize the varaible in ReadHistoryTrafficData.

And the Form1 is the only Form in this program. and there is no thread is used. So I am very sure the problem is triggered by Button1.click.
Title: Re: SetLength error
Post by: MarkMLl on October 23, 2020, 01:24:45 pm
If it really is crashing where you think it's crashing- and you've still not put in WriteLn()s to verify that- then it's time to try an older version of the compiler.

MarkMLl
Title: Re: SetLength error
Post by: MarkMLl on October 23, 2020, 01:28:31 pm
And I also check it by adding a lot of lines of MessageDlg() to ensure I know where I was.

What use is that? You've got a crash in your program, something's behaving as though code generation is broken, and you expect the messageloop to be working properly? Oi vey!

Do you really think that most of us have never had to contend with this sort of thing? I said WriteLn() and I meant WriteLn()!!!!!

MarkMLl
Title: Re: SetLength error
Post by: VisualLab on October 23, 2020, 01:38:20 pm
I have prepared a little test. Here is the code:

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls;
  9.  
  10. type
  11.   PTrafficRecord = ^TTrafficRecord;
  12.   TTrafficRecord = record
  13.     Time: TDateTime;
  14.     Interval: Integer;
  15.     Volume: Integer;
  16.     Index: Integer;
  17.   end;
  18.  
  19.   {TForm1}
  20.   TForm1 = class(TForm)
  21.     Button1: TButton;
  22.     Memo1: TMemo;
  23.     procedure Button1Click(Sender: TObject);
  24.   private
  25.     procedure ReadHistoryTrafficData(const AIndexFrom, AIndexTo: Integer);
  26.   public
  27.  
  28.   end;
  29.  
  30. var
  31.   Form1: TForm1;
  32.  
  33. implementation
  34.  
  35. {$R *.lfm}
  36.  
  37. {TForm1}
  38.  
  39. procedure TForm1.Button1Click(Sender: TObject);
  40. begin
  41.   ReadHistoryTrafficData(1, 10);
  42. end;
  43.  
  44. procedure TForm1.ReadHistoryTrafficData(const AIndexFrom, AIndexTo: Integer);
  45. var
  46.   VItem: PTrafficRecord;
  47.   VData: array of PTrafficRecord;
  48.   i, VCount, VSize: Integer;
  49.   VCurrentTime: TTime;
  50.   VText: String;
  51. begin
  52.   if AIndexFrom > AIndexTo
  53.    then Exit;
  54.   VCount := AIndexTo - AIndexFrom + 1;
  55.   SetLength(VData, VCount);
  56.   Memo1.Lines.Clear;
  57.   VCurrentTime := Now;
  58.   Randomize;
  59.   VSize := 0;
  60.   for i := 0 to AIndexTo - AIndexFrom do
  61.    begin
  62.     New(VItem);
  63.     VItem^.Index := i;
  64.     VItem^.Time := Now;
  65.     VItem^.Interval := Trunc(VCurrentTime - VItem^.Time);
  66.     VItem^.Volume := Random(1000);
  67.     VData[i] := VItem;
  68.     VSize := VSize + SizeOf(VItem^);
  69.     VText := 'i = ' + i.ToString +
  70.              ', time: ' + TimeToStr(VItem^.Time) +
  71.              ', interval: ' + VItem^.Interval.ToString +
  72.              ', volume: ' + VItem^.Volume.ToString;
  73.     Memo1.Lines.Add(VText);
  74.    end;
  75.   Memo1.Lines.Add('-----');
  76.   Memo1.Lines.Add('n: ' + VCount.ToString);
  77.   Memo1.Lines.Add('structure size: ' + VSize.ToString);
  78.   SetLength(VData, 0);
  79. end;
  80.  
  81. end.
  82.  

The code compiles. I also checked it with a debugger. I am not getting any error messages. I'm using: Windows 10, Lazarus 2.0.10.

VisualLab
Title: Re: SetLength error
Post by: MarkMLl on October 23, 2020, 01:41:23 pm
@VisualLab: same compiler version (3.2.0 64-bit)?

MarkMLl
Title: Re: SetLength error
Post by: VisualLab on October 23, 2020, 01:48:28 pm
Yes.

Lazarus 2.0.10 r63526 FPC 3.2.0 x86_64-win64-win32/win64

Windows 10, Ent LTSC, 64-bit

VisualLab
Title: Re: SetLength error
Post by: rvk on October 23, 2020, 01:51:55 pm
What are your values for SpinEdit1.Value & SpinEdit2.Value?

Does  passing 1 and 1 work?

You error is definitely somewhere else and is probably memory related.
Did you create TForm1 yourself somewhere?

@VisualLab, if you just use setlength on array of TTrafficRecord in your example, does it crash for you?
(Because a small reproducable example with crash would be helpfull)
Title: Re: SetLength error
Post by: Cyrax on October 23, 2020, 01:57:19 pm
Hi all,

I got a procedure in which SetLength is used. But everytime when it run to SetLength line, the program corrupted. I guess it is a very simple stupid problem. But I just can't figure it out. Who can remind me something about it?

Code: Pascal  [Select][+][-]
  1. type
  2.   PTrafficRecord = ^TrafficRecord;
  3.   TrafficRecord = record
  4.     Time:TDateTime;        
  5.     Interval:integer;      
  6.     Volume:integer;          
  7.     Index:integer;          
  8.   end;          
  9.  
  10. procedure TForm1.ReadHistoryTrafficData(const IndexFrom:integer;const IndexTo:integer);
  11. var
  12.   hd:array of TrafficRecord;
  13.   i,Len:integer;
  14. begin
  15.   if IndexFrom>IndexTo then exit;
  16.   Len:=IndexTo-IndexFrom+1;
  17.   SetLength(hd,Len);                                  //program corrupted here!!  
  18.   for i:=0 to IndexTo-IndexFrom do begin
  19.     //do something
  20.   end;            
  21.   SetLength(hd,0);
  22. end;                  
  23.  

Your program is 64-bit, right? Because address in the error message tells that your program is 32-bit one.
Title: Re: SetLength error
Post by: VisualLab on October 23, 2020, 02:01:44 pm
No. SetLength works fine in my code. I can preview what's going on in the code with a debugger. This is the result of the test application:
Title: Re: SetLength error
Post by: senglit on October 23, 2020, 02:39:56 pm
I have prepared a little test. Here is the code:

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls;
  9.  
  10. type
  11.   PTrafficRecord = ^TTrafficRecord;
  12.   TTrafficRecord = record
  13.     Time: TDateTime;
  14.     Interval: Integer;
  15.     Volume: Integer;
  16.     Index: Integer;
  17.   end;
  18.  
  19.   {TForm1}
  20.   TForm1 = class(TForm)
  21.     Button1: TButton;
  22.     Memo1: TMemo;
  23.     procedure Button1Click(Sender: TObject);
  24.   private
  25.     procedure ReadHistoryTrafficData(const AIndexFrom, AIndexTo: Integer);
  26.   public
  27.  
  28.   end;
  29.  
  30. var
  31.   Form1: TForm1;
  32.  
  33. implementation
  34.  
  35. {$R *.lfm}
  36.  
  37. {TForm1}
  38.  
  39. procedure TForm1.Button1Click(Sender: TObject);
  40. begin
  41.   ReadHistoryTrafficData(1, 10);
  42. end;
  43.  
  44. procedure TForm1.ReadHistoryTrafficData(const AIndexFrom, AIndexTo: Integer);
  45. var
  46.   VItem: PTrafficRecord;
  47.   VData: array of PTrafficRecord;
  48.   i, VCount, VSize: Integer;
  49.   VCurrentTime: TTime;
  50.   VText: String;
  51. begin
  52.   if AIndexFrom > AIndexTo
  53.    then Exit;
  54.   VCount := AIndexTo - AIndexFrom + 1;
  55.   SetLength(VData, VCount);
  56.   Memo1.Lines.Clear;
  57.   VCurrentTime := Now;
  58.   Randomize;
  59.   VSize := 0;
  60.   for i := 0 to AIndexTo - AIndexFrom do
  61.    begin
  62.     New(VItem);
  63.     VItem^.Index := i;
  64.     VItem^.Time := Now;
  65.     VItem^.Interval := Trunc(VCurrentTime - VItem^.Time);
  66.     VItem^.Volume := Random(1000);
  67.     VData[i] := VItem;
  68.     VSize := VSize + SizeOf(VItem^);
  69.     VText := 'i = ' + i.ToString +
  70.              ', time: ' + TimeToStr(VItem^.Time) +
  71.              ', interval: ' + VItem^.Interval.ToString +
  72.              ', volume: ' + VItem^.Volume.ToString;
  73.     Memo1.Lines.Add(VText);
  74.    end;
  75.   Memo1.Lines.Add('-----');
  76.   Memo1.Lines.Add('n: ' + VCount.ToString);
  77.   Memo1.Lines.Add('structure size: ' + VSize.ToString);
  78.   SetLength(VData, 0);
  79. end;
  80.  
  81. end.
  82.  

The code compiles. I also checked it with a debugger. I am not getting any error messages. I'm using: Windows 10, Lazarus 2.0.10.

VisualLab

what a wonderful thing! I don't know why that happened to me. I use SetLength in many of my program. But it conly crash in this procedure.
Title: Re: SetLength error
Post by: MarkMLl on October 23, 2020, 02:48:39 pm
Well if you do find out what's happening let us know. But I think you need to check an older compiler version.

MarkMLl
Title: Re: SetLength error
Post by: senglit on October 23, 2020, 03:56:43 pm
@MarkMLl @VisualLab @rvk @Paolo @howardpc

Thanks for your warm hearted help! Finally I found the problem and here is the review of this stupid problem:

The program crashed at SetLength(hd,Len). @MarkMLl insist it is not the real problem. @VisualLab built a small sample to testify MarkMLl's opinion. But It always crashed at this step. I used F8 step debug, logfile and MessageDlg to confirm it really happened at this step.

But SetLength is a frequently used function and it never crashed before, even in this program. So I set breakpoints in all of the lines which used SetLength.

What was found: There is another procedure called by FormCreate. And there was something like

Code: Pascal  [Select][+][-]
  1. procedure ..........
  2. var
  3.   arr:array of word;
  4.   fs:TFileStream;
  5.   ......
  6. begin
  7.   SetLength(arr,Len1);
  8.   fs:=TFileStream.Create(filename,fmOpenRead or fmShareDenyNone);
  9.   fs.Position:=0;
  10.   fs.Read(arr[pos],len2);     //<---------------- stupid bug: pos+len2>len1. Modified, I wrote it wrongly at first time.
  11.   fs.Free;
  12. end;
  13.  


when this procedure is called by FormCreate, everything looks good. I can do anything on the Form1 window only if I don't click button1.

When button1 was clicked, this is the first time to run SetLength after the bug. Even hd is not arr the program still crashed.

So, another lesson for me: problem often happens before you can see it. Seek the real bug before the crash point.

Answer to Cyrax: I use win10 64bit and lazarus 64bit. But this program will run on a winxp 32bit system. So I installed a lazarus-cross-plateform and compile the program as a win32 exe file.

Thanks again to everybody!
Title: Re: SetLength error
Post by: marcov on October 23, 2020, 03:59:56 pm
You don't want to read into ARR, that is a  space for the 4/8 byte large pointer.

fs.read into arr[0]
Title: Re: SetLength error
Post by: senglit on October 23, 2020, 04:06:04 pm
You don't want to read into ARR, that is a  space for the 4/8 byte large pointer.

fs.read into arr[0]

Yes, you are right. I posted it wrongly. Modified. Thank you.
Title: Re: SetLength error
Post by: MarkMLl on October 23, 2020, 04:18:14 pm
So, another lesson for me: problem often happens before you can see it. Seek the real bug before the crash point.

Old adage: when a programmer has a hard time finding a bug, it's because he's looking in the wrong place.

I wasn't necessarily saying it wasn't SetLength(), just that if you were 100% confident in your parameters and the runtime check then it wasn't happening where you were looking. And WriteLn() as I suggested- *NOT* a dialog(ue) box, should have shown that eventually.

MarkMLl
TinyPortal © 2005-2018