Recent

Author Topic: Heaptrc and MacOS  (Read 1772 times)

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 5515
    • wiki
Re: Heaptrc and MacOS
« Reply #30 on: May 07, 2019, 11:18:46 pm »
Well, it doesn't have to be a leak in your code. It could also be in the cocoa widgetset. Both possible.

From the info avail, I would at first guess widgetset.

Though that is not necessary true. Lets assume something is allocated during ShowModal. Maybe the memory for a string which is refcounted.
Then at some point you create another object that references the string, or you do some pointer stuff "new(Pstring)^ := TheStringFromModal". You leak that other object or pointer. Then you also leak that string.
Heaktrc does not realize that the string was leaked at that later time. HeapTrc only shows you where it was allocated.

Of course for the above to happen you would expect that other object/pointer to be in the list of leaks.

But now say, you have some code that writes to random memory (e.g. a dangling pointer). The data to which the pointer points no longer exists, so the pointer points to something else, that just happened to re-use that memory.
Now say again ShowModal allocetad mem (that would be ok). It stores a pointer to that mem (eg to the object). Then Later show modal would free that object.
But your code, writes nil to the dangling pointer. And that happens to be the same address as the above object. Then the modal code sees nil, and cannot free the object.
Admitted, not a likely case. But a possibility.

Leaks are tricky.



FreeAndNil may not always be needed. It does no harm either. (I heard arguments saying it does, and it may on a first glance look like that, but the great balance may be different. So its a matter what math you subscribe too)

If you do

Code: Pascal  [Select]
  1. Procedure Bar;
  2. var Foo: TFoo;
  3. begin
  4.   Foo := getObject():
  5.   if Foo <> nil then begin
  6.      dosomething;
  7.     Foo.Destroy
  8.   end;
  9. end;
  10.  
You can use Destroy because:
- You already ensured Foo <> nil
- Foo goes out of scope on the next line.
  The remaining dangling pointer can never be accessed.

If you did not have "if Foo <> nil then begin" then you would maybe use
  Foo.Free;
because Foo maybe nil, and Free cares about that.

But in both cases you can also do FreeAndNil.
1) It does not hurt (unless you need code so time critical you should write it in asm instead)
2) Always using FreeAndNil means you will not accidentally NOT use it in other places
3) Foo is now a local var. What if you refactor it to be a field on "self" or a global var? Then you have to remember to change to FreeAndNil.

There is the argument that your code should be correct, and not even try to access the variable, if it is not assigned, or if the assigned object has been freed. And maybe that is how you designed it. But even then, bugs happen, and FreeAndNil may prevent them.

If you want to catch them, then write your own function
  FreeThenBadPointer
and set the variable to pointer(-1).
If your code accesses the var, where it should not, then it will crash and you know you got an error.


In some case you want to use FreeThenNil. But that is a diff story.




ahnz

  • Jr. Member
  • **
  • Posts: 57
Re: Heaptrc and MacOS
« Reply #31 on: May 08, 2019, 11:19:00 pm »
Thankyou very much Martin for such a detailed and helpful response - certainly gives me some stuff to think about.

I  have since done the following simple test...

Code: Pascal  [Select]
  1. program project1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.   {$IFDEF UNIX}{$IFDEF UseCThreads}
  7.   cthreads,
  8.   {$ENDIF}{$ENDIF}
  9.   Interfaces, // this includes the LCL widgetset
  10.   Forms, Unit1, Unit2
  11.   { you can add units after this };
  12.  
  13. {$R *.res}
  14.  
  15. begin
  16.   SetHeapTraceOutput('/Users/andy/Desktop/memleak.trc');
  17.   RequireDerivedFormResource:=True;
  18.   Application.Scaled:=True;
  19.   Application.Initialize;
  20.   Application.CreateForm(TForm1, Form1);
  21.   Application.CreateForm(TForm2, Form2);
  22.   Application.Run;
  23. end.
  24.      
  25.  
  26. unit Unit1;
  27.  
  28. {$mode objfpc}{$H+}
  29.  
  30. interface
  31.  
  32. uses
  33.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, OSUtils, Unit2;
  34.  
  35. type
  36.  
  37.   { TForm1 }
  38.  
  39.   TForm1 = class(TForm)
  40.     Button1: TButton;
  41.     procedure Button1Click(Sender: TObject);
  42.   private
  43.  
  44.   public
  45.  
  46.   end;
  47.  
  48. var
  49.   Form1: TForm1;
  50.  
  51. implementation
  52.  
  53. {$R *.lfm}
  54.  
  55. { TForm1 }
  56.  
  57. procedure TForm1.Button1Click(Sender: TObject);
  58. begin
  59.   if Form2.ShowModal = mrOk then
  60.   Close;
  61. end;
  62.  
  63. end.
  64.  
  65.  
  66. unit Unit2;
  67.  
  68. {$mode objfpc}{$H+}
  69.  
  70. interface
  71.  
  72. uses
  73.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls;
  74.  
  75. type
  76.  
  77.   { TForm2 }
  78.  
  79.   TForm2 = class(TForm)
  80.     Button1: TButton;
  81.     Button2: TButton;
  82.     procedure Button1Click(Sender: TObject);
  83.     procedure Button2Click(Sender: TObject);
  84.   private
  85.  
  86.   public
  87.  
  88.   end;
  89.  
  90. var
  91.   Form2: TForm2;
  92.  
  93. implementation
  94.  
  95. {$R *.lfm}
  96.  
  97. { TForm2 }
  98.  
  99. procedure TForm2.Button1Click(Sender: TObject);
  100. begin
  101.   ModalResult := mrOk;
  102. end;
  103.  
  104. procedure TForm2.Button2Click(Sender: TObject);
  105. begin
  106.   ModalResult := mrCancel;
  107. end;
  108.  
  109. end.

... and heaptrc shows (suggests) a memory leak at "if Form2.ShowModal = mrOk then" ... so I'm reckoning your first guess that its the widgets isn't too far off the mark :)


Thanks again for all the assistance
OS: OSX High Sierra 10.13.6
Lazarus: 2.1.0, r61162

Widgetset: Cocoa
Target: 64bit OSX

Compiler:
FPC 3.0.4 [2017/11/26] for x86_64

Debug:
LLDB  (with fpdebug)(Beta)
Path: /usr/bin/lldb
Version: lldb-1000.11.38.2