Lazarus

Programming => General => Topic started by: Hansvb on March 28, 2023, 04:16:29 pm

Title: Find text in memo gives memory leak
Post by: Hansvb on March 28, 2023, 04:16:29 pm
Hi,

On this website
Quote
https://wiki.freepascal.org/TMemo
I found how to search for a text in a memo. I copied this almost exactly. Searching for a text in the memo works fine but when I close the app I have a memory leak. If I remove or don't use the search function then I don't have a memory leak.
However, I don't see what could be causing a memory leak in this function.
what goes wrong here?

Code: Pascal  [Select][+][-]
  1. procedure TFrmMain.ButtonSearchClick(Sender: TObject);
  2. const
  3.   SearchStr : String = '';
  4.   SearchStart : Integer = 0;
  5. begin
  6.   // heeft een geheugenlek... ???
  7.   if SearchStr <> EditSearchFormatedMemo.Text then begin
  8.     SearchStart := 0;
  9.     SearchStr := EditSearchFormatedMemo.Text;
  10.   end;
  11.  
  12.   SearchStart := FindInMemo(MemoFormatted, SearchStr, SearchStart + 1);
  13.   if SearchStart > 0 then begin
  14.     Caption := 'Found at position[' + IntToStr(SearchStart) + ']!';
  15.   end
  16.   else begin
  17.     Caption := 'No further finds.';
  18.   end;
  19. end;
  20.  
  21. function TFrmMain.FindInMemo(aMemo: TMemo; aString: String; StartPos: Integer
  22.   ): Integer;
  23. begin
  24.   // UpperCase. niet hoofdleter gevoelig voorlopig.
  25.   Result := PosEx(UpperCase(aString), UpperCase(aMemo.Text), StartPos);
  26.   //Result := PosEx(aString, aMemo.Text, StartPos);
  27.   if Result > 0 then begin
  28.     aMemo.SelStart := UTF8Length(PChar(aMemo.Text), Result -1);
  29.     aMemo.SelLength := Length(aString);
  30.     aMemo.SetFocus;
  31.   end;
  32. end;
Title: Re: Find text in memo gives memory leak
Post by: howardpc on March 28, 2023, 08:03:52 pm
There seems to be a minor bug in LazFileUtils, specifically in the

Code: Pascal  [Select][+][-]
  1. function ConvertUTF16ToUTF8(Dest: PChar; DestCharCount: SizeUInt;
  2.   Src: PWideChar; SrcWideCharCount: SizeUInt; Options: TConvertOptions;
  3.   out ActualCharCount: SizeUInt): TConvertResult;
althouugh I am not sure I have traced through the debugger correctly.
The function is very clever, and relies on casts of string to PChar, and also casts the
Code: Pascal  [Select][+][-]
  1. SrcI
'th widechar to a Word value which is used in a comparison. Perhaps the output string is not finalised correctly?
I am not clever enough to see what is causing the leak, but ansistring/widestring conversion is a pretty specialist topic, and I am sure it requires an expert in cross-platform string handling to get it right, since all strings (except shortstrings) have automatic memory management by the compiler. So things happen "behind your back", and in this instance perhaps the timing of the auto-memory managment works against the logic of the function?
There is no bug in the code you posted that I can see.
Title: Re: Find text in memo gives memory leak
Post by: dsiders on March 28, 2023, 08:55:19 pm
There seems to be a minor bug in LazFileUtils, specifically in the
Code: Pascal  [Select][+][-]
  1. function ConvertUTF16ToUTF8(Dest: PChar; DestCharCount: SizeUInt;
  2.   Src: PWideChar; SrcWideCharCount: SizeUInt; Options: TConvertOptions;
  3.   out ActualCharCount: SizeUInt): TConvertResult;

Did you post a bug report?
Title: Re: Find text in memo gives memory leak
Post by: howardpc on March 28, 2023, 09:04:00 pm
No because I cannot accurately pinpoint where the bug lies, or even if it is in that particular function.
Title: Re: Find text in memo gives memory leak
Post by: Bart on March 28, 2023, 10:38:04 pm
Confirmed with more simple program:
Code: Pascal  [Select][+][-]
  1. program test;
  2. {$mode objfpc}
  3. {$h+}
  4.  
  5. uses
  6.   Classes;
  7. var
  8.   SL: TStringList;
  9. procedure Foo;
  10. const
  11.   S: String = '';
  12. begin
  13.   S := SL.Text;
  14. end;
  15. begin
  16.   SL := TStringList.Create;
  17.   SL.Add('1');
  18.   Foo;
  19.   SL.Free;
  20. end.
Code: [Select]
C:\Users\Bart\LazarusProjecten>ConsoleProjecten\test
Heap dump by heaptrc unit of C:\Users\Bart\LazarusProjecten\ConsoleProjecten\test.exe
116 memory blocks allocated : 2817/3104
115 memory blocks freed     : 2801/3088
1 unfreed memory blocks : 16
True heap size : 262144 (112 used in System startup)
True free heap : 261904
Should be : 261920
Call trace for block $015E1338 size 16
  $00401768  Foo,  line 13 of test.pas
  $004017F3  $main,  line 18 of test.pas

Bart
Title: Re: Find text in memo gives memory leak
Post by: paweld on March 28, 2023, 10:38:39 pm
Change const to var and no memory leaks. 
Code: Pascal  [Select][+][-]
  1. procedure TFrmMain.ButtonSearchClick(Sender: TObject);
  2. var  //<-- here
  3.   SearchStr: String = '';
  4.   SearchStart: Integer = 0;
  5. begin
  6.   // heeft een geheugenlek... ???
  7.   if SearchStr <> EditSearchFormatedMemo.Text then
  8.   begin
  9.     SearchStart := 0;
  10.     SearchStr := EditSearchFormatedMemo.Text;
  11.   end;
  12.  
  13.   SearchStart := FindInMemo(MemoFormatted, SearchStr, SearchStart + 1);
  14.   if SearchStart > 0 then
  15.   begin
  16.     Caption := 'Found at position[' + IntToStr(SearchStart) + ']!';
  17.   end
  18.   else
  19.   begin
  20.     Caption := 'No further finds.';
  21.   end;
  22. end;
  23.  
  24. function TFrmMain.FindInMemo(aMemo: TMemo; aString: String; StartPos: Integer): Integer;
  25. begin
  26.   // UpperCase. niet hoofdleter gevoelig voorlopig.
  27.   Result := PosEx(UpperCase(aString), UpperCase(aMemo.Text), StartPos);
  28.   //Result := PosEx(aString, aMemo.Text, StartPos);
  29.   if Result > 0 then
  30.   begin
  31.     aMemo.SelStart := UTF8Length(PChar(aMemo.Text), Result - 1);
  32.     aMemo.SelLength := Length(aString);
  33.     aMemo.SetFocus;
  34.   end;
  35. end;  
  36.  
 
or clear SearchStr and SearchStart at the end of the procedure:
Code: Pascal  [Select][+][-]
  1. procedure TFrmMain.ButtonSearchClick(Sender: TObject);
  2. const
  3.   SearchStr: String = '';
  4.   SearchStart: Integer = 0;
  5. begin
  6.   // heeft een geheugenlek... ???
  7.   if SearchStr <> EditSearchFormatedMemo.Text then
  8.   begin
  9.     SearchStart := 0;
  10.     SearchStr := EditSearchFormatedMemo.Text;
  11.   end;
  12.  
  13.   SearchStart := FindInMemo(MemoFormatted, SearchStr, SearchStart + 1);
  14.   if SearchStart > 0 then
  15.   begin
  16.     Caption := 'Found at position[' + IntToStr(SearchStart) + ']!';
  17.   end
  18.   else
  19.   begin
  20.     Caption := 'No further finds.';
  21.   end;
  22.   SearchStr := '';       //<-- here
  23.   SearchStart := 0;   //<-- and here
  24. end;            
  25.  
Title: Re: Find text in memo gives memory leak
Post by: Bart on March 28, 2023, 10:44:13 pm
He uses const, so the value is preserved when leaving and re-entering the procedure.
And yes, making it a var, or making it a shortstring resolves the memory leak.

I asked on the fpc devel mailinglist.

Bart
Title: Re: Find text in memo gives memory leak
Post by: cdbc on March 29, 2023, 12:03:34 pm
Hi
2 alternatives spring to mind...
1) UTF8Pos from unit lazutf8.
2) Function FindMatchesBoyerMooreCaseInSensitive(const S, OldPattern: String; out aMatches: SizeIntArray; const aMatchAll: Boolean) : Boolean; from unit StrUtils.
I've written my own, which is a mixture of the 2 above, works flawless with utf8 et.al.
It is a rather lengthy function, but let me know if you need it  ;)
Regards Benny
Title: Re: Find text in memo gives memory leak
Post by: Bart on March 30, 2023, 12:09:53 am
Even simpler example of triggering the memory leak:
Code: Pascal  [Select][+][-]
  1. program test;
  2. {$mode objfpc}
  3. {$h+}
  4.  
  5. var
  6.   HideTheMemoryLeak: Boolean;
  7.  
  8. procedure Foo;
  9. const
  10.   S: String = '';
  11. begin
  12.   S := 'X';
  13.   if HideTheMemoryLeak then Exit;
  14.   S := S + 'Y';  // This line cause a memory leak
  15. end;
  16.  
  17. begin
  18.   HideTheMemoryLeak := (ParamCount = 1) and (LowerCase(ParamStr(1)) = 'hidethememoryleak');
  19.   Foo;
  20. end.

Reported in the bugtracker (https://gitlab.com/freepascal.org/fpc/source/-/issues/40225)
TinyPortal © 2005-2018