Recent

Author Topic: intercepting exceptions  (Read 15121 times)

Rik

  • Jr. Member
  • **
  • Posts: 72
Re: intercepting exceptions
« Reply #15 on: August 21, 2018, 10:03:13 am »
Rik, if creating fewer bitmaps makes the exception go away, it's more likely that the exception occurs because of corrupted memory, not because of running out of memory.   
Is it possible for you to create a small standalone program that isolates the code that generates the exception and post it ?   
How do you keep track of all the bitmaps ?... do you have an array of handles ?  ... some list ?  (any of those may be too small to hold the handles to the bitmaps you're creating causing memory to be corrupted.)
Maybe I should explain what I am trying to do:
I want to show a function changing in time (or a 3D function where Z= time). The function is too complex (or my computer too slow) to calculate and draw it "on the fly". So I calculate the function in advance (timesteps of 20 ms) and draw each step to a Bitmap and then draw the Bitmaps one by one to an Image with the right timing:
Code: Pascal  [Select][+][-]
  1.   for i:= 0 to nFrame-1 do
  2.   begin
  3.     MyImage.Canvas.Draw(0,0,MyBitmap[i]);
  4.     Application.ProcessMessages;
  5.     repeat
  6.       t:= Round(ET.Elapsed);
  7.     until t> t_last;
  8.     t_last:= t_last+20;
  9.   end;

Creating the Bitmaps:
Code: Pascal  [Select][+][-]
  1. var
  2.   MyBitmap: array of tBitmap;
  3.   ET: TEpikTimer;
Code: Pascal  [Select][+][-]
  1.   SetLength(MyBitmap,nFrame);
  2.   for i:= 0 to nFrame-1 do MyBitmap[i]:= tBitmap.Create;

I will try to make a small program that just creates and fills the Bitmaps.

Rik

440bx

  • Hero Member
  • *****
  • Posts: 4029
Re: intercepting exceptions
« Reply #16 on: August 21, 2018, 10:25:41 am »
Based on the code you posted, the first thing I'd check in the code is that the value of nFrame has not changed from the time it is used to set the number of elements in the array and the time it is used in the for loop.

If the value isn't the same, that will cause problems, particularly if it's larger when it is used in the loop.

Another thing I would do is, after the setlength(MyBitmap, nFrame), I'd initialize the array to be all nil.  Like this, for instance,
Code: Pascal  [Select][+][-]
  1. FillChar(MyBitmap, sizeof(MyBitmap), #0);

then after the loop that creates all the bitmaps has executed, I'd add another loop to check that every value in the array is no longer nil.  If for whatever reason, the creation of one or more bitmaps failed then, attempting to access the bitmap will effectively be an attempt to dereference a nil pointer which will result in a sigdev.



(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

Rik

  • Jr. Member
  • **
  • Posts: 72
Re: intercepting exceptions
« Reply #17 on: August 21, 2018, 10:52:35 am »
Based on the code you posted, the first thing I'd check in the code is that the value of nFrame has not changed from the time it is used to set the number of elements in the array and the time it is used in the for loop.
If the value isn't the same, that will cause problems, particularly if it's larger when it is used in the loop.
I just checked: nFrame does not change.

Another thing I would do is, after the setlength(MyBitmap, nFrame), I'd initialize the array to be all nil.  Like this, for instance,
Code: Pascal  [Select][+][-]
  1. FillChar(MyBitmap, sizeof(MyBitmap), #0);

then after the loop that creates all the bitmaps has executed, I'd add another loop to check that every value in the array is no longer nil.  If for whatever reason, the creation of one or more bitmaps failed then, attempting to access the bitmap will effectively be an attempt to dereference a nil pointer which will result in a sigdev.

I changed the code as suggested:
Code: Pascal  [Select][+][-]
  1.   SetLength(MyBitmap,nFrame);
  2.   FillChar(MyBitmap,sizeof(MyBitmap),#0);
  3.   for i:= 0 to nFrame-1 do MyBitmap[i]:= tBitmap.Create;
But this results in a SIGSEGV exception at the line 3 (for i:= 0 to nFrame-1 do MyBitmap:= tBitmap.Create;).

Rik

440bx

  • Hero Member
  • *****
  • Posts: 4029
Re: intercepting exceptions
« Reply #18 on: August 21, 2018, 11:12:49 am »
But this results in a SIGSEGV exception at the line 3 (for i:= 0 to nFrame-1 do MyBitmap:= tBitmap.Create;).

Rik

First reformat that code to make it easier to debug, like this:
Code: Pascal  [Select][+][-]
  1. for i:= 0 to nFrame-1 do
  2. begin
  3.   MyBitmap[i]:= tBitmap.Create;
  4. end;
  5.  

That way you don't get the sigdev as soon as you try to execute the for loop, instead you'll get it during the assignment of MyBitmap.  What you really want to see now is, what's the value of "I" when you get the sigdev.  Does it happen immediately (when I = 0 or later ?)

Also, before you even enter the loop, create a variable, TheSize (for instance) and assign it the length of MyBitmap.  TheSize := Length(MyBitmap); and ensure you get the expected length (the one you set using SetLength).  For good measure, after that assignment do, TheSize := sizeof(MyBitmap); that one should equal 8 times the length (in 64bit), if it isn't then, the array, for some reason, isn't large enough to accommodate the handles of all the bitmaps you are creating.

Basically, the first thing to do when debugging is to check that everything is as expected, that often helps figure out what is going wrong.

(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 9867
  • Debugger - SynEdit - and more
    • wiki
Re: intercepting exceptions
« Reply #19 on: August 21, 2018, 11:41:52 am »

Another thing I would do is, after the setlength(MyBitmap, nFrame), I'd initialize the array to be all nil.  Like this, for instance,
Code: Pascal  [Select][+][-]
  1. FillChar(MyBitmap, sizeof(MyBitmap), #0);

1)  You only need this, if you do NOT immediately create all of the TBitMaps. That is if you are going to keep some of the nil pointers.

2) The save way is to do a "for to " loop, and set each entry to nil.

3) the correct syntax is
Code: Pascal  [Select][+][-]
  1. FillChar(MyBitmap[0], length(MyBitmap) * sizeof(MyBitmap[0]), #0);

Rik

  • Jr. Member
  • **
  • Posts: 72
Re: intercepting exceptions
« Reply #20 on: August 21, 2018, 11:44:37 am »
First reformat that code to make it easier to debug, like this:
Code: Pascal  [Select][+][-]
  1. for i:= 0 to nFrame-1 do
  2. begin
  3.   MyBitmap[i]:= tBitmap.Create;
  4. end;
  5.  

That way you don't get the sigdev as soon as you try to execute the for loop, instead you'll get it during the assignment of MyBitmap.  What you really want to see now is, what's the value of "I" when you get the sigdev.  Does it happen immediately (when I = 0 or later ?)
Also, before you even enter the loop, create a variable, TheSize (for instance) and assign it the length of MyBitmap.  TheSize := Length(MyBitmap); and ensure you get the expected length (the one you set using SetLength).  For good measure, after that assignment do, TheSize := sizeof(MyBitmap); that one should equal 8 times the length (in 64bit), if it isn't then, the array, for some reason, isn't large enough to accommodate the handles of all the bitmaps you are creating.
Basically, the first thing to do when debugging is to check that everything is as expected, that often helps figure out what is going wrong.

I just tried
Code: Pascal  [Select][+][-]
  1.   nFrame:= 6000; // large enough to provoce the exception
  2.   SetLength(MyBitmap,nFrame);
  3.   TheSize:= Length(MyBitmap);
  4.   Memo.Lines.Add(IntToStr(TheSize));
  5.   TheSize:= sizeof(MyBitmap);
  6.   Memo.Lines.Add(IntToStr(TheSize));
  7.   FillChar(MyBitmap,sizeof(MyBitmap),#0);
  8.   for i:= 0 to Scans do
  9.   begin
  10.     Caption:= IntToStr(i);
  11.     MyBitmap[i]:= tBitmap.Create;
  12.   end;
TheSize:= Length(MyBitmap) returns 6001 and TheSize:= sizeof(MyBitmap) returns 8 (!). I am puzzled by this last value.
The SIGSEGV exception occurs at i = 0.

Rik

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 9867
  • Debugger - SynEdit - and more
    • wiki
Re: intercepting exceptions
« Reply #21 on: August 21, 2018, 11:48:15 am »
Another thought. Can you get a virtual machine with Linux?

On Linux there is a tool called valgrind (this exists only for Linux). And this can be of help, to find the problem. It will run your app, though a lot slower than normal, but it checks every single statement.
If you consider this I can write a few more hints. (on using it, you need to be able to intall Lazarus on Linux, and install valgrind, and your app must be able to run under Linux)

440bx

  • Hero Member
  • *****
  • Posts: 4029
Re: intercepting exceptions
« Reply #22 on: August 21, 2018, 11:51:49 am »
1)  You only need this, if you do NOT immediately create all of the TBitMaps. That is if you are going to keep some of the nil pointers.

2) The save way is to do a "for to " loop, and set each entry to nil.

3) the correct syntax is
Code: Pascal  [Select][+][-]
  1. FillChar(MyBitmap[0], length(MyBitmap) * sizeof(MyBitmap[0]), #0);

You are definitely right about the correct syntax.  I forgot we are dealing with a dynamic array, its size is the size of a pointer not the size of the array itself. Oops!.

As far as needing to initialize the array, in this case, it's just for debugging purposes.  That way, every element of the array is preset to a known value which is usually very helpful when tracking down a problem.  Just prophylactic (as long as the correct syntax is used.)  Thank you for pointing out that mistake.

ETA

TheSize:= Length(MyBitmap) returns 6001 and TheSize:= sizeof(MyBitmap) returns 8 (!). I am puzzled by this last value.
The SIGSEGV exception occurs at i = 0.

Rik
8 is correct.  It is the size of a pointer.  A dynamic array identifier is just a pointer to a dynamically allocated memory block. 

Martin above, corrected my error and provided the corrected statement. 

The fact that it occurs at I = 0 implies that either no space was allocated for the dynamic array or there is something in the bitmap constructor that doesn't "like" something in your code.

If you can make a short program that exhibits the problem, that would be very helpful.

« Last Edit: August 21, 2018, 11:58:44 am by 440bx »
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 9867
  • Debugger - SynEdit - and more
    • wiki
Re: intercepting exceptions
« Reply #23 on: August 21, 2018, 11:53:00 am »
TheSize:= Length(MyBitmap) returns 6001 and TheSize:= sizeof(MyBitmap) returns 8 (!). I am puzzled by this last value.
The SIGSEGV exception occurs at i = 0.

MyBitmap is a dynamic array, that means internally it is a pointer to the array data.

sizeof(MyBitmap) is the size of the pointer
sizeof(MyBitmap[0])  is the size of one element
sizeof(MyBitmap[0]) * length(MyBitMap) is the memory allocated (though if there was xtra alignment, this may go wrong too)

"FillChar(MyBitmap, sizeof(MyBitmap), #0)" overwrites the pointer. This is the same as "MyBitmap := nil".
So of course, after this "MyBitmap[ x ] := ..." crashes.

"FillChar(MyBitmap[ 0 ], ..., #0)" fills memory in the array, starting with the first element of it.

Rik

  • Jr. Member
  • **
  • Posts: 72
Re: intercepting exceptions
« Reply #24 on: August 21, 2018, 11:56:27 am »

Another thing I would do is, after the setlength(MyBitmap, nFrame), I'd initialize the array to be all nil.  Like this, for instance,
Code: Pascal  [Select][+][-]
  1. FillChar(MyBitmap, sizeof(MyBitmap), #0);

1)  You only need this, if you do NOT immediately create all of the TBitMaps. That is if you are going to keep some of the nil pointers.

2) The save way is to do a "for to " loop, and set each entry to nil.

3) the correct syntax is
Code: Pascal  [Select][+][-]
  1. FillChar(MyBitmap[0], length(MyBitmap) * sizeof(MyBitmap[0]), #0);

If I ommit
Code: Pascal  [Select][+][-]
  1. FillChar(MyBitmap, sizeof(MyBitmap), #0);
I can create the 6000 Bitmaps and fill 3275 of them before I run into the exception.

What would be the correct way to use FillChar here?
Just
Code: Pascal  [Select][+][-]
  1. FillChar(MyBitmap[0], length(MyBitmap) * sizeof(MyBitmap[0]), #0);
Or
Code: Pascal  [Select][+][-]
  1. for i:= 0 to nFrame-1 do
  2. begin
  3.   FillChar(MyBitmap[i], length(MyBitmap) * sizeof(MyBitmap[i]), #0);
  4. end

Rik

Rik

  • Jr. Member
  • **
  • Posts: 72
Re: intercepting exceptions
« Reply #25 on: August 21, 2018, 12:05:23 pm »
If you can make a short program that exhibits the problem, that would be very helpful.

I will try to make a short program that "isolates" the problem and post it here.
Meanwhile, many thanks to you, Martin_fr and others for helping me out.

Rik

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 9867
  • Debugger - SynEdit - and more
    • wiki
Re: intercepting exceptions
« Reply #26 on: August 21, 2018, 12:18:33 pm »
What would be the correct way to use FillChar here?
Just
Code: Pascal  [Select][+][-]
  1. FillChar(MyBitmap[0], length(MyBitmap) * sizeof(MyBitmap[0]), #0);
Or
Code: Pascal  [Select][+][-]
  1. for i:= 0 to nFrame-1 do
  2. begin
  3.   FillChar(MyBitmap[i], length(MyBitmap) * sizeof(MyBitmap[i]), #0);
  4. end

The first.


440bx

  • Hero Member
  • *****
  • Posts: 4029
Re: intercepting exceptions
« Reply #27 on: August 21, 2018, 12:18:58 pm »
I will try to make a short program that "isolates" the problem and post it here.
Meanwhile, many thanks to you, Martin_fr and others for helping me out.

Rik
You're welcome.  As far as your question about which construct to use to initialize the array, use the syntax Martin_fr posted, specifically, use this:

Code: Pascal  [Select][+][-]
  1. 1.FillChar(MyBitmap[0], length(MyBitmap) * sizeof(MyBitmap[0]), #0);

The loop you showed will make a mess. Don't use it and don't use what I first posted either.
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

Rik

  • Jr. Member
  • **
  • Posts: 72
Re: intercepting exceptions
« Reply #28 on: August 21, 2018, 12:37:31 pm »
Below a small program that provokes the exception.
It causes an EInvalidOperation exception with message: Canvas does not allow drawing.
This happens at caption "Filling 3324".

Rik

Code: Pascal  [Select][+][-]
  1. unit MainUnit;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs;
  9.  
  10. type
  11.  
  12.   { TMainForm }
  13.  
  14.   TMainForm = class(TForm)
  15.     procedure FormActivate(Sender: TObject);
  16.   private
  17.     { private declarations }
  18.   public
  19.     { public declarations }
  20.   end;
  21.  
  22. var
  23.   MainForm: TMainForm;
  24.  
  25. implementation
  26.  
  27. {$R *.lfm}
  28.  
  29. { TMainForm }
  30. var
  31.   MyBitmap: array of tBitmap;
  32.  
  33. procedure TMainForm.FormActivate(Sender: TObject);
  34. var
  35.   i: Integer;
  36. begin
  37.   SetLength(MyBitmap,6000); // create enough Bitmaps to provoke the exception
  38.   FillChar(MyBitmap[0],length(MyBitmap)*sizeof(MyBitmap[0]),#0);
  39.   for i:= 0 to 5999 do
  40.   begin
  41.     Caption:= 'Creating '+IntToStr(i);
  42.     MyBitmap[i]:= tBitmap.Create;
  43.   end;
  44.   for i:= 0 to 5999 do
  45.   begin
  46.     Caption:= 'Filling '+IntToStr(i);
  47.     with MyBitmap[i] do
  48.     begin
  49.       Width:= 500;
  50.       Height:= 500;
  51.       Canvas.Brush.Style:= bsSolid;
  52.       Canvas.Brush.Color:= clGray;
  53.       Canvas.Pen.Color:= clBlue;
  54.       try
  55.         Canvas.FillRect(0,0,500,500);
  56.       except
  57.         On E :Exception do
  58.         begin
  59.           ShowMessage(E.Message);
  60.         end;
  61.       end;
  62.     end;
  63.   end;
  64. end;
  65.  
  66. end.

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 9867
  • Debugger - SynEdit - and more
    • wiki
Re: intercepting exceptions
« Reply #29 on: August 21, 2018, 12:43:13 pm »
The "Canvas does not allow drawing" is probably because you are running out of Windows resources. Windows can only create so many handles (or maybe the canvas needs GDI resources, which are also limited)

The same is true for brushes and pens, you only can create a limited amount. Limited by Windows.

Just a thought, since your real app will also be out of resources, it is possible that then the LCL will encounter the same error for some other operation (repainting the form, or whatever). It is possible that the LCL itself has a bug, not handling this correct, and leading to the sigsegv.
« Last Edit: August 21, 2018, 12:55:26 pm by Martin_fr »

 

TinyPortal © 2005-2018