├── .gitignore
├── .gitmodules
├── Delphinus.Info.json
├── Delphinus.Install.json
├── External
├── DUnit
│ ├── FastMMMemLeakMonitor.pas
│ ├── LICENSE.html
│ └── TestFramework.pas
└── DUnitX
│ ├── DUnitX.MemoryLeakMonitor.LeakCheck.pas
│ └── DUnitX.MemoryLeakMonitor.LeakCheckCycle.pas
├── License.txt
├── README.md
├── Samples
├── CustomLeakReport
│ ├── .gitignore
│ ├── LeakCheckCustomReport.dpr
│ ├── LeakCheckCustomReport.dproj
│ ├── LeakCheckCustomReport.res
│ ├── TestMain.dfm
│ └── TestMain.pas
└── CustomLeakReportFMX
│ ├── .gitignore
│ ├── LeakCheckCustomReportFMX.deployproj
│ ├── LeakCheckCustomReportFMX.dpr
│ ├── LeakCheckCustomReportFMX.dproj
│ ├── LeakCheckCustomReportFMX.res
│ ├── TestMainFMX.fmx
│ └── TestMainFMX.pas
├── Source
├── LeakCheck.Collections.pas
├── LeakCheck.Configuration.inc
├── LeakCheck.Cycle.Utils.pas
├── LeakCheck.Cycle.pas
├── LeakCheck.DUnit.pas
├── LeakCheck.DUnitCycle.pas
├── LeakCheck.MapFile.pas
├── LeakCheck.Report.FileLog.pas
├── LeakCheck.Report.Utils.pas
├── LeakCheck.Report.pas
├── LeakCheck.Setup.JclTrace.pas
├── LeakCheck.Setup.Trace.pas
├── LeakCheck.Trace.Backtrace.pas
├── LeakCheck.Trace.DbgHelp.pas
├── LeakCheck.Trace.Jcl.pas
├── LeakCheck.Trace.Map.pas
├── LeakCheck.Trace.WinApi.pas
├── LeakCheck.Trace.pas
├── LeakCheck.Types.inc
├── LeakCheck.Utils.pas
├── LeakCheck.inc
└── LeakCheck.pas
└── Test
├── .gitignore
├── LeakCheck.TestCycle.pas
├── LeakCheck.TestDUnit.pas
├── LeakCheck.TestDUnitX.pas
├── LeakCheck.TestForm.fmx
├── LeakCheck.TestForm.pas
├── LeakCheck.TestUnit.pas
├── XE
├── TestGroup.groupproj
├── TestProject.dpr
├── TestProject.dproj
├── TestProject.res
├── TestProjectX.dpr
└── TestProjectX.dproj
├── XE6
├── TestProject.deployproj
├── TestProject.dpr
├── TestProject.dproj
└── TestProject.res
├── XE7
├── TestGroup.groupproj
├── TestProject.deployproj
├── TestProject.dpr
├── TestProject.dproj
├── TestProject.res
├── TestProjectX.dpr
├── TestProjectX.dproj
└── TestProjectX.res
└── readme.txt
/.gitignore:
--------------------------------------------------------------------------------
1 | __history/
2 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "External/Backtrace"]
2 | path = External/Backtrace
3 | url = https://bitbucket.org/shadow_cs/delphi-arm-backtrace.git
4 |
--------------------------------------------------------------------------------
/Delphinus.Info.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "{E2F44B06-C8AB-447A-A006-493E016A74CF}",
3 | "name": "LeakCheck",
4 | "license_type": "Apache 2.0",
5 | "license_file": "License.txt",
6 | "platforms": "Win32;Win64;OSX32",
7 | "compiler_min": 22,
8 | "dependencies": [{
9 | "id": "{73E0C7A7-C9D6-4384-9AF5-117C574493AF}",
10 | "version_min": "0.0.2-Alpha"
11 | }]
12 | }
13 |
--------------------------------------------------------------------------------
/Delphinus.Install.json:
--------------------------------------------------------------------------------
1 | {
2 | "search_pathes": [{
3 | "pathes": "Source;External\\DUnit;External\\DUnitX",
4 | "platforms": "Win32;Win64;OSX32"
5 | }
6 | ],
7 | "source_folders": [{
8 | "folder": ".",
9 | "filter": "License.txt"
10 | },
11 | {
12 | "folder": "External",
13 | "recursive": true
14 | },
15 | {
16 | "folder": "Samples",
17 | "recursive": true
18 | },
19 | {
20 | "folder": "Source",
21 | "recursive": true
22 | }]
23 | }
--------------------------------------------------------------------------------
/External/DUnit/FastMMMemLeakMonitor.pas:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shadow-cs/delphi-leakcheck/67cc1bdfb670242b5b15d5942174c0024d7117b2/External/DUnit/FastMMMemLeakMonitor.pas
--------------------------------------------------------------------------------
/External/DUnit/TestFramework.pas:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shadow-cs/delphi-leakcheck/67cc1bdfb670242b5b15d5942174c0024d7117b2/External/DUnit/TestFramework.pas
--------------------------------------------------------------------------------
/External/DUnitX/DUnitX.MemoryLeakMonitor.LeakCheck.pas:
--------------------------------------------------------------------------------
1 | {***************************************************************************}
2 | { }
3 | { LeakCheck for Delphi }
4 | { }
5 | { Copyright (c) 2015 Honza Rames }
6 | { }
7 | { https://bitbucket.org/shadow_cs/delphi-leakcheck }
8 | { }
9 | {***************************************************************************}
10 | { }
11 | { Licensed under the Apache License, Version 2.0 (the "License"); }
12 | { you may not use this file except in compliance with the License. }
13 | { You may obtain a copy of the License at }
14 | { }
15 | { http://www.apache.org/licenses/LICENSE-2.0 }
16 | { }
17 | { Unless required by applicable law or agreed to in writing, software }
18 | { distributed under the License is distributed on an "AS IS" BASIS, }
19 | { WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. }
20 | { See the License for the specific language governing permissions and }
21 | { limitations under the License. }
22 | { }
23 | {***************************************************************************}
24 |
25 | unit DUnitX.MemoryLeakMonitor.LeakCheck;
26 |
27 | {$I DUnitX.inc}
28 |
29 | interface
30 |
31 | uses
32 | LeakCheck,
33 | Rtti,
34 | DUnitX.TestFramework;
35 |
36 | {$IF CompilerVersion >= 25} // >= XE4
37 | {$LEGACYIFEND ON}
38 | {$IFEND}
39 |
40 | type
41 | TDUnitXLeakCheckMemoryLeakMonitor = class(TInterfacedObject,IMemoryLeakMonitor
42 | {$IF Declared(IMemoryLeakMonitor2)} // Check if newer leak monitor is available
43 | ,IMemoryLeakMonitor2
44 | {$IFEND}
45 | )
46 | private class var
47 | FRunnerLogMessages: TRttiField;
48 | class function GetRunnerLogMessages: TRttiField;
49 | private
50 | FPreSetUpSnapshot: TLeakCheck.TSnapshot;
51 | FPreTestSnapshot: TLeakCheck.TSnapshot;
52 | FPreTearDownSnapshot: TLeakCheck.TSnapshot;
53 | FSetUpAllocated: Int64;
54 | FTestAllocated: Int64;
55 | FTearDownAllocated: Int64;
56 | ///
57 | /// Set to true of there are any leaks detected anywhere in the test
58 | /// (SetUp/Test/TearDown). If false it indicates that all allocation
59 | /// functions should return zero.
60 | ///
61 | FTestLeaked: Boolean;
62 | FLeaksIgnored: Boolean;
63 | // Utility functions that are safe to use even in case of an exception
64 | // (not thread-safe)
65 | procedure BeginIgnore;
66 | procedure EndIgnore;
67 | function GetSnapshot: Pointer;
68 | strict protected
69 | property Snapshot: Pointer read GetSnapshot;
70 | public
71 | procedure AfterConstruction; override;
72 | destructor Destroy; override;
73 |
74 | procedure PreSetup;
75 | procedure PostSetUp;
76 | procedure PreTest;
77 | procedure PostTest;
78 | procedure PreTearDown;
79 | procedure PostTearDown;
80 |
81 | function SetUpMemoryAllocated: Int64;
82 | function TearDownMemoryAllocated: Int64;
83 | function TestMemoryAllocated: Int64;
84 |
85 | function GetReport: string; virtual;
86 | end;
87 |
88 | implementation
89 |
90 | uses
91 | LeakCheck.Utils,
92 | Classes,
93 | DUnitX.MemoryLeakMonitor.Default,
94 | DUnitX.IoC,
95 | DUnitX.TestRunner;
96 |
97 | {$REGION 'TDUnitXLeakCheckMemoryLeakMonitor'}
98 |
99 | // Basic idea is that all allocations made outside of the test object are
100 | // considered as not leaks. Since the leak monitor is short-lived and guaranteed
101 | // to free itself safely (since it is ref counted), we can safely disable leak
102 | // monitoring for short period of time.
103 |
104 | procedure TDUnitXLeakCheckMemoryLeakMonitor.AfterConstruction;
105 | begin
106 | inherited;
107 | FPreSetUpSnapshot.Create;
108 | BeginIgnore;
109 | end;
110 |
111 | procedure TDUnitXLeakCheckMemoryLeakMonitor.BeginIgnore;
112 | begin
113 | if not FLeaksIgnored then
114 | begin
115 | FLeaksIgnored := True;
116 | TLeakCheck.BeginIgnore;
117 | end;
118 | end;
119 |
120 | destructor TDUnitXLeakCheckMemoryLeakMonitor.Destroy;
121 | begin
122 | EndIgnore;
123 | inherited;
124 | end;
125 |
126 | procedure TDUnitXLeakCheckMemoryLeakMonitor.EndIgnore;
127 | begin
128 | if FLeaksIgnored then
129 | begin
130 | FLeaksIgnored := False;
131 | TLeakCheck.EndIgnore;
132 | end;
133 | end;
134 |
135 | function TDUnitXLeakCheckMemoryLeakMonitor.GetReport: string;
136 | var
137 | Report: LeakString;
138 | begin
139 | Report := TLeakCheck.GetReport(Snapshot);
140 | // Report is ASCII so it can be easily treated as UTF-8
141 | Result := sLineBreak + UTF8ToString(Report);
142 | Report.Free;
143 | end;
144 |
145 | class function TDUnitXLeakCheckMemoryLeakMonitor.GetRunnerLogMessages: TRttiField;
146 |
147 | procedure InitLogMessages;
148 | var
149 | Ctx: TRttiContext;
150 | begin
151 | Ctx := TRttiContext.Create;
152 | FRunnerLogMessages := Ctx.GetType(TDUnitXTestRunner).GetField('FLogMessages');
153 | end;
154 |
155 | begin
156 | if not Assigned(FRunnerLogMessages) then
157 | InitLogMessages;
158 | Result := FRunnerLogMessages;
159 | end;
160 |
161 | function TDUnitXLeakCheckMemoryLeakMonitor.GetSnapshot: Pointer;
162 | begin
163 | Result := FPreSetUpSnapshot.Snapshot;
164 | end;
165 |
166 | procedure TDUnitXLeakCheckMemoryLeakMonitor.PostSetUp;
167 | begin
168 | FSetUpAllocated := FPreSetUpSnapshot.LeakSize;
169 | BeginIgnore;
170 | end;
171 |
172 | procedure TDUnitXLeakCheckMemoryLeakMonitor.PostTearDown;
173 | begin
174 | FTearDownAllocated := FPreTearDownSnapshot.LeakSize;
175 | BeginIgnore;
176 | end;
177 |
178 | procedure TDUnitXLeakCheckMemoryLeakMonitor.PostTest;
179 | begin
180 | FTestAllocated := FPreTestSnapshot.LeakSize;
181 | BeginIgnore;
182 | end;
183 |
184 | procedure TDUnitXLeakCheckMemoryLeakMonitor.PreSetup;
185 | begin
186 | System.Assert(Assigned(FPreSetUpSnapshot.Snapshot));
187 | EndIgnore;
188 | end;
189 |
190 | procedure TDUnitXLeakCheckMemoryLeakMonitor.PreTearDown;
191 | begin
192 | FPreTearDownSnapshot.Create;
193 | EndIgnore;
194 | end;
195 |
196 | procedure TDUnitXLeakCheckMemoryLeakMonitor.PreTest;
197 | begin
198 | FPreTestSnapshot.Create;
199 | EndIgnore;
200 | end;
201 |
202 | function TDUnitXLeakCheckMemoryLeakMonitor.SetUpMemoryAllocated: Int64;
203 | var
204 | Runner: TObject;
205 | begin
206 | Runner := TDUnitX.CurrentRunner as TObject;
207 | if Runner is TDUnitXTestRunner then
208 | begin
209 | // Fixes issues that made DUnitX disable leak checking in commit 4d2f444.
210 | IgnoreStrings(GetRunnerLogMessages.GetValue(Runner).AsObject as TStrings);
211 | end;
212 | // Evaluate here as this is the first function guaranteed to run during leak
213 | // evaluation.
214 | FTestLeaked := FPreSetUpSnapshot.LeakSize > 0;
215 | {$IFOPT C+}
216 | if FTestLeaked then
217 | System.Assert(FSetUpAllocated + FTestAllocated + FTearDownAllocated > 0);
218 | // else summation may be not zero since we do not support negative leak size
219 | {$ENDIF}
220 |
221 | // If there are no leaks after teardown the test was OK
222 | if not FTestLeaked then
223 | Exit(0);
224 |
225 | Result := FSetUpAllocated;
226 | end;
227 |
228 | function TDUnitXLeakCheckMemoryLeakMonitor.TearDownMemoryAllocated: Int64;
229 | begin
230 | // If there are no leaks after teardown the test was OK
231 | if not FTestLeaked then
232 | Exit(0);
233 |
234 | Result := FTearDownAllocated;
235 | end;
236 |
237 | function TDUnitXLeakCheckMemoryLeakMonitor.TestMemoryAllocated: Int64;
238 | begin
239 | // If there are no leaks after teardown the test was OK
240 | if not FTestLeaked then
241 | Exit(0);
242 |
243 | Result := FTestAllocated;
244 | end;
245 |
246 | {$ENDREGION}
247 |
248 | procedure Inititalize;
249 | begin
250 | TDUnitXIoC.DefaultContainer.RegisterType(
251 | function : IMemoryLeakMonitor
252 | begin
253 | result := TDUnitXLeakCheckMemoryLeakMonitor.Create;
254 | end);
255 | end;
256 |
257 | initialization
258 | Inititalize;
259 |
260 | end.
261 |
262 |
--------------------------------------------------------------------------------
/External/DUnitX/DUnitX.MemoryLeakMonitor.LeakCheckCycle.pas:
--------------------------------------------------------------------------------
1 | {***************************************************************************}
2 | { }
3 | { LeakCheck for Delphi }
4 | { }
5 | { Copyright (c) 2015 Honza Rames }
6 | { }
7 | { https://bitbucket.org/shadow_cs/delphi-leakcheck }
8 | { }
9 | {***************************************************************************}
10 | { }
11 | { Licensed under the Apache License, Version 2.0 (the "License"); }
12 | { you may not use this file except in compliance with the License. }
13 | { You may obtain a copy of the License at }
14 | { }
15 | { http://www.apache.org/licenses/LICENSE-2.0 }
16 | { }
17 | { Unless required by applicable law or agreed to in writing, software }
18 | { distributed under the License is distributed on an "AS IS" BASIS, }
19 | { WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. }
20 | { See the License for the specific language governing permissions and }
21 | { limitations under the License. }
22 | { }
23 | {***************************************************************************}
24 |
25 | unit DUnitX.MemoryLeakMonitor.LeakCheckCycle;
26 |
27 | {$I DUnitX.inc}
28 |
29 | interface
30 |
31 | uses
32 | LeakCheck,
33 | SysUtils,
34 | TypInfo,
35 | LeakCheck.Cycle,
36 | DUnitX.MemoryLeakMonitor.LeakCheck;
37 |
38 | type
39 | ///
40 | /// In addition to detecting leaks, it also detect reference cycles in
41 | /// those leaks. Must be enabled manually.
42 | ///
43 | TDUnitXLeakCheckCycleMemoryLeakMonitor = class(TDUnitXLeakCheckMemoryLeakMonitor)
44 | strict private class var
45 | FUseExtendedRtti: Boolean;
46 | FOnInstanceIgnored: TScanner.TIsInstanceIgnored;
47 | strict protected
48 | FFormat: TCycle.TCycleFormats;
49 | ScanProc: function(const Instance: TObject; Flags: TScanFlags;
50 | InstanceIgnoreProc: TScanner.TIsInstanceIgnored): TCycles;
51 | procedure AppendCycles(var ErrorMsg: string; ASnapshot: Pointer);
52 | public
53 | procedure AfterConstruction; override;
54 |
55 | function GetReport: string; override;
56 |
57 | class property UseExtendedRtti: Boolean read FUseExtendedRtti write FUseExtendedRtti;
58 | class property OnInstanceIgnored: TScanner.TIsInstanceIgnored read FOnInstanceIgnored write FOnInstanceIgnored;
59 | end;
60 |
61 | ///
62 | /// Extends
63 | /// functionality by outputing Graphviz DOT compatible format that can be
64 | /// converted to graphical representation.
65 | ///
66 | TDUnitXLeakCheckCycleGraphMemoryLeakMonitor = class(TDUnitXLeakCheckCycleMemoryLeakMonitor)
67 | public
68 | procedure AfterConstruction; override;
69 | end;
70 |
71 | ///
72 | /// Extends
73 | /// functionality by outputing Graphviz DOT compatible format that can be
74 | /// converted to graphical representation. But instead of scanning just for
75 | /// cycles, it outputs the entire object structure tree. Warning: it can be
76 | /// a lot of data.
77 | ///
78 | TDUnitXLeakCheckGraphMemoryLeakMonitor = class(TDUnitXLeakCheckCycleMemoryLeakMonitor)
79 | public
80 | procedure AfterConstruction; override;
81 | end;
82 |
83 | implementation
84 |
85 | {$REGION 'TDUnitXLeakCheckCycleMemoryLeakMonitor'}
86 |
87 | procedure TDUnitXLeakCheckCycleMemoryLeakMonitor.AfterConstruction;
88 | begin
89 | inherited;
90 | ScanProc := ScanForCycles;
91 | // It is always useful but only supported with extended RTTI and not appended
92 | // otherwise.
93 | FFormat := [TCycleFormat.WithField];
94 | end;
95 |
96 | procedure TDUnitXLeakCheckCycleMemoryLeakMonitor.AppendCycles(
97 | var ErrorMsg: string; ASnapshot: Pointer);
98 | var
99 | Leaks: TLeaks;
100 | Leak: TLeak;
101 | Cycles: TCycles;
102 | Flags: TScanFlags;
103 | Formatter: TCyclesFormatter;
104 | begin
105 | Formatter := TCyclesFormatter.Create(FFormat);
106 | // See LSnapshot in GetMemoryUseMsg
107 | TLeakCheck.MarkNotLeaking(ASnapshot);
108 |
109 | Flags := [];
110 | if UseExtendedRtti then
111 | Include(Flags, TScanFlag.UseExtendedRtti);
112 |
113 | Leaks := TLeakCheck.GetLeaks(Self.Snapshot);
114 | try
115 | for Leak in Leaks do
116 | if Leak.TypeKind = LeakCheck.tkClass then
117 | begin
118 | Cycles := ScanProc(Leak.Data, Flags, OnInstanceIgnored);
119 | Formatter.Append(Cycles);
120 | end;
121 | finally
122 | Leaks.Free;
123 | end;
124 |
125 | ErrorMsg := ErrorMsg + sLineBreak + Formatter.ToString;
126 | end;
127 |
128 | function TDUnitXLeakCheckCycleMemoryLeakMonitor.GetReport: string;
129 | var
130 | // Will mark any internal allocations of this functions as not a leak
131 | LSnapshot: TLeakCheck.TSnapshot;
132 | begin
133 | LSnapshot.Create;
134 | Result := inherited;
135 | AppendCycles(Result, LSnapshot.Snapshot);
136 | end;
137 |
138 | {$ENDREGION}
139 |
140 | {$REGION 'TDUnitXLeakCheckCycleGraphMemoryLeakMonitor'}
141 |
142 | procedure TDUnitXLeakCheckCycleGraphMemoryLeakMonitor.AfterConstruction;
143 | begin
144 | inherited;
145 | FFormat := [TCycleFormat.Graphviz, TCycleFormat.WithAddress,
146 | TCycleFormat.WithField, TCycleFormat.FindRoots];
147 | end;
148 |
149 | {$ENDREGION}
150 |
151 | {$REGION 'TDUnitXLeakCheckGraphMemoryLeakMonitor'}
152 |
153 | procedure TDUnitXLeakCheckGraphMemoryLeakMonitor.AfterConstruction;
154 | begin
155 | inherited;
156 | FFormat := TCyclesFormatter.CompleteGraph;
157 | ScanProc := ScanGraph;
158 | end;
159 |
160 | {$ENDREGION}
161 |
162 | end.
163 |
--------------------------------------------------------------------------------
/License.txt:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
10 |
11 | "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
12 |
13 | "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
14 |
15 | "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
16 |
17 | "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
18 |
19 | "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
20 |
21 | "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
22 |
23 | "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
24 |
25 | "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
26 |
27 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
28 |
29 | 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
30 |
31 | 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
32 |
33 | 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
34 |
35 | You must give any other recipients of the Work or Derivative Works a copy of this License; and
36 |
37 | You must cause any modified files to carry prominent notices stating that You changed the files; and
38 |
39 | You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
40 |
41 | If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
42 | You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
43 | 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
44 |
45 | 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
46 |
47 | 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
48 |
49 | 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
50 |
51 | 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
52 |
53 | END OF TERMS AND CONDITIONS
--------------------------------------------------------------------------------
/Samples/CustomLeakReport/.gitignore:
--------------------------------------------------------------------------------
1 | Win32/
2 | Win64/
3 | LeakCheckCustomReport.dproj.local
4 | LeakCheckCustomReport.identcache
5 | LeakCheckCustomReport.dsk
6 |
--------------------------------------------------------------------------------
/Samples/CustomLeakReport/LeakCheckCustomReport.dpr:
--------------------------------------------------------------------------------
1 | program LeakCheckCustomReport;
2 |
3 | {$R *.res}
4 |
5 | uses
6 | // LeakCheck, // Does not need do be defined here (LeakCheck.Report will do it) unless you want to reference it from the DPR
7 | LeakCheck.Report, // Me first! - I don't have any dependencies but LeakCheck so I finalize after all other units
8 | LeakCheck.Setup.Trace, // (Optional) Then me - Run setup to configure stack tracing for us
9 | LeakCheck.Report.FileLog, // Then me - I'm the one that pulls some dependencies and have all the functionality
10 | Classes, // All other units
11 | Forms,
12 | StdCtrls,
13 | TestMain in 'TestMain.pas' {Form1};
14 |
15 | begin
16 | Application.Initialize;
17 | Application.CreateForm(TForm1, Form1);
18 | Application.Run;
19 | end.
20 |
--------------------------------------------------------------------------------
/Samples/CustomLeakReport/LeakCheckCustomReport.res:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shadow-cs/delphi-leakcheck/67cc1bdfb670242b5b15d5942174c0024d7117b2/Samples/CustomLeakReport/LeakCheckCustomReport.res
--------------------------------------------------------------------------------
/Samples/CustomLeakReport/TestMain.dfm:
--------------------------------------------------------------------------------
1 | object Form1: TForm1
2 | Left = 0
3 | Top = 0
4 | Caption = 'Form1'
5 | ClientHeight = 337
6 | ClientWidth = 635
7 | Color = clBtnFace
8 | Font.Charset = DEFAULT_CHARSET
9 | Font.Color = clWindowText
10 | Font.Height = -11
11 | Font.Name = 'Tahoma'
12 | Font.Style = []
13 | OldCreateOrder = False
14 | PixelsPerInch = 96
15 | TextHeight = 13
16 | object cmdAddLeak: TButton
17 | Left = 288
18 | Top = 176
19 | Width = 75
20 | Height = 25
21 | Caption = 'Add leak...'
22 | TabOrder = 0
23 | OnClick = cmdAddLeakClick
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/Samples/CustomLeakReport/TestMain.pas:
--------------------------------------------------------------------------------
1 | unit TestMain;
2 |
3 | interface
4 |
5 | uses
6 | Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
7 | Dialogs, StdCtrls;
8 |
9 | type
10 | TForm1 = class(TForm)
11 | cmdAddLeak: TButton;
12 | procedure cmdAddLeakClick(Sender: TObject);
13 | private
14 | { Private declarations }
15 | public
16 | { Public declarations }
17 | end;
18 |
19 | var
20 | Form1: TForm1;
21 |
22 | implementation
23 |
24 | {$R *.dfm}
25 |
26 | procedure TForm1.cmdAddLeakClick(Sender: TObject);
27 | begin
28 | TButton.Create(nil); // Create a leak here
29 | end;
30 |
31 | end.
32 |
--------------------------------------------------------------------------------
/Samples/CustomLeakReportFMX/.gitignore:
--------------------------------------------------------------------------------
1 | Win32/
2 | Win64/
3 | Android/
4 | LeakCheckCustomReportFMX.dproj.local
5 | LeakCheckCustomReportFMX.identcache
6 | AndroidManifest.template.xml
7 |
--------------------------------------------------------------------------------
/Samples/CustomLeakReportFMX/LeakCheckCustomReportFMX.deployproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 12
5 |
6 |
7 |
8 | LeakCheckCustomReportFMX\res\drawable-xlarge\
9 | splash_image.png
10 | 1
11 |
12 |
13 | True
14 |
15 |
16 | LeakCheckCustomReportFMX\res\drawable-large\
17 | splash_image.png
18 | 1
19 |
20 |
21 | True
22 |
23 |
24 | LeakCheckCustomReportFMX\library\lib\armeabi\
25 | libLeakCheckCustomReportFMX.so
26 | 1
27 |
28 |
29 | True
30 |
31 |
32 | LeakCheckCustomReportFMX\res\drawable-xxhdpi\
33 | ic_launcher.png
34 | 1
35 |
36 |
37 | True
38 |
39 |
40 | LeakCheckCustomReportFMX\res\drawable-mdpi\
41 | ic_launcher.png
42 | 1
43 |
44 |
45 | True
46 |
47 |
48 | LeakCheckCustomReportFMX\res\drawable\
49 | splash_image_def.xml
50 | 1
51 |
52 |
53 | True
54 |
55 |
56 | LeakCheckCustomReportFMX\library\lib\mips\
57 | libLeakCheckCustomReportFMX.so
58 | 1
59 |
60 |
61 | True
62 |
63 |
64 | LeakCheckCustomReportFMX\
65 | AndroidManifest.xml
66 | 1
67 |
68 |
69 | True
70 |
71 |
72 | LeakCheckCustomReportFMX\res\drawable-small\
73 | splash_image.png
74 | 1
75 |
76 |
77 | True
78 |
79 |
80 | LeakCheckCustomReportFMX\classes\
81 | classes.dex
82 | 1
83 |
84 |
85 | True
86 |
87 |
88 | LeakCheckCustomReportFMX\res\values\
89 | styles.xml
90 | 1
91 |
92 |
93 | True
94 |
95 |
96 | LeakCheckCustomReportFMX\res\drawable-xhdpi\
97 | ic_launcher.png
98 | 1
99 |
100 |
101 | True
102 |
103 |
104 | LeakCheckCustomReportFMX\library\lib\armeabi-v7a\
105 | libLeakCheckCustomReportFMX.so
106 | 1
107 |
108 |
109 | True
110 | True
111 |
112 |
113 | LeakCheckCustomReportFMX\library\lib\armeabi-v7a\
114 | gdbserver
115 | 1
116 |
117 |
118 | True
119 |
120 |
121 | LeakCheckCustomReportFMX\res\drawable-ldpi\
122 | ic_launcher.png
123 | 1
124 |
125 |
126 | True
127 |
128 |
129 | LeakCheckCustomReportFMX\library\lib\x86\
130 | libLeakCheckCustomReportFMX.so
131 | 1
132 |
133 |
134 | True
135 |
136 |
137 | LeakCheckCustomReportFMX\res\drawable-hdpi\
138 | ic_launcher.png
139 | 1
140 |
141 |
142 | True
143 |
144 |
145 | LeakCheckCustomReportFMX\res\drawable-normal\
146 | splash_image.png
147 | 1
148 |
149 |
150 | True
151 |
152 |
153 |
154 |
155 |
156 | LeakCheckCustomReportFMX\
157 | LeakCheckCustomReportFMX.exe
158 | 0
159 |
160 |
161 | True
162 | True
163 |
164 |
165 |
166 |
167 | LeakCheckCustomReportFMX.app\Contents\MacOS\
168 | libcgunwind.1.0.dylib
169 | 1
170 |
171 |
172 | True
173 |
174 |
175 |
176 |
177 | LeakCheckCustomReportFMX.app\
178 | libcgunwind.1.0.dylib
179 | 1
180 |
181 |
182 | True
183 |
184 |
185 |
186 |
187 |
--------------------------------------------------------------------------------
/Samples/CustomLeakReportFMX/LeakCheckCustomReportFMX.dpr:
--------------------------------------------------------------------------------
1 | program LeakCheckCustomReportFMX;
2 |
3 | uses
4 | // LeakCheck, // Does not need do be defined here (LeakCheck.Report will do it) unless you want to reference it from the DPR
5 | LeakCheck.Report, // Me first! - I don't have any dependencies but LeakCheck so I finalize after all other units
6 | LeakCheck.Setup.Trace, // (Optional) Then me - Run setup to configure stack tracing for us
7 | LeakCheck.Report.FileLog, // Then me - I'm the one that pulls some dependencies and have all the functionality
8 | System.StartUpCopy, // All other units
9 | FMX.Forms,
10 | TestMainFMX in 'TestMainFMX.pas' {Form2};
11 |
12 | {$R *.res}
13 |
14 | begin
15 | Application.Initialize;
16 | Application.CreateForm(TForm2, Form2);
17 | Application.Run;
18 | end.
19 |
--------------------------------------------------------------------------------
/Samples/CustomLeakReportFMX/LeakCheckCustomReportFMX.res:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shadow-cs/delphi-leakcheck/67cc1bdfb670242b5b15d5942174c0024d7117b2/Samples/CustomLeakReportFMX/LeakCheckCustomReportFMX.res
--------------------------------------------------------------------------------
/Samples/CustomLeakReportFMX/TestMainFMX.fmx:
--------------------------------------------------------------------------------
1 | object Form2: TForm2
2 | Left = 0
3 | Top = 0
4 | Caption = 'Form2'
5 | ClientHeight = 480
6 | ClientWidth = 640
7 | FormFactor.Width = 320
8 | FormFactor.Height = 480
9 | FormFactor.Devices = [Desktop]
10 | DesignerMasterStyle = 0
11 | object cmdExit: TButton
12 | Position.X = 220.000000000000000000
13 | Position.Y = 244.000000000000000000
14 | Size.Width = 177.000000000000000000
15 | Size.Height = 85.000000000000000000
16 | Size.PlatformDefault = False
17 | TabOrder = 1
18 | Text = 'Exit'
19 | OnClick = cmdExitClick
20 | end
21 | object cmdAddLeak: TButton
22 | Position.X = 220.000000000000000000
23 | Position.Y = 104.000000000000000000
24 | Size.Width = 177.000000000000000000
25 | Size.Height = 85.000000000000000000
26 | Size.PlatformDefault = False
27 | TabOrder = 0
28 | Text = 'Add leak...'
29 | OnClick = cmdAddLeakClick
30 | end
31 | end
32 |
--------------------------------------------------------------------------------
/Samples/CustomLeakReportFMX/TestMainFMX.pas:
--------------------------------------------------------------------------------
1 | unit TestMainFMX;
2 |
3 | interface
4 |
5 | uses
6 | System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
7 | FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.StdCtrls;
8 |
9 | type
10 | TForm2 = class(TForm)
11 | cmdExit: TButton;
12 | cmdAddLeak: TButton;
13 | procedure cmdAddLeakClick(Sender: TObject);
14 | procedure cmdExitClick(Sender: TObject);
15 | private
16 | { Private declarations }
17 | public
18 | { Public declarations }
19 | end;
20 |
21 | var
22 | Form2: TForm2;
23 |
24 | implementation
25 |
26 | {$R *.fmx}
27 |
28 | procedure TForm2.cmdAddLeakClick(Sender: TObject);
29 | var
30 | o: TObject;
31 | begin
32 | Pointer(o) := TButton.Create(nil);
33 | end;
34 |
35 | procedure TForm2.cmdExitClick(Sender: TObject);
36 | begin
37 | Application.Terminate;
38 | end;
39 |
40 | end.
41 |
--------------------------------------------------------------------------------
/Source/LeakCheck.Collections.pas:
--------------------------------------------------------------------------------
1 | {***************************************************************************}
2 | { }
3 | { LeakCheck for Delphi }
4 | { }
5 | { Copyright (c) 2015 Honza Rames }
6 | { }
7 | { https://bitbucket.org/shadow_cs/delphi-leakcheck }
8 | { }
9 | {***************************************************************************}
10 | { }
11 | { Licensed under the Apache License, Version 2.0 (the "License"); }
12 | { you may not use this file except in compliance with the License. }
13 | { You may obtain a copy of the License at }
14 | { }
15 | { http://www.apache.org/licenses/LICENSE-2.0 }
16 | { }
17 | { Unless required by applicable law or agreed to in writing, software }
18 | { distributed under the License is distributed on an "AS IS" BASIS, }
19 | { WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. }
20 | { See the License for the specific language governing permissions and }
21 | { limitations under the License. }
22 | { }
23 | {***************************************************************************}
24 |
25 | unit LeakCheck.Collections;
26 |
27 | {$I LeakCheck.inc}
28 |
29 | interface
30 |
31 | uses
32 | LeakCheck,
33 | {$IFDEF MSWINDOWS}
34 | Windows,
35 | {$ENDIF}
36 | Generics.Collections;
37 |
38 | type
39 | IDictionary = interface
40 | function TryGetValue(const Key: TKey; out Value: TValue): Boolean;
41 | procedure AddOrSetValue(const Key: TKey; const Value: TValue);
42 | function GetEnumerator: Generics.Collections.TDictionary.TPairEnumerator;
43 | end;
44 |
45 | TDictionary = class(Generics.Collections.TDictionary,
46 | IDictionary)
47 | protected
48 | {$IFNDEF AUTOREFCOUNT}
49 | FRefCount: Integer;
50 | {$ENDIF}
51 | function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
52 | function _AddRef: Integer; stdcall;
53 | function _Release: Integer; stdcall;
54 | public
55 | {$IFNDEF AUTOREFCOUNT}
56 | procedure AfterConstruction; override;
57 | procedure BeforeDestruction; override;
58 | class function NewInstance: TObject; override;
59 | {$ENDIF}
60 | end;
61 |
62 | implementation
63 |
64 | { TDictionary }
65 |
66 | {$IFNDEF AUTOREFCOUNT}
67 | procedure TDictionary.AfterConstruction;
68 | begin
69 | inherited;
70 | // Release the constructor's implicit refcount
71 | {$IFNDEF MSWINDOWS}
72 | AtomicDecrement(FRefCount);
73 | {$ELSE MSWINDOWS}
74 | InterlockedDecrement(FRefCount);
75 | {$ENDIF MSWINDOWS}
76 | end;
77 |
78 | procedure TDictionary.BeforeDestruction;
79 | begin
80 | if FRefCount <> 0 then
81 | System.Error(reInvalidPtr);
82 | inherited;
83 | end;
84 |
85 | class function TDictionary.NewInstance: TObject;
86 | begin
87 | Result := inherited NewInstance;
88 | TDictionary(Result).FRefCount := 1;
89 | end;
90 | {$ENDIF}
91 |
92 | function TDictionary.QueryInterface(const IID: TGUID;
93 | out Obj): HResult;
94 | begin
95 | if GetInterface(IID, Obj) then
96 | Result := 0
97 | else
98 | Result := E_NOINTERFACE;
99 | end;
100 |
101 | function TDictionary._AddRef: Integer;
102 | begin
103 | {$IFNDEF AUTOREFCOUNT}
104 | {$IFNDEF MSWINDOWS}
105 | Result := AtomicIncrement(FRefCount);
106 | {$ELSE MSWINDOWS}
107 | Result := InterlockedIncrement(FRefCount);
108 | {$ENDIF MSWINDOWS}
109 | {$ELSE AUTOREFCOUNT}
110 | Result := __ObjAddRef;
111 | {$ENDIF AUTOREFCOUNT}
112 | end;
113 |
114 | function TDictionary._Release: Integer;
115 | begin
116 | {$IFNDEF AUTOREFCOUNT}
117 | {$IFNDEF MSWINDOWS}
118 | Result := AtomicDecrement(FRefCount);
119 | {$ELSE MSWINDOWS}
120 | Result := InterlockedDecrement(FRefCount);
121 | {$ENDIF MSWINDOWS}
122 | if Result = 0 then
123 | Destroy;
124 | {$ELSE AUTOREFCOUNT}
125 | Result := __ObjRelease;
126 | {$ENDIF AUTOREFCOUNT}
127 | end;
128 |
129 | end.
130 |
--------------------------------------------------------------------------------
/Source/LeakCheck.Configuration.inc:
--------------------------------------------------------------------------------
1 | {***************************************************************************}
2 | { }
3 | { LeakCheck for Delphi }
4 | { }
5 | { Copyright (c) 2015 Honza Rames }
6 | { }
7 | { https://bitbucket.org/shadow_cs/delphi-leakcheck }
8 | { }
9 | {***************************************************************************}
10 | { }
11 | { Licensed under the Apache License, Version 2.0 (the "License"); }
12 | { you may not use this file except in compliance with the License. }
13 | { You may obtain a copy of the License at }
14 | { }
15 | { http://www.apache.org/licenses/LICENSE-2.0 }
16 | { }
17 | { Unless required by applicable law or agreed to in writing, software }
18 | { distributed under the License is distributed on an "AS IS" BASIS, }
19 | { WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. }
20 | { See the License for the specific language governing permissions and }
21 | { limitations under the License. }
22 | { }
23 | {***************************************************************************}
24 |
25 | const
26 | LeakCheckEnabled = {$IFNDEF LEAKCHECK_DISABLE}True{$ELSE}False{$ENDIF};
27 |
28 | ///
29 | /// Maximum depth of stack traces. If greater than zero, stack trace will be
30 | /// acquired when memory is allocated.
31 | ///
32 | MaxStackSize = 64;
33 |
34 | ///
35 | /// Footer can be used to detect memory corruption and range errors. Enabled
36 | /// if greater than 0. (Size in multiples of pointer size)
37 | ///
38 | FooterSize = 0;
39 |
40 | ///
41 | /// Separator between internal LeakCheck data and the returned pointer.
42 | /// Can be used to detect memory corruption. Enabled if greater than 0.
43 | /// (Size in multiples of pointer size)
44 | ///
45 | SeparatorSize = 4;
46 |
47 | ///
48 | /// If enabled virtual calls on freed object will be intercepted and
49 | /// reported.
50 | ///
51 | EnableVirtualCallsOnFreedObjectInterception = True and LeakCheckEnabled;
52 |
53 | ///
54 | /// If enabled interface calls on freed object will be intercepted and
55 | /// reported. If not used together with VTablesFastFill significant
56 | /// slow-down will be exhibited.
57 | ///
58 | EnableInterfaceCallsOnFreedObjectInterception = False and LeakCheckEnabled;
59 |
60 | ///
61 | /// Since checking for class type is pretty slow, enabling this option will
62 | /// use just a faster move but will clear all memory not just objects.
63 | ///
64 | EnableInterfaceVTablesFastFill = True and
65 | EnableInterfaceCallsOnFreedObjectInterception;
66 |
67 | ///
68 | /// If enabled fake VMT is assigned to freed objects so other utilities do
69 | /// not raise AVs if they reach dangling pointer of a freed object (if they
70 | /// do freed object checking). If freed object virtual call interception is
71 | /// not enabled. Virtual call on this pointer will raise NPE.
72 | ///
73 | EnableFreedObjectDetection = (True or
74 | EnableVirtualCallsOnFreedObjectInterception or
75 | EnableInterfaceCallsOnFreedObjectInterception) and LeakCheckEnabled;
76 |
77 | // Used internally
78 | EnableFreeCleanup = EnableFreedObjectDetection or (SeparatorSize > 0) or
79 | (FooterSize > 0);
80 |
81 | // Used internally
82 | NeedsIndexTypes =
83 | EnableVirtualCallsOnFreedObjectInterception or
84 | EnableInterfaceCallsOnFreedObjectInterception;
85 |
86 | ///
87 | /// If enabled (together with MaxStackSize) stack trace is acquired
88 | /// also when memory is released.
89 | ///
90 | RecordFreeStackTrace = False or
91 | EnableVirtualCallsOnFreedObjectInterception or
92 | EnableInterfaceCallsOnFreedObjectInterception;
93 |
94 | ///
95 | /// Use separate heap for internal allocations, disable if you leak count
96 | /// is getting bigger and the internal functions fail to function.
97 | /// Enabling this option will ensure that freed memory of current process
98 | /// will not be overwritten by LeakCheck internal data.
99 | /// (MS Windows only)
100 | ///
101 | UseInternalHeap = True;
102 |
103 | {$IF Declared(TScanner)}
104 | type
105 | ///
106 | /// Type of sanitation to be done.
107 | ///
108 | TScannerSanitationType = (
109 | ///
110 | /// No sanitation at all.
111 | ///
112 | None,
113 | ///
114 | /// Check for TLeckCheck.TFreedObject, but do not perform more
115 | /// complex tests (faster).
116 | ///
117 | FreedObject,
118 | ///
119 | /// The scanner will put object through series of tests to determine
120 | /// whether the given pointer is valid and some basic class fields are
121 | /// readable (About 10x slower, See LeakCheck.GetObjectClass).
122 | ///
123 | Complex);
124 |
125 | const
126 | ///
127 | /// Enable sanitation of object pointers in a scanner.
128 | ///
129 | ScannerEnableObjectPointerSanitation =
130 | {$IF EnableFreedObjectDetection}
131 | TScannerSanitationType.FreedObject
132 | {$ELSE}
133 | TScannerSanitationType.Complex
134 | {$IFEND}
135 | ;
136 | {$IFEND}
137 |
--------------------------------------------------------------------------------
/Source/LeakCheck.Cycle.Utils.pas:
--------------------------------------------------------------------------------
1 | {***************************************************************************}
2 | { }
3 | { LeakCheck for Delphi }
4 | { }
5 | { Copyright (c) 2015 Honza Rames }
6 | { }
7 | { https://bitbucket.org/shadow_cs/delphi-leakcheck }
8 | { }
9 | {***************************************************************************}
10 | { }
11 | { Licensed under the Apache License, Version 2.0 (the "License"); }
12 | { you may not use this file except in compliance with the License. }
13 | { You may obtain a copy of the License at }
14 | { }
15 | { http://www.apache.org/licenses/LICENSE-2.0 }
16 | { }
17 | { Unless required by applicable law or agreed to in writing, software }
18 | { distributed under the License is distributed on an "AS IS" BASIS, }
19 | { WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. }
20 | { See the License for the specific language governing permissions and }
21 | { limitations under the License. }
22 | { }
23 | {***************************************************************************}
24 |
25 | unit LeakCheck.Cycle.Utils;
26 |
27 | {$I LeakCheck.inc}
28 |
29 | interface
30 |
31 | uses
32 | LeakCheck,
33 | LeakCheck.Trace,
34 | LeakCheck.Utils,
35 | LeakCheck.Cycle;
36 |
37 | type
38 | TGraphIgnorer = class(TScanner)
39 | strict protected
40 | { Do not use TypeStart! Since AFieldName is sometimes passed here if
41 | inspecting first class field and then class instance, false positives may
42 | be reached which would lead to errors and always use more specific
43 | functions. }
44 | procedure ScanClassInternal(const Instance: TObject); override;
45 | end;
46 |
47 | ///
48 | /// Ignore all leaks in object graph given by entry-point. Note that all
49 | /// object inside the graph have to have all object fields with valid
50 | /// references. If invalid reference is kept in any of the fields and is
51 | /// freed without nil-ing the field, AV will probably be raised. May also
52 | /// cause issues if used in multi-threaded environment (that have
53 | /// race-condition issues).
54 | ///
55 | procedure IgnoreGraphLeaks(const Entrypoint: TObject; Flags: TScanFlags = [];
56 | InstanceIgnoreProc: TScanner.TIsInstanceIgnored = nil);
57 |
58 | type
59 | TIgnore = class(LeakCheck.Utils.TIgnore)
60 | public
61 | ///
62 | /// Ignore the class and all fields from it and all objects within its
63 | /// object graph (all nested object that are referenced from this
64 | /// object). May easily ignore large portions of your memory. Use with
65 | /// care.
66 | ///
67 | class function AnyAndGraph(const Instance: TObject; ClassType: TClass): Boolean; static;
68 | end;
69 |
70 | TIgnoreInterface = class(LeakCheck.Utils.TIgnoreInterface)
71 | public
72 | ///
73 | /// Ignore the implementing class and all fields from it and all objects
74 | /// within itsobject graph (all nested object that are referenced from
75 | /// this object). May easily ignore large portions of your memory. Use
76 | /// with care.
77 | ///
78 | class function ImplementsAndGraph(const Instance: TObject; ClassType: TClass): Boolean; static;
79 | end;
80 |
81 | implementation
82 |
83 | procedure IgnoreGraphLeaks(const Entrypoint: TObject; Flags: TScanFlags = [];
84 | InstanceIgnoreProc: TScanner.TIsInstanceIgnored = nil);
85 | begin
86 | TGraphIgnorer.Scan(Entrypoint, TGraphIgnorer, Flags, InstanceIgnoreProc);
87 | end;
88 |
89 | {$REGION 'TGraphIgnorer'}
90 |
91 | procedure TGraphIgnorer.ScanClassInternal(const Instance: TObject);
92 | begin
93 | inherited;
94 | if Assigned(Instance) then
95 | begin
96 | {$IFDEF LEAKCHECK_TRACE}
97 | Trace('Ignoring: ' + Instance.QualifiedClassName);
98 | {$ENDIF}
99 | RegisterExpectedMemoryLeak(Instance);
100 | end;
101 | end;
102 |
103 | {$ENDREGION}
104 |
105 | {$REGION 'TIgnore'}
106 |
107 | class function TIgnore.AnyAndGraph(const Instance: TObject;
108 | ClassType: TClass): Boolean;
109 | begin
110 | Result := ClassType.InheritsFrom(T);
111 | if Result then
112 | IgnoreGraphLeaks(Instance, [TScanFlag.UseExtendedRtti]);
113 | end;
114 |
115 | {$ENDREGION}
116 |
117 | {$REGION 'TIgnoreInterface'}
118 |
119 | class function TIgnoreInterface.ImplementsAndGraph(const Instance: TObject;
120 | ClassType: TClass): Boolean;
121 | begin
122 | Result := Implements(Instance, ClassType);
123 | if Result then
124 | IgnoreGraphLeaks(Instance, [TScanFlag.UseExtendedRtti]);
125 | end;
126 |
127 | {$ENDREGION}
128 |
129 | end.
130 |
--------------------------------------------------------------------------------
/Source/LeakCheck.DUnit.pas:
--------------------------------------------------------------------------------
1 | {***************************************************************************}
2 | { }
3 | { LeakCheck for Delphi }
4 | { }
5 | { Copyright (c) 2015 Honza Rames }
6 | { }
7 | { https://bitbucket.org/shadow_cs/delphi-leakcheck }
8 | { }
9 | {***************************************************************************}
10 | { }
11 | { Licensed under the Apache License, Version 2.0 (the "License"); }
12 | { you may not use this file except in compliance with the License. }
13 | { You may obtain a copy of the License at }
14 | { }
15 | { http://www.apache.org/licenses/LICENSE-2.0 }
16 | { }
17 | { Unless required by applicable law or agreed to in writing, software }
18 | { distributed under the License is distributed on an "AS IS" BASIS, }
19 | { WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. }
20 | { See the License for the specific language governing permissions and }
21 | { limitations under the License. }
22 | { }
23 | {***************************************************************************}
24 |
25 | unit LeakCheck.DUnit;
26 |
27 | {$I LeakCheck.inc}
28 |
29 | interface
30 |
31 | uses
32 | LeakCheck,
33 | SysUtils,
34 | TestFramework;
35 |
36 | type
37 | TLeakCheckMonitor = class(TInterfacedObject, IMemLeakMonitor, IDUnitMemLeakMonitor)
38 | private
39 | FSnapshot: TLeakCheck.TSnapshot;
40 | function LeakDetail(ASnapshot: Pointer): string;
41 | function GetSnapshot: Pointer;
42 | strict protected
43 | property Snapshot: Pointer read GetSnapshot;
44 | public
45 | procedure AfterConstruction; override;
46 |
47 | // IMemLeakMonitor
48 | function MemLeakDetected(out LeakSize: Integer): Boolean; overload;
49 |
50 | // IDUnitMemLeakMonitor
51 | function MemLeakDetected(const AllowedLeakSize: Integer;
52 | const FailOnMemoryRecovery: Boolean;
53 | out LeakSize: Integer): Boolean; overload;
54 | function MemLeakDetected(const AllowedValuesGetter: TListIterator;
55 | const FailOnMemoryRecovery: Boolean;
56 | out LeakIndex: integer;
57 | out LeakSize: Integer): Boolean; overload;
58 | function GetMemoryUseMsg(const FailOnMemoryRecovery: Boolean;
59 | const TestProcChangedMem: Integer;
60 | out ErrorMsg: string): Boolean; overload;
61 | function GetMemoryUseMsg(const FailOnMemoryRecovery: boolean;
62 | const TestSetupChangedMem: Integer;
63 | const TestProcChangedMem: Integer;
64 | const TestTearDownChangedMem: Integer;
65 | const TestCaseChangedMem: Integer;
66 | out ErrorMsg: string): boolean; overload;
67 | procedure MarkMemInUse;
68 | procedure TestMethodDone(const Test: ITest);
69 | end;
70 |
71 | implementation
72 |
73 | uses
74 | LeakCheck.Utils;
75 |
76 | { TLeakCheckMonitor }
77 |
78 | function TLeakCheckMonitor.GetMemoryUseMsg(const FailOnMemoryRecovery: Boolean;
79 | const TestProcChangedMem: Integer; out ErrorMsg: string): Boolean;
80 | var
81 | // Will mark any internal allocations of this functions as not a leak
82 | LSnapshot: TLeakCheck.TSnapshot;
83 | begin
84 | ErrorMsg := '';
85 | LSnapshot.Create;
86 |
87 | if TestProcChangedMem > 0 then
88 | ErrorMsg := IntToStr(TestProcChangedMem) +
89 | ' Bytes Memory Leak in Test Procedure' + LeakDetail(LSnapshot.Snapshot)
90 | else
91 | if (TestProcChangedMem < 0) and (FailOnMemoryRecovery) then
92 | ErrorMsg := IntToStr(Abs(TestProcChangedMem)) +
93 | ' Bytes Memory Recovered in Test Procedure' + LeakDetail(LSnapshot.Snapshot);
94 |
95 | Result := Length(ErrorMsg) = 0;
96 | end;
97 |
98 | procedure TLeakCheckMonitor.AfterConstruction;
99 | begin
100 | inherited;
101 | MarkMemInUse;
102 | end;
103 |
104 | function TLeakCheckMonitor.LeakDetail(ASnapshot: Pointer): string;
105 | var
106 | Report: LeakString;
107 | begin
108 | // See LSnapshot in GetMemoryUseMsg
109 | TLeakCheck.MarkNotLeaking(ASnapshot);
110 | Report := TLeakCheck.GetReport(Snapshot);
111 | // Report is ASCII so it can be easily treated as UTF-8
112 | Result := sLineBreak + UTF8ToString(Report);
113 | Report.Free;
114 | end;
115 |
116 | function TLeakCheckMonitor.GetMemoryUseMsg(const FailOnMemoryRecovery: boolean;
117 | const TestSetupChangedMem, TestProcChangedMem, TestTearDownChangedMem,
118 | TestCaseChangedMem: Integer; out ErrorMsg: string): boolean;
119 | var
120 | // Will mark any internal allocations of this functions as not a leak
121 | LSnapshot: TLeakCheck.TSnapshot;
122 | Location: string;
123 | begin
124 | Result := False;
125 | ErrorMsg := '';
126 | LSnapshot.Create;
127 |
128 | if (TestSetupChangedMem = 0) and (TestProcChangedMem = 0) and
129 | (TestTearDownChangedMem = 0) and (TestCaseChangedMem <> 0) then
130 | begin
131 | ErrorMsg :=
132 | 'Test leaked memory. No leaks in Setup, TestProc or Teardown but '+
133 | IntToStr(TestCaseChangedMem) +
134 | ' Bytes Memory Leak reported across TestCase' + LeakDetail(LSnapshot.Snapshot);
135 | Exit;
136 | end;
137 |
138 | if (TestSetupChangedMem + TestProcChangedMem + TestTearDownChangedMem) <>
139 | TestCaseChangedMem then
140 | begin
141 | ErrorMsg :=
142 | 'Test leaked memory. Sum of Setup, TestProc and Teardown leaks <> '+
143 | IntToStr(TestCaseChangedMem) +
144 | ' Bytes Memory Leak reported across TestCase' + LeakDetail(LSnapshot.Snapshot);
145 | Exit;
146 | end;
147 |
148 | Result := True;
149 | if TestCaseChangedMem = 0 then
150 | Exit; // Don't waste further time here
151 |
152 | if (TestCaseChangedMem < 0) and not FailOnMemoryRecovery then
153 | Exit; // Don't waste further time here
154 |
155 |
156 | // We get to here because there is a memory use imbalance to report.
157 | if (TestCaseChangedMem > 0) then
158 | ErrorMsg := IntToStr(TestCaseChangedMem) + ' Bytes memory leak ('
159 | else
160 | ErrorMsg := IntToStr(TestCaseChangedMem) + ' Bytes memory recovered (';
161 |
162 | Location := '';
163 |
164 | if (TestSetupChangedMem <> 0) then
165 | Location := 'Setup= ' + IntToStr(TestSetupChangedMem) + ' ';
166 | if (TestProcChangedMem <> 0) then
167 | Location := Location + 'TestProc= ' + IntToStr(TestProcChangedMem) + ' ';
168 | if (TestTearDownChangedMem <> 0) then
169 | Location := Location + 'TearDown= ' + IntToStr(TestTearDownChangedMem) + ' ';
170 |
171 | ErrorMsg := ErrorMsg + Location + ')' + LeakDetail(LSnapshot.Snapshot);
172 | Result := (Length(ErrorMsg) = 0);
173 | end;
174 |
175 | function TLeakCheckMonitor.GetSnapshot: Pointer;
176 | begin
177 | Result := FSnapshot.Snapshot;
178 | end;
179 |
180 | procedure TLeakCheckMonitor.MarkMemInUse;
181 | begin
182 | FSnapshot.Create;
183 | end;
184 |
185 | function TLeakCheckMonitor.MemLeakDetected(out LeakSize: Integer): Boolean;
186 | var
187 | Leaks: TLeaks;
188 | begin
189 | Leaks := TLeakCheck.GetLeaks(Snapshot);
190 | Result := Leaks.Length > 0;
191 | LeakSize := Leaks.TotalSize;
192 | Leaks.Free;
193 | end;
194 |
195 | function TLeakCheckMonitor.MemLeakDetected(const AllowedLeakSize: Integer;
196 | const FailOnMemoryRecovery: Boolean; out LeakSize: Integer): Boolean;
197 | begin
198 | LeakSize := 0;
199 | MemLeakDetected(LeakSize);
200 | Result := ((LeakSize > 0) and (LeakSize <> AllowedLeakSize)) or
201 | ((LeakSize < 0) and (FailOnMemoryRecovery) and (LeakSize <> AllowedLeakSize));
202 | end;
203 |
204 | function TLeakCheckMonitor.MemLeakDetected(
205 | const AllowedValuesGetter: TListIterator; const FailOnMemoryRecovery: Boolean;
206 | out LeakIndex, LeakSize: Integer): Boolean;
207 | var
208 | AllowedLeakSize: Integer;
209 | begin
210 | LeakIndex := 0;
211 | LeakSize := 0;
212 | Result := False;
213 | MemLeakDetected(LeakSize);
214 |
215 | if LeakSize = 0 then
216 | Exit;
217 |
218 | // Next line access value stored via SetAllowedLeakSize, if any
219 | if LeakSize = AllowedValuesGetter then
220 | Exit;
221 |
222 | // Loop over values stored via SetAllowedLeakArray
223 | repeat
224 | Inc(LeakIndex);
225 | AllowedLeakSize := AllowedValuesGetter;
226 | if (LeakSize = AllowedLeakSize) then
227 | Exit;
228 | until AllowedLeakSize = 0;
229 |
230 | Result := (LeakSize > 0) or ((LeakSize < 0) and FailOnMemoryRecovery);
231 | end;
232 |
233 | type
234 | TAbstractTestAccess = class(TAbstractTest);
235 |
236 | procedure TLeakCheckMonitor.TestMethodDone(const Test: ITest);
237 | var
238 | AbstractTest: TAbstractTestAccess;
239 | begin
240 | if Test is TAbstractTest then
241 | begin
242 | AbstractTest := TAbstractTestAccess(Test as TObject);
243 | if Assigned(AbstractTest.FStatusStrings) then
244 | IgnoreStrings(AbstractTest.FStatusStrings);
245 | end;
246 | end;
247 |
248 | initialization
249 | TestFramework.MemLeakMonitorClass := TLeakCheckMonitor;
250 |
251 | end.
252 |
--------------------------------------------------------------------------------
/Source/LeakCheck.DUnitCycle.pas:
--------------------------------------------------------------------------------
1 | {***************************************************************************}
2 | { }
3 | { LeakCheck for Delphi }
4 | { }
5 | { Copyright (c) 2015 Honza Rames }
6 | { }
7 | { https://bitbucket.org/shadow_cs/delphi-leakcheck }
8 | { }
9 | {***************************************************************************}
10 | { }
11 | { Licensed under the Apache License, Version 2.0 (the "License"); }
12 | { you may not use this file except in compliance with the License. }
13 | { You may obtain a copy of the License at }
14 | { }
15 | { http://www.apache.org/licenses/LICENSE-2.0 }
16 | { }
17 | { Unless required by applicable law or agreed to in writing, software }
18 | { distributed under the License is distributed on an "AS IS" BASIS, }
19 | { WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. }
20 | { See the License for the specific language governing permissions and }
21 | { limitations under the License. }
22 | { }
23 | {***************************************************************************}
24 |
25 | unit LeakCheck.DUnitCycle;
26 |
27 | {$I LeakCheck.inc}
28 |
29 | interface
30 |
31 | uses
32 | LeakCheck,
33 | SysUtils,
34 | TypInfo,
35 | TestFramework,
36 | LeakCheck.Cycle,
37 | LeakCheck.DUnit;
38 |
39 | type
40 | ///
41 | /// In addition to detecting leaks, it also detect reference cycles in
42 | /// those leaks. Must be enabled manually.
43 | ///
44 | TLeakCheckCycleMonitor = class(TLeakCheckMonitor, IDUnitMemLeakMonitor)
45 | strict private class var
46 | FUseExtendedRtti: Boolean;
47 | FOnInstanceIgnored: TScanner.TIsInstanceIgnored;
48 | strict protected
49 | FFormat: TCycle.TCycleFormats;
50 | ScanProc: function(const Instance: TObject; Flags: TScanFlags;
51 | InstanceIgnoreProc: TScanner.TIsInstanceIgnored): TCycles;
52 | procedure AppendCycles(var ErrorMsg: string; ASnapshot: Pointer);
53 | public
54 | procedure AfterConstruction; override;
55 | function GetMemoryUseMsg(const FailOnMemoryRecovery: Boolean;
56 | const TestProcChangedMem: Integer;
57 | out ErrorMsg: string): Boolean; overload;
58 | function GetMemoryUseMsg(const FailOnMemoryRecovery: boolean;
59 | const TestSetupChangedMem: Integer;
60 | const TestProcChangedMem: Integer;
61 | const TestTearDownChangedMem: Integer;
62 | const TestCaseChangedMem: Integer;
63 | out ErrorMsg: string): boolean; overload;
64 | class property UseExtendedRtti: Boolean read FUseExtendedRtti write FUseExtendedRtti;
65 | class property OnInstanceIgnored: TScanner.TIsInstanceIgnored read FOnInstanceIgnored write FOnInstanceIgnored;
66 | end;
67 |
68 | ///
69 | /// Extends
70 | /// functionality by outputing Graphviz DOT compatible format that can be
71 | /// converted to graphical representation.
72 | ///
73 | TLeakCheckCycleGraphMonitor = class(TLeakCheckCycleMonitor)
74 | public
75 | procedure AfterConstruction; override;
76 | end;
77 |
78 | ///
79 | /// Extends
80 | /// functionality by outputing Graphviz DOT compatible format that can be
81 | /// converted to graphical representation. But instead of scanning just for
82 | /// cycles, it outputs the entire object structure tree. Warning: it can be
83 | /// a lot of data.
84 | ///
85 | TLeakCheckGraphMonitor = class(TLeakCheckCycleMonitor)
86 | public
87 | procedure AfterConstruction; override;
88 | end;
89 |
90 | implementation
91 |
92 | {$REGION 'TLeakCheckCycleMonitor'}
93 |
94 | function TLeakCheckCycleMonitor.GetMemoryUseMsg(
95 | const FailOnMemoryRecovery: Boolean; const TestProcChangedMem: Integer;
96 | out ErrorMsg: string): Boolean;
97 | var
98 | // Will mark any internal allocations of this functions as not a leak
99 | LSnapshot: TLeakCheck.TSnapshot;
100 | begin
101 | LSnapshot.Create;
102 | Result := inherited;
103 | if not Result then
104 | AppendCycles(ErrorMsg, LSnapshot.Snapshot);
105 | end;
106 |
107 | procedure TLeakCheckCycleMonitor.AfterConstruction;
108 | begin
109 | inherited;
110 | ScanProc := ScanForCycles;
111 | // It is always useful but only supported with extended RTTI and not appended
112 | // otherwise.
113 | FFormat := [TCycleFormat.WithField];
114 | end;
115 |
116 | procedure TLeakCheckCycleMonitor.AppendCycles(var ErrorMsg: string; ASnapshot: Pointer);
117 | var
118 | Leaks: TLeaks;
119 | Leak: TLeak;
120 | Cycles: TCycles;
121 | Flags: TScanFlags;
122 | Formatter: TCyclesFormatter;
123 | begin
124 | Formatter := TCyclesFormatter.Create(FFormat);
125 | // See LSnapshot in GetMemoryUseMsg
126 | TLeakCheck.MarkNotLeaking(ASnapshot);
127 |
128 | Flags := [];
129 | if UseExtendedRtti then
130 | Include(Flags, TScanFlag.UseExtendedRtti);
131 |
132 | Leaks := TLeakCheck.GetLeaks(Self.Snapshot);
133 | try
134 | for Leak in Leaks do
135 | if Leak.TypeKind = LeakCheck.tkClass then
136 | begin
137 | Cycles := ScanProc(Leak.Data, Flags, OnInstanceIgnored);
138 | Formatter.Append(Cycles);
139 | end;
140 | finally
141 | Leaks.Free;
142 | end;
143 |
144 | ErrorMsg := ErrorMsg + sLineBreak + Formatter.ToString;
145 | end;
146 |
147 | function TLeakCheckCycleMonitor.GetMemoryUseMsg(
148 | const FailOnMemoryRecovery: boolean; const TestSetupChangedMem,
149 | TestProcChangedMem, TestTearDownChangedMem, TestCaseChangedMem: Integer;
150 | out ErrorMsg: string): boolean;
151 | var
152 | // Will mark any internal allocations of this functions as not a leak
153 | LSnapshot: TLeakCheck.TSnapshot;
154 | begin
155 | LSnapshot.Create;
156 | Result := inherited;
157 | if not Result then
158 | AppendCycles(ErrorMsg, LSnapshot.Snapshot);
159 | end;
160 |
161 | {$ENDREGION}
162 |
163 | {$REGION 'TLeakCheckCycleGraphMonitor'}
164 |
165 | procedure TLeakCheckCycleGraphMonitor.AfterConstruction;
166 | begin
167 | inherited;
168 | FFormat := [TCycleFormat.Graphviz, TCycleFormat.WithAddress,
169 | TCycleFormat.WithField, TCycleFormat.FindRoots];
170 | end;
171 |
172 | {$ENDREGION}
173 |
174 | {$REGION 'TLeakCheckGraphMonitor'}
175 |
176 | procedure TLeakCheckGraphMonitor.AfterConstruction;
177 | begin
178 | inherited;
179 | FFormat := TCyclesFormatter.CompleteGraph;
180 | ScanProc := ScanGraph;
181 | end;
182 |
183 | {$ENDREGION}
184 |
185 | end.
186 |
--------------------------------------------------------------------------------
/Source/LeakCheck.Report.FileLog.pas:
--------------------------------------------------------------------------------
1 | {***************************************************************************}
2 | { }
3 | { LeakCheck for Delphi }
4 | { }
5 | { Copyright (c) 2015 Honza Rames }
6 | { }
7 | { https://bitbucket.org/shadow_cs/delphi-leakcheck }
8 | { }
9 | {***************************************************************************}
10 | { }
11 | { Licensed under the Apache License, Version 2.0 (the "License"); }
12 | { you may not use this file except in compliance with the License. }
13 | { You may obtain a copy of the License at }
14 | { }
15 | { http://www.apache.org/licenses/LICENSE-2.0 }
16 | { }
17 | { Unless required by applicable law or agreed to in writing, software }
18 | { distributed under the License is distributed on an "AS IS" BASIS, }
19 | { WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. }
20 | { See the License for the specific language governing permissions and }
21 | { limitations under the License. }
22 | { }
23 | {***************************************************************************}
24 |
25 | unit LeakCheck.Report.FileLog;
26 |
27 | {$I LeakCheck.inc}
28 |
29 | interface
30 |
31 | uses
32 | LeakCheck,
33 | LeakCheck.Report.Utils;
34 |
35 | implementation
36 |
37 | {$IF CompilerVersion >= 25} // >= XE4
38 | {$LEGACYIFEND ON}
39 | {$IFEND}
40 |
41 | uses
42 | {$IFDEF MSWINDOWS}
43 | Windows,
44 | {$ENDIF}
45 | {$IFDEF POSIX}
46 | Posix.Unistd,
47 | {$ENDIF}
48 | {$IFDEF ANDROID}
49 | Androidapi.Log,
50 | {$ENDIF}
51 | IOUtils,
52 | SysUtils;
53 |
54 | type
55 | TLeakCheckFileReporter = class(TLeakCheckReporter)
56 | private
57 | FLogFileName: string;
58 | FGraphFileName: string;
59 | FLog: TextFile;
60 | protected
61 | constructor Create; override;
62 | procedure NoLeaks; override;
63 | procedure BeginLog; override;
64 | procedure WritelnLog(Log: MarshaledAString); override;
65 | procedure EndLog; override;
66 | procedure WriteGraph(const Graph: string); override;
67 | procedure ShowMessage; override;
68 | end;
69 |
70 | { TLeakCheckFileReporter }
71 |
72 | procedure TLeakCheckFileReporter.BeginLog;
73 | begin
74 | inherited;
75 | AssignFile(FLog, FLogFileName);
76 | Rewrite(FLog);
77 | end;
78 |
79 | constructor TLeakCheckFileReporter.Create;
80 | var
81 | BasePath, BaseName: string;
82 | begin
83 | inherited;
84 |
85 | {$IF Defined(MACOS) OR Defined(IOS) OR Defined(LEAK_REPORT_DOCUMENTS)}
86 | BasePath := TPath.GetDocumentsPath;
87 | BaseName := ExtractFileName(ParamStr(0));
88 | {$ELSEIF Defined(MSWINDOWS)}
89 | BasePath := ExtractFilePath(ParamStr(0));
90 | BaseName := ExtractFileName(ParamStr(0));
91 | {$ELSEIF Defined(ANDROID)}
92 | // Note that this requires Read/Write External Storage permissions
93 | // Write permissions option is enough, reading will be available as well
94 | BasePath := '/storage/emulated/0/';
95 | BaseName := 'LeakCheck_Log';
96 | {$IFEND}
97 | BasePath := TPath.Combine(BasePath, ChangeFileExt(BaseName, ''));
98 |
99 | FLogFileName := BasePath + '.log';
100 | FGraphFileName := BasePath + '.dot';
101 | end;
102 |
103 | procedure TLeakCheckFileReporter.EndLog;
104 | begin
105 | inherited;
106 | CloseFile(FLog);
107 | end;
108 |
109 | procedure TLeakCheckFileReporter.NoLeaks;
110 | begin
111 | inherited;
112 | DeleteFile(FLogFileName);
113 | DeleteFile(FGraphFileName);
114 | end;
115 |
116 | procedure TLeakCheckFileReporter.ShowMessage;
117 | {$IFDEF ANDROID}
118 | const
119 | TAG: MarshaledAString = MarshaledAString('leak');
120 | var
121 | M: TMarshaller;
122 | {$ENDIF}
123 | var
124 | Msg: string;
125 | begin
126 | inherited;
127 | Msg := 'Memory leak detected, see ' + FLogFileName + ' and ' + FGraphFileName;
128 | {$IFDEF MSWINDOWS}
129 | MessageBox(0, PChar(Msg), 'Memory leak', MB_ICONERROR);
130 | {$ENDIF}
131 | {$IFDEF ANDROID}
132 | __android_log_write(ANDROID_LOG_WARN, TAG, M.AsAnsi(Msg).ToPointer);
133 | {$ENDIF}
134 | end;
135 |
136 | procedure TLeakCheckFileReporter.WriteGraph(const Graph: string);
137 | var
138 | f: TextFile;
139 | begin
140 | inherited;
141 | AssignFile(f, FGraphFileName);
142 | Rewrite(f);
143 | try
144 | Writeln(f, Graph);
145 | finally
146 | CloseFile(f);
147 | end;
148 | end;
149 |
150 | procedure TLeakCheckFileReporter.WritelnLog(Log: MarshaledAString);
151 | begin
152 | inherited;
153 | Writeln(FLog, Log);
154 | end;
155 |
156 | initialization
157 | ReporterClass := TLeakCheckFileReporter;
158 |
159 | end.
160 |
--------------------------------------------------------------------------------
/Source/LeakCheck.Report.Utils.pas:
--------------------------------------------------------------------------------
1 | {***************************************************************************}
2 | { }
3 | { LeakCheck for Delphi }
4 | { }
5 | { Copyright (c) 2015 Honza Rames }
6 | { }
7 | { https://bitbucket.org/shadow_cs/delphi-leakcheck }
8 | { }
9 | {***************************************************************************}
10 | { }
11 | { Licensed under the Apache License, Version 2.0 (the "License"); }
12 | { you may not use this file except in compliance with the License. }
13 | { You may obtain a copy of the License at }
14 | { }
15 | { http://www.apache.org/licenses/LICENSE-2.0 }
16 | { }
17 | { Unless required by applicable law or agreed to in writing, software }
18 | { distributed under the License is distributed on an "AS IS" BASIS, }
19 | { WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. }
20 | { See the License for the specific language governing permissions and }
21 | { limitations under the License. }
22 | { }
23 | {***************************************************************************}
24 |
25 | unit LeakCheck.Report.Utils;
26 |
27 | {$I LeakCheck.inc}
28 |
29 | interface
30 |
31 | uses
32 | LeakCheck;
33 |
34 | type
35 | TReportFormat = (WithLog, WithCycles);
36 | TReportFormats = set of TReportFormat;
37 |
38 | TLeakCheckReporter = class
39 | protected
40 | procedure NoLeaks; virtual;
41 | procedure BeginLog; virtual;
42 | procedure WritelnLog(Log: MarshaledAString); virtual;
43 | procedure EndLog; virtual;
44 | procedure WriteGraph(const Graph: string); virtual;
45 | procedure ShowMessage; virtual;
46 | constructor Create; virtual;
47 | end;
48 | TLeakCheckReporterClass = class of TLeakCheckReporter;
49 |
50 | var
51 | ReportFormat: TReportFormats = [TReportFormat.WithLog, TReportFormat.WithCycles];
52 | ReporterClass: TLeakCheckReporterClass = TLeakCheckReporter;
53 |
54 | implementation
55 |
56 | uses
57 | LeakCheck.Report,
58 | LeakCheck.Cycle,
59 | SysUtils,
60 | TypInfo;
61 |
62 | var
63 | Reporter: TLeakCheckReporter;
64 |
65 |
66 | procedure ReportProc(const Data: MarshaledAString);
67 | begin
68 | Reporter.WritelnLog(Data);
69 | end;
70 |
71 | ///
72 | /// Will generate memory report and dangling object graph to help you
73 | /// understand what causes the leak. Works regardless of
74 | /// ReportMemoryLeaksOnShutdown.
75 | ///
76 | procedure SaveReport(const Snapshot: TLeakCheck.TSnapshot);
77 | var
78 | Formatter: TCyclesFormatter;
79 | Leaks: TLeaks;
80 | Leak: TLeak;
81 | Cycles: TCycles;
82 | InternalSnapshot: TLeakCheck.TSnapshot;
83 | begin
84 | if ReportFormat = [] then
85 | Exit;
86 |
87 | InternalSnapshot.Create;
88 | Reporter := ReporterClass.Create;
89 | // Make sure we do not report memory we just allocated
90 | TLeakCheck.MarkNotLeaking(InternalSnapshot.Snapshot);
91 | // Internal LeakCheck reporting functions use different memory manager so
92 | // they won't show up in the report so we don't need to ignore them again
93 | // (Windows uses completely separate memory manager where other platforms just
94 | // skip LeakCheck allocation mechanisms and defer that to the system memory
95 | // manager directly).
96 | Leaks := TLeakCheck.GetLeaks(Snapshot.Snapshot);
97 | try
98 | if Leaks.IsEmpty then
99 | begin
100 | Reporter.NoLeaks;
101 | Exit;
102 | end;
103 |
104 | {$IFDEF WEAKREF}
105 | // Do not report unknown pointers if WeakRefs are used, it is most likely
106 | // held by System WeakRef pool (RTL bug).
107 | TLeakCheck.IgnoredLeakTypes := [tkUnknown];
108 | {$ENDIF}
109 |
110 | // Save the log
111 | if TReportFormat.WithLog in ReportFormat then
112 | begin
113 | Reporter.BeginLog;
114 | try
115 | TLeakCheck.GetReport(ReportProc, Snapshot.Snapshot);
116 | finally
117 | Reporter.EndLog;
118 | end;
119 | end;
120 |
121 | // Save the graph
122 | if TReportFormat.WithCycles in ReportFormat then
123 | begin
124 | Formatter := TCyclesFormatter.Create([
125 | TCycleFormat.Graphviz,
126 | TCycleFormat.WithAddress,
127 | TCycleFormat.WithField]);
128 | for Leak in Leaks do
129 | if Leak.TypeKind = LeakCheck.tkClass then
130 | begin
131 | Cycles := ScanGraph(Leak.Data, [TScanFlag.UseExtendedRtti]);
132 | Formatter.Append(Cycles);
133 | end;
134 | Reporter.WriteGraph(Formatter.ToString);
135 | end;
136 |
137 | Reporter.ShowMessage;
138 |
139 | ReportMemoryLeaksOnShutdown := False;
140 | finally
141 | Leaks.Free;
142 | Reporter.Free;
143 | end;
144 | end;
145 |
146 | { TLeakCheckReport }
147 |
148 | procedure TLeakCheckReporter.BeginLog;
149 | begin
150 | // NOP
151 | end;
152 |
153 | constructor TLeakCheckReporter.Create;
154 | begin
155 | inherited;
156 | end;
157 |
158 | procedure TLeakCheckReporter.EndLog;
159 | begin
160 | // NOP
161 | end;
162 |
163 | procedure TLeakCheckReporter.NoLeaks;
164 | begin
165 | // NOP
166 | end;
167 |
168 | procedure TLeakCheckReporter.ShowMessage;
169 | begin
170 | // NOP
171 | end;
172 |
173 | procedure TLeakCheckReporter.WriteGraph(const Graph: string);
174 | begin
175 | // NOP
176 | end;
177 |
178 | procedure TLeakCheckReporter.WritelnLog(Log: MarshaledAString);
179 | begin
180 | // NOP
181 | end;
182 |
183 | initialization
184 | GetReport := SaveReport;
185 |
186 | end.
187 |
--------------------------------------------------------------------------------
/Source/LeakCheck.Report.pas:
--------------------------------------------------------------------------------
1 | {***************************************************************************}
2 | { }
3 | { LeakCheck for Delphi }
4 | { }
5 | { Copyright (c) 2015 Honza Rames }
6 | { }
7 | { https://bitbucket.org/shadow_cs/delphi-leakcheck }
8 | { }
9 | {***************************************************************************}
10 | { }
11 | { Licensed under the Apache License, Version 2.0 (the "License"); }
12 | { you may not use this file except in compliance with the License. }
13 | { You may obtain a copy of the License at }
14 | { }
15 | { http://www.apache.org/licenses/LICENSE-2.0 }
16 | { }
17 | { Unless required by applicable law or agreed to in writing, software }
18 | { distributed under the License is distributed on an "AS IS" BASIS, }
19 | { WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. }
20 | { See the License for the specific language governing permissions and }
21 | { limitations under the License. }
22 | { }
23 | {***************************************************************************}
24 |
25 | unit LeakCheck.Report;
26 |
27 | interface
28 |
29 | {$I LeakCheck.inc}
30 |
31 | uses
32 | LeakCheck;
33 |
34 | var
35 | ///
36 | /// Some other unit must set this callback. It will be called just before
37 | /// LeakCheck finalizes but unlike this unit, it may use some higher level
38 | /// functionality. This hacks the way how unit initialization and
39 | /// finalization works so use with care.
40 | ///
41 | GetReport: procedure(const Snapshot: TLeakCheck.TSnapshot);
42 |
43 | implementation
44 |
45 | var
46 | Snapshot: TLeakCheck.TSnapshot;
47 |
48 | initialization
49 | // Create a Snapshot to hide some accidental allocations made by internal
50 | // units.
51 | Snapshot.Create;
52 |
53 | finalization
54 | // Run high level stuff from other unit after all other units have finalized
55 | // (dangerous if you don't know what you're doing).
56 | if Assigned(GetReport) then
57 | GetReport(Snapshot);
58 | end.
59 |
--------------------------------------------------------------------------------
/Source/LeakCheck.Setup.JclTrace.pas:
--------------------------------------------------------------------------------
1 | {***************************************************************************}
2 | { }
3 | { LeakCheck for Delphi }
4 | { }
5 | { Copyright (c) 2015 Honza Rames }
6 | { }
7 | { https://bitbucket.org/shadow_cs/delphi-leakcheck }
8 | { }
9 | {***************************************************************************}
10 | { }
11 | { Licensed under the Apache License, Version 2.0 (the "License"); }
12 | { you may not use this file except in compliance with the License. }
13 | { You may obtain a copy of the License at }
14 | { }
15 | { http://www.apache.org/licenses/LICENSE-2.0 }
16 | { }
17 | { Unless required by applicable law or agreed to in writing, software }
18 | { distributed under the License is distributed on an "AS IS" BASIS, }
19 | { WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. }
20 | { See the License for the specific language governing permissions and }
21 | { limitations under the License. }
22 | { }
23 | {***************************************************************************}
24 |
25 | ///
26 | /// Unit that if placed as first unit in the project will initialize
27 | /// LeakCheck memory manager together with the JCL raw stack tracer and trace
28 | /// formatter. This configuration has JCL external dependency.
29 | ///
30 | unit LeakCheck.Setup.JclTrace;
31 |
32 | {$I LeakCheck.inc}
33 |
34 | interface
35 |
36 | uses
37 | LeakCheck,
38 | LeakCheck.Utils;
39 |
40 | implementation
41 |
42 | uses
43 | LeakCheck.Trace.Jcl;
44 |
45 | initialization
46 | {$IFDEF CPUX64}
47 | TLeakCheck.GetStackTraceProc := JclFramesStackTrace;
48 | {$ELSE}
49 | TLeakCheck.GetStackTraceProc := JclRawStackTrace;
50 | {$ENDIF}
51 | TLeakCheck.GetStackTraceFormatterProc := JclStackTraceFormatter;
52 |
53 | end.
54 |
--------------------------------------------------------------------------------
/Source/LeakCheck.Setup.Trace.pas:
--------------------------------------------------------------------------------
1 | {***************************************************************************}
2 | { }
3 | { LeakCheck for Delphi }
4 | { }
5 | { Copyright (c) 2015 Honza Rames }
6 | { }
7 | { https://bitbucket.org/shadow_cs/delphi-leakcheck }
8 | { }
9 | {***************************************************************************}
10 | { }
11 | { Licensed under the Apache License, Version 2.0 (the "License"); }
12 | { you may not use this file except in compliance with the License. }
13 | { You may obtain a copy of the License at }
14 | { }
15 | { http://www.apache.org/licenses/LICENSE-2.0 }
16 | { }
17 | { Unless required by applicable law or agreed to in writing, software }
18 | { distributed under the License is distributed on an "AS IS" BASIS, }
19 | { WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. }
20 | { See the License for the specific language governing permissions and }
21 | { limitations under the License. }
22 | { }
23 | {***************************************************************************}
24 |
25 | ///
26 | /// Unit that if placed as first unit in the project will initialize
27 | /// LeakCheck memory manager together with the WinApi stack tracer and MAP
28 | /// file based trace formatter on Windows or Backtrace and Posix.Proc trace
29 | /// formatter on Posix. This configuration has no external dependencies.
30 | ///
31 | unit LeakCheck.Setup.Trace;
32 |
33 | {$I LeakCheck.inc}
34 |
35 | interface
36 |
37 | uses
38 | LeakCheck,
39 | LeakCheck.Utils;
40 |
41 | implementation
42 |
43 | {$IFDEF MSWINDOWS}
44 | uses
45 | LeakCheck.Trace.WinApi,
46 | LeakCheck.Trace.Map;
47 |
48 | initialization
49 | TLeakCheck.GetStackTraceProc := WinApiStackTrace;
50 | TLeakCheck.GetStackTraceFormatterProc := MapStackTraceFormatter;
51 | {$ENDIF}
52 |
53 | {$IFDEF POSIX}
54 | uses
55 | LeakCheck.Trace.Backtrace;
56 |
57 | initialization
58 | TLeakCheck.GetStackTraceProc := BacktraceStackTrace;
59 | TLeakCheck.GetStackTraceFormatterProc := PosixProcStackTraceFormatter;
60 | {$ENDIF}
61 |
62 | end.
63 |
--------------------------------------------------------------------------------
/Source/LeakCheck.Trace.Backtrace.pas:
--------------------------------------------------------------------------------
1 | {***************************************************************************}
2 | { }
3 | { LeakCheck for Delphi }
4 | { }
5 | { Copyright (c) 2015 Honza Rames }
6 | { }
7 | { https://bitbucket.org/shadow_cs/delphi-leakcheck }
8 | { }
9 | {***************************************************************************}
10 | { }
11 | { Licensed under the Apache License, Version 2.0 (the "License"); }
12 | { you may not use this file except in compliance with the License. }
13 | { You may obtain a copy of the License at }
14 | { }
15 | { http://www.apache.org/licenses/LICENSE-2.0 }
16 | { }
17 | { Unless required by applicable law or agreed to in writing, software }
18 | { distributed under the License is distributed on an "AS IS" BASIS, }
19 | { WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. }
20 | { See the License for the specific language governing permissions and }
21 | { limitations under the License. }
22 | { }
23 | {***************************************************************************}
24 |
25 | unit LeakCheck.Trace.Backtrace;
26 |
27 | {$I LeakCheck.inc}
28 |
29 | interface
30 |
31 | uses
32 | LeakCheck,
33 | Posix.Backtrace,
34 | Posix.Proc;
35 |
36 | ///
37 | /// Currently the only POSIX (Android) implementation, does not support frame
38 | /// skipping.
39 | ///
40 | function BacktraceStackTrace(IgnoredFrames: Integer; Data: PPointer;
41 | Size: Integer): Integer;
42 |
43 | ///
44 | /// Formats stack trace addresses so they can be fed to addr2line
45 | /// utility.
46 | ///
47 | function PosixProcStackTraceFormatter: TLeakCheck.IStackTraceFormatter;
48 |
49 | implementation
50 |
51 | uses
52 | System.SysUtils,
53 | System.Math;
54 |
55 | function BacktraceStackTrace(IgnoredFrames: Integer; Data: PPointer;
56 | Size: Integer): Integer;
57 | begin
58 | Result := backtrace(Data, Size);
59 | end;
60 |
61 | type
62 | TPosixProcStackTraceFormatter = class(TInterfacedObject, TLeakCheck.IStackTraceFormatter)
63 | private
64 | FProcEntries: TPosixProcEntryList;
65 | public
66 | constructor Create;
67 | destructor Destroy; override;
68 |
69 | function FormatLine(Addr: Pointer; const Buffer: MarshaledAString;
70 | Size: Integer): Integer;
71 | end;
72 |
73 | { TPosixProcStackTraceFormatter }
74 |
75 | constructor TPosixProcStackTraceFormatter.Create;
76 | begin
77 | inherited Create;
78 | FProcEntries := TPosixProcEntryList.Create;
79 | FProcEntries.LoadFromCurrentProcess;
80 | end;
81 |
82 | destructor TPosixProcStackTraceFormatter.Destroy;
83 | begin
84 | FProcEntries.Free;
85 | inherited;
86 | end;
87 |
88 | function TPosixProcStackTraceFormatter.FormatLine(Addr: Pointer;
89 | const Buffer: MarshaledAString; Size: Integer): Integer;
90 | var
91 | s: string;
92 | M: TMarshaller;
93 | P: MarshaledAString;
94 | begin
95 | TLeakCheck.BeginIgnore;
96 | try
97 | s := FProcEntries.GetStackLine(NativeUInt(Addr));
98 | Result := Min(Length(s), Size - 1);
99 | if Result > 0 then
100 | begin
101 | P:=M.AsAnsi(s).ToPointer;
102 | Move(P^, Buffer^, Result + 1); // Add trailing zero
103 | end;
104 | finally
105 | TLeakCheck.EndIgnore;
106 | end;
107 | end;
108 |
109 | function PosixProcStackTraceFormatter: TLeakCheck.IStackTraceFormatter;
110 | begin
111 | Result := TPosixProcStackTraceFormatter.Create;
112 | end;
113 |
114 | end.
115 |
--------------------------------------------------------------------------------
/Source/LeakCheck.Trace.DbgHelp.pas:
--------------------------------------------------------------------------------
1 | {***************************************************************************}
2 | { }
3 | { LeakCheck for Delphi }
4 | { }
5 | { Copyright (c) 2015 Honza Rames }
6 | { }
7 | { https://bitbucket.org/shadow_cs/delphi-leakcheck }
8 | { }
9 | {***************************************************************************}
10 | { }
11 | { Licensed under the Apache License, Version 2.0 (the "License"); }
12 | { you may not use this file except in compliance with the License. }
13 | { You may obtain a copy of the License at }
14 | { }
15 | { http://www.apache.org/licenses/LICENSE-2.0 }
16 | { }
17 | { Unless required by applicable law or agreed to in writing, software }
18 | { distributed under the License is distributed on an "AS IS" BASIS, }
19 | { WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. }
20 | { See the License for the specific language governing permissions and }
21 | { limitations under the License. }
22 | { }
23 | {***************************************************************************}
24 |
25 | unit LeakCheck.Trace.DbgHelp;
26 |
27 | {$I LeakCheck.inc}
28 |
29 | interface
30 |
31 | ///
32 | /// Native DbgHelp solution. Win 32 only. Similar to WinApi solution but
33 | /// works without Win32 support if DbgHelp redistributable is installed. Does
34 | /// not use global caches.
35 | ///
36 | function DbgHelpStackTrace(IgnoredFrames: Integer; Data: PPointer;
37 | Size: Integer): Integer;
38 |
39 | implementation
40 |
41 | uses
42 | Windows;
43 |
44 | {$ALIGN 4}
45 |
46 | type
47 | PKDHELP = ^KDHELP;
48 | KDHELP = record
49 | //
50 | // address of kernel thread object, as provided in the
51 | // WAIT_STATE_CHANGE packet.
52 | //
53 | Thread: DWORD;
54 | //
55 | // offset in thread object to pointer to the current callback frame
56 | // in kernel stack.
57 | //
58 | ThCallbackStack: DWORD;
59 | //
60 | // offsets to values in frame:
61 | //
62 | // address of next callback frame
63 | NextCallback: DWORD;
64 | // address of saved frame pointer (if applicable)
65 | FramePointer: DWORD;
66 | //
67 | // Address of the kernel function that calls out to user mode
68 | //
69 | KiCallUserMode: DWORD;
70 | //
71 | // Address of the user mode dispatcher function
72 | //
73 | KeUserCallbackDispatcher: DWORD;
74 | //
75 | // Lowest kernel mode address
76 | //
77 | SystemRangeStart: DWORD;
78 | //
79 | // offset in thread object to pointer to the current callback backing
80 | // store frame in kernel stack.
81 | //
82 | ThCallbackBStore: DWORD;
83 | Reserved: array[0..7] of DWORD;
84 | end;
85 | type
86 | ADDRESS_MODE = (AddrMode1616, AddrMode1632, AddrModeReal, AddrModeFlat);
87 |
88 | LPADDRESS = ^ADDRESS;
89 | ADDRESS = record
90 | Offset: DWORD;
91 | Segment: WORD;
92 | Mode: DWORD;
93 | end;
94 |
95 | LPSTACKFRAME = ^STACKFRAME;
96 | STACKFRAME = record
97 | AddrPC: ADDRESS; // program counter
98 | AddrReturn: ADDRESS; // return address
99 | AddrFrame: ADDRESS; // frame pointer
100 | AddrStack: ADDRESS; // stack pointer
101 | FuncTableEntry: Pointer; // pointer to pdata/fpo or NULL
102 | Params: array[0..3] of DWORD; // possible arguments to the function
103 | bFar: LONGBOOL; // WOW far call
104 | bVirtual: LONGBOOL; // is this a virtual frame?
105 | Reserved: array[0..2] of DWORD;
106 | KdHelp: KDHELP;
107 | AddrBStore: ADDRESS; // backing store pointer
108 | end;
109 |
110 | PIMAGEHLP_LINE = ^IMAGEHLP_LINE;
111 | IMAGEHLP_LINE = record
112 | SizeOfStruct: DWORD; // set to sizeof(IMAGEHLP_LINE)
113 | Key: Pointer; // internal
114 | LineNumber: DWORD; // line number in file
115 | FileName: PChar; // full filename
116 | Address: DWORD; // first instruction of line
117 | end;
118 |
119 | PREAD_PROCESS_MEMORY_ROUTINE = function(hProcess: THandle; lpBaseAddress: DWORD;
120 | lpBuffer: Pointer; nSize: DWORD; var lpNumberOfBytesRead: DWORD): Boolean; stdcall;
121 |
122 | PFUNCTION_TABLE_ACCESS_ROUTINE = function(hProcess: THandle; AddrBase: DWORD): Pointer; stdcall;
123 |
124 | PGET_MODULE_BASE_ROUTINE = function(hProcess: THandle; Address: DWORD): DWORD; stdcall;
125 |
126 | PTRANSLATE_ADDRESS_ROUTINE = function(hProcess, hThread: THandle; lpaddr: LPADDRESS): DWORD; stdcall;
127 |
128 | const
129 | SDbgHelpDll = 'dbghelp.dll';
130 |
131 | function StackWalk(MachineType: DWORD; hProcess, hThread: THandle;
132 | StackFrame: LPSTACKFRAME; ContextRecord: Pointer;
133 | ReadMemoryRoutine: PREAD_PROCESS_MEMORY_ROUTINE;
134 | FunctionTableAccessRoutine: PFUNCTION_TABLE_ACCESS_ROUTINE;
135 | GetModuleBaseRoutine: PGET_MODULE_BASE_ROUTINE;
136 | TranslateAddress: PTRANSLATE_ADDRESS_ROUTINE): Integer; stdcall; external SDbgHelpDll;
137 | function SymFunctionTableAccess(hProcess: THandle; AddrBase: DWORD): Pointer; stdcall; external SDbgHelpDll;
138 | function SymGetModuleBase(hProcess: THandle; Address: DWORD): DWORD; stdcall; external SDbgHelpDll;
139 |
140 | function GetEIP : DWORD; assembler;
141 | asm
142 | mov EAX, dword ptr [ESP]
143 | end;
144 |
145 | function GetESP : DWORD; assembler;
146 | asm
147 | mov EAX, ESP
148 | end;
149 |
150 | function GetEBP : DWORD; assembler;
151 | asm
152 | mov EAX, EBP
153 | end;
154 |
155 | procedure InitContext(var Context: TContext);
156 | begin
157 | FillChar(Context, sizeof(TContext), 0);
158 | Context.ContextFlags := CONTEXT_FULL;
159 |
160 | Context.Eip:=GetEIP;
161 | Context.Esp:=GetESP+4;
162 | Context.Ebp:=GetEBP;
163 | end;
164 |
165 | procedure Prepare(var Stack: STACKFRAME; const Context: TContext);
166 | begin
167 | FillChar(Stack, sizeof(STACKFRAME), 0);
168 | Stack.AddrPC.Offset := Context.Eip;
169 | Stack.AddrPC.Mode := DWORD(AddrModeFlat);
170 | Stack.AddrStack.Offset := Context.Esp;
171 | Stack.AddrStack.Mode := DWORD(AddrModeFlat);
172 | Stack.AddrFrame.Offset := Context.Ebp;
173 | Stack.AddrFrame.Mode := DWORD(AddrModeFlat);
174 | end;
175 |
176 | function GetNextFrame(var Stack: STACKFRAME; var Context: TContext): Pointer;
177 | begin
178 | if (StackWalk(IMAGE_FILE_MACHINE_I386, GetCurrentProcess(), GetCurrentThread(),
179 | @Stack, @Context, nil, SymFunctionTableAccess, SymGetModuleBase, nil) <> 0) then
180 | begin
181 | Result := Pointer(Stack.AddrPC.Offset);
182 | end
183 | else
184 | Result := nil;
185 | end;
186 |
187 | function DbgHelpStackTrace(IgnoredFrames: Integer; Data: PPointer;
188 | Size: Integer): Integer;
189 | var
190 | Context: TContext;
191 | Stack: STACKFRAME;
192 | i: Integer;
193 | Addr: Pointer;
194 | begin
195 | InitContext(Context);
196 | Prepare(Stack, Context);
197 | Result := 0;
198 |
199 | // First skip ignored frames
200 | for i := 0 to IgnoredFrames - 1 do
201 | begin
202 | Addr := GetNextFrame(Stack, Context);
203 | if not Assigned(Addr) then
204 | Exit;
205 | end;
206 |
207 | // Then record the trace
208 | for i := 0 to Size - 1 do
209 | begin
210 | Addr := GetNextFrame(Stack, Context);
211 | if not Assigned(Addr) then
212 | Exit;
213 | Inc(Result);
214 | Data^ := Addr;
215 | Inc(Data);
216 | end;
217 | end;
218 |
219 | end.
220 |
--------------------------------------------------------------------------------
/Source/LeakCheck.Trace.Jcl.pas:
--------------------------------------------------------------------------------
1 | {***************************************************************************}
2 | { }
3 | { LeakCheck for Delphi }
4 | { }
5 | { Copyright (c) 2015 Honza Rames }
6 | { }
7 | { https://bitbucket.org/shadow_cs/delphi-leakcheck }
8 | { }
9 | {***************************************************************************}
10 | { }
11 | { Licensed under the Apache License, Version 2.0 (the "License"); }
12 | { you may not use this file except in compliance with the License. }
13 | { You may obtain a copy of the License at }
14 | { }
15 | { http://www.apache.org/licenses/LICENSE-2.0 }
16 | { }
17 | { Unless required by applicable law or agreed to in writing, software }
18 | { distributed under the License is distributed on an "AS IS" BASIS, }
19 | { WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. }
20 | { See the License for the specific language governing permissions and }
21 | { limitations under the License. }
22 | { }
23 | {***************************************************************************}
24 |
25 | unit LeakCheck.Trace.Jcl;
26 |
27 | {$I LeakCheck.inc}
28 |
29 | interface
30 |
31 | {$I jcl.inc}
32 |
33 | uses
34 | LeakCheck, JclDebug;
35 |
36 | ///
37 | /// JCL solution performing frame-based scanning. Win 32 and Win 64 only.
38 | /// Reasonably safe and robust Win 32 implementation, identical to
39 | /// LeakCheck.Trace.WinApi on Win64. Uses global caches!
40 | ///
41 | function JclFramesStackTrace(IgnoredFrames: Integer; Data: PPointer;
42 | Size: Integer): Integer;
43 | {$IFDEF CPU32}
44 | ///
45 | /// JCL solution performing raw scanning (matches entire stack while scanning
46 | /// all valid addresses, may produce invalid calls but is also able to
47 | /// display more information than other techniques). Win 32 and Win 64 only.
48 | /// Identical to LeakCheck.Trace.WinApi on Win64. Uses global caches!
49 | ///
50 | function JclRawStackTrace(IgnoredFrames: Integer; Data: PPointer;
51 | Size: Integer): Integer;
52 | {$ENDIF}
53 |
54 | ///
55 | /// JCL implementation using different methods to obtain the formatted line
56 | /// (including debug symbols and MAP file). Uses global caches!
57 | ///
58 | function JclStackTraceFormatter: TLeakCheck.IStackTraceFormatter;
59 |
60 | implementation
61 |
62 | uses
63 | Math;
64 |
65 | function JclStackTrace(IgnoredFrames: Integer; Data: PPointer;
66 | Size: Integer; Raw: Boolean): Integer;
67 | var
68 | OldTracer: TLeakCheck.TGetStackTrace;
69 | StackList: TJclStackInfoList;
70 | i: Integer;
71 | begin
72 | // There are some allocations in the JCL tracer that would cause a cycle so
73 | // suspend allocation tracing during stack tracing. Also ignore JCL global
74 | // caches.
75 | OldTracer := TLeakCheck.GetStackTraceProc;
76 | TLeakCheck.GetStackTraceProc := nil;
77 | TLeakCheck.BeginIgnore;
78 | try
79 | // Use TJclStackInfoList directly (without calling JclCreateStackList) to
80 | // bypass JCL caches.
81 | StackList := TJclStackInfoList.Create(Raw, IgnoredFrames, nil);
82 | try
83 | Result := Min(StackList.Count, Size);
84 | for i := 0 to Result - 1 do
85 | begin
86 | Data^ := StackList[i].CallerAddr;
87 | Inc(Data);
88 | end;
89 | finally
90 | StackList.Free;
91 | end;
92 | finally
93 | TLeakCheck.EndIgnore;
94 | TLeakCheck.GetStackTraceProc := OldTracer;
95 | end;
96 | end;
97 |
98 | function JclFramesStackTrace(IgnoredFrames: Integer; Data: PPointer;
99 | Size: Integer): Integer;
100 | begin
101 | Result := JclStackTrace(IgnoredFrames + 3, Data, Size, False);
102 | end;
103 |
104 | {$IFDEF CPU32}
105 | function JclRawStackTrace(IgnoredFrames: Integer; Data: PPointer;
106 | Size: Integer): Integer;
107 | begin
108 | Result := JclStackTrace(IgnoredFrames + 5, Data, Size, True);
109 | end;
110 | {$ENDIF}
111 |
112 | type
113 | TJclStackTraceFormatter = class(TInterfacedObject, TLeakCheck.IStackTraceFormatter)
114 | public
115 | function FormatLine(Addr: Pointer; const Buffer: MarshaledAString;
116 | Size: Integer): Integer;
117 | end;
118 |
119 | { TJclStackTraceFormatter }
120 |
121 | function TJclStackTraceFormatter.FormatLine(Addr: Pointer;
122 | const Buffer: MarshaledAString; Size: Integer): Integer;
123 | var
124 | s: AnsiString;
125 | begin
126 | TLeakCheck.BeginIgnore;
127 | try
128 | s := AnsiString(GetLocationInfoStr(Addr, True, True, True, True));
129 | Result := Min(Length(s), Size - 1);
130 | if Result > 0 then
131 | Move(s[1], Buffer^, Result + 1); // Add trailing zero
132 | finally
133 | TLeakCheck.EndIgnore;
134 | end;
135 | end;
136 |
137 | function JclStackTraceFormatter: TLeakCheck.IStackTraceFormatter;
138 | var
139 | s: string;
140 | begin
141 | // Get some info to initialize global cache
142 | s := GetLocationInfoStr(
143 | {$IF (CompilerVersion >= 23.0)}ReturnAddress{$ELSE}Caller(0, True){$IFEND},
144 | True, True, True, True);
145 | s := '';
146 | Result := TJclStackTraceFormatter.Create;
147 | end;
148 |
149 | initialization
150 | finalization
151 | // JCL global caches are about to be cleared, no further tracing is possible
152 | if (Pointer(TLeakCheck.GetStackTraceProc) = @JclFramesStackTrace)
153 | {$IF SizeOf(Pointer) = 4}
154 | or (Pointer(TLeakCheck.GetStackTraceProc) = @JclRawStackTrace)
155 | {$IFEND}
156 | then
157 | begin
158 | TLeakCheck.GetStackTraceProc := nil;
159 | end;
160 | if Pointer(TLeakCheck.GetStackTraceFormatterProc) = @JclStackTraceFormatter then
161 | TLeakCheck.CleanupStackTraceFormatter;
162 | end.
163 |
--------------------------------------------------------------------------------
/Source/LeakCheck.Trace.Map.pas:
--------------------------------------------------------------------------------
1 | {***************************************************************************}
2 | { }
3 | { LeakCheck for Delphi }
4 | { }
5 | { Copyright (c) 2015 Honza Rames }
6 | { }
7 | { https://bitbucket.org/shadow_cs/delphi-leakcheck }
8 | { }
9 | {***************************************************************************}
10 | { }
11 | { Licensed under the Apache License, Version 2.0 (the "License"); }
12 | { you may not use this file except in compliance with the License. }
13 | { You may obtain a copy of the License at }
14 | { }
15 | { http://www.apache.org/licenses/LICENSE-2.0 }
16 | { }
17 | { Unless required by applicable law or agreed to in writing, software }
18 | { distributed under the License is distributed on an "AS IS" BASIS, }
19 | { WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. }
20 | { See the License for the specific language governing permissions and }
21 | { limitations under the License. }
22 | { }
23 | {***************************************************************************}
24 |
25 | unit LeakCheck.Trace.Map;
26 |
27 | {$I LeakCheck.inc}
28 |
29 | interface
30 |
31 | uses
32 | LeakCheck,
33 | LeakCheck.MapFile;
34 |
35 | ///
36 | /// Based on own implementation that does not use global caching and is safe
37 | /// to use after finalization. Supports function names and line numbers with
38 | /// displacement. Does not have any external dependencies.
39 | ///
40 | function MapStackTraceFormatter: TLeakCheck.IStackTraceFormatter;
41 |
42 | implementation
43 |
44 | uses
45 | SysUtils,
46 | Generics.Collections,
47 | Math,
48 | AnsiStrings,
49 | Windows;
50 |
51 | type
52 | TMapStackTraceFormatter = class(TInterfacedObject, TLeakCheck.IStackTraceFormatter)
53 | private type
54 | TMAPCache = TObjectDictionary;
55 | private
56 | FMaps: TMAPCache;
57 | function GetSymobls(Addr: Pointer; var ModuleName: string): TMAPCollection;
58 | public
59 | constructor Create;
60 | destructor Destroy; override;
61 |
62 | function FormatLine(Addr: Pointer; const Buffer: MarshaledAString;
63 | Size: Integer): Integer;
64 | end;
65 |
66 | { TMapStackTraceFormatter }
67 |
68 | constructor TMapStackTraceFormatter.Create;
69 | begin
70 | inherited Create;
71 | FMaps := TMAPCache.Create([doOwnsValues]);
72 | end;
73 |
74 | destructor TMapStackTraceFormatter.Destroy;
75 | begin
76 | FMaps.Free;
77 | inherited;
78 | end;
79 |
80 | function TMapStackTraceFormatter.FormatLine(Addr: Pointer;
81 | const Buffer: MarshaledAString; Size: Integer): Integer;
82 | var
83 | OldTracer: TLeakCheck.TGetStackTrace;
84 | ModuleName: string;
85 | s: string;
86 | ansi: AnsiString;
87 | Map: TMAPCollection;
88 | Displacement: NativeUInt;
89 | i, j: Integer;
90 | begin
91 | OldTracer := TLeakCheck.GetStackTraceProc;
92 | TLeakCheck.GetStackTraceProc := nil;
93 | TLeakCheck.BeginIgnore;
94 | try
95 | Map := GetSymobls(Addr, ModuleName);
96 | s := '$' + IntToHex(NativeUInt(Addr), SizeOf(Pointer) * 2) + ' - ' +
97 | ExtractFileName(ModuleName);
98 | if Assigned(Map) then
99 | begin
100 | // Format line
101 | s:=s + ' - ' + Map.GetNearestSymbol(NativeUInt(Addr), Displacement);
102 | s:=s + ' + $' + IntToHex(Displacement, 0);
103 | if (Map.HasLines and (Displacement < $40000)) then
104 | begin
105 | // Get the beginning line of the function
106 | i := Map.GetNearestLine(NativeUInt(Addr) - Displacement, Displacement);
107 | // Should be 0 but in case we don't have debug DCUs ot there
108 | // are no symbols for this particular unit, it will be bigger
109 | if (Displacement <= $2000) then
110 | begin
111 | j := Map.GetNearestLine(NativeUInt(Addr), Displacement);
112 | s := s + ' (' +IntToStr(j);
113 | if (Displacement <> 0) then
114 | s := s + ' + $' + IntToHex(Displacement, 0);
115 | s := s + ' +' + IntToStr(j - i) + ')';
116 | end;
117 | end;
118 | // else most likely way off code
119 | end;
120 |
121 | Result := Min(Length(s), Size - 1);
122 | if Result > 0 then
123 | begin
124 | ansi := AnsiString(s);
125 | Move(ansi[1], Buffer^, Result + 1); // Add trailing zero
126 | end;
127 | finally
128 | TLeakCheck.EndIgnore;
129 | TLeakCheck.GetStackTraceProc := OldTracer;
130 | end;
131 | end;
132 |
133 | function TMapStackTraceFormatter.GetSymobls(Addr: Pointer;
134 | var ModuleName: string): TMAPCollection;
135 | var
136 | MapName: string;
137 | mbi: MEMORY_BASIC_INFORMATION;
138 | begin
139 | VirtualQuery(Addr, mbi, sizeof(mbi));
140 | SetLength(ModuleName, MAX_PATH+1);
141 | SetLength(ModuleName, GetModuleFileName(Cardinal(mbi.AllocationBase),
142 | PChar(ModuleName), MAX_PATH));
143 | if not FMaps.TryGetValue(ModuleName, Result) then
144 | begin
145 | MapName := ChangeFileExt(ModuleName, '.map');
146 | if (FileExists(MapName)) then begin
147 | Result:=TMAPCollection.Create;
148 | try
149 | Result.CodeBase:=NativeUInt(mbi.AllocationBase);
150 | Result.LoadFromFile(MapName);
151 | except
152 | Result.Free;
153 | raise;
154 | end;
155 | end
156 | else
157 | Result := nil;
158 | FMaps.Add(ModuleName, Result);
159 | end;
160 | end;
161 |
162 | function MapStackTraceFormatter: TLeakCheck.IStackTraceFormatter;
163 | begin
164 | Result := TMapStackTraceFormatter.Create;
165 | end;
166 |
167 | end.
168 |
--------------------------------------------------------------------------------
/Source/LeakCheck.Trace.WinApi.pas:
--------------------------------------------------------------------------------
1 | {***************************************************************************}
2 | { }
3 | { LeakCheck for Delphi }
4 | { }
5 | { Copyright (c) 2015 Honza Rames }
6 | { }
7 | { https://bitbucket.org/shadow_cs/delphi-leakcheck }
8 | { }
9 | {***************************************************************************}
10 | { }
11 | { Licensed under the Apache License, Version 2.0 (the "License"); }
12 | { you may not use this file except in compliance with the License. }
13 | { You may obtain a copy of the License at }
14 | { }
15 | { http://www.apache.org/licenses/LICENSE-2.0 }
16 | { }
17 | { Unless required by applicable law or agreed to in writing, software }
18 | { distributed under the License is distributed on an "AS IS" BASIS, }
19 | { WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. }
20 | { See the License for the specific language governing permissions and }
21 | { limitations under the License. }
22 | { }
23 | {***************************************************************************}
24 |
25 | unit LeakCheck.Trace.WinApi;
26 |
27 | {$I LeakCheck.inc}
28 |
29 | interface
30 |
31 | ///
32 | /// Native Windows API solution. Performs basic scanning and may omit some
33 | /// stack frames. Win32 and Win64 only. Best solution for Win64. Does not use
34 | /// global caches.
35 | ///
36 | function WinApiStackTrace(IgnoredFrames: Integer; Data: PPointer;
37 | Size: Integer): Integer;
38 |
39 | implementation
40 |
41 | uses
42 | Windows,
43 | SysUtils;
44 |
45 | function RtlCaptureStackBackTrace(FramesToSkip: ULONG; FramesToCapture: ULONG;
46 | BackTrace: PPointer; BackTraceHash : PULONG = nil): USHORT;
47 | stdcall; external 'kernel32.dll';
48 |
49 | var
50 | // Current windows version is XP or older (RtlCaptureStackBackTrace is not
51 | // suppored on older version than XP according to MSDN, it may be supported
52 | // on Windows 2000 but the documentation has been stripped off already)
53 | WinXPDown: Boolean = False;
54 |
55 | function WinApiStackTrace(IgnoredFrames: Integer; Data: PPointer;
56 | Size: Integer): Integer;
57 | begin
58 | // Implicitly ignore current frame
59 | Inc(IgnoredFrames);
60 | if (WinXPDown) then
61 | begin
62 | // Windows XP only supports IgnoredFrames + Size < 63
63 | // https://msdn.microsoft.com/en-us/library/windows/desktop/bb204633(v=vs.85).aspx
64 | if IgnoredFrames + Size >= 63 then
65 | Size := 62 - IgnoredFrames;
66 | end;
67 |
68 | Result := RtlCaptureStackBackTrace(IgnoredFrames, Size, Data);
69 | end;
70 |
71 | initialization
72 | WinXPDown := not CheckWin32Version(6);
73 |
74 | end.
75 |
--------------------------------------------------------------------------------
/Source/LeakCheck.Trace.pas:
--------------------------------------------------------------------------------
1 | {***************************************************************************}
2 | { }
3 | { LeakCheck for Delphi }
4 | { }
5 | { Copyright (c) 2015 Honza Rames }
6 | { }
7 | { https://bitbucket.org/shadow_cs/delphi-leakcheck }
8 | { }
9 | {***************************************************************************}
10 | { }
11 | { Licensed under the Apache License, Version 2.0 (the "License"); }
12 | { you may not use this file except in compliance with the License. }
13 | { You may obtain a copy of the License at }
14 | { }
15 | { http://www.apache.org/licenses/LICENSE-2.0 }
16 | { }
17 | { Unless required by applicable law or agreed to in writing, software }
18 | { distributed under the License is distributed on an "AS IS" BASIS, }
19 | { WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. }
20 | { See the License for the specific language governing permissions and }
21 | { limitations under the License. }
22 | { }
23 | {***************************************************************************}
24 |
25 | unit LeakCheck.Trace;
26 |
27 | {$I LeakCheck.inc}
28 |
29 | interface
30 |
31 | procedure Trace(const Msg: string);
32 |
33 | implementation
34 | uses
35 | {$IFDEF MSWINDOWS}
36 | Windows,
37 | {$ENDIF}
38 | {$IFDEF POSIX}
39 | Posix.Unistd,
40 | SysUtils,
41 | {$ENDIF}
42 | {$IFDEF ANDROID}
43 | Androidapi.Log,
44 | {$ENDIF}
45 | LeakCheck;
46 |
47 | {$IF CompilerVersion >= 25} // >= XE4
48 | {$LEGACYIFEND ON}
49 | {$IFEND}
50 |
51 | {$IFDEF LEAKCHECK_TRACE_FILE}
52 | var
53 | GOutput: TextFile;
54 | {$ENDIF}
55 |
56 | procedure Trace(const Msg: string);
57 | {$IF Defined(LEAKCHECK_TRACE_FILE)}
58 | begin
59 | if TTextRec(GOutput).Handle = 0 then
60 | begin
61 | Assign(GOutput, 'LeakCheck.trace');
62 | Rewrite(GOutput);
63 | end;
64 | Writeln(GOutput, Msg);
65 | end;
66 | {$ELSEIF Defined(ANDROID)}
67 | const
68 | TAG: MarshaledAString = MarshaledAString('LeakCheck');
69 | var
70 | m: TMarshaller;
71 | begin
72 | __android_log_write(ANDROID_LOG_WARN, TAG, m.AsAnsi(Msg).ToPointer);
73 | // Do not sleep here, it causes slowdown and we don't mind (much) if we loose
74 | // some messages.
75 | // usleep(1 * 1000);
76 | end;
77 | {$ELSEIF Defined(MSWINDOWS)}
78 | begin
79 | OutputDebugString(PChar(Msg));
80 | end;
81 | {$ELSEIF Defined(POSIX)}
82 | begin
83 | WriteLn(ErrOutput, Msg);
84 | end;
85 | {$ELSE}
86 | {$MESSAGE FATAL 'Unsupported platform'}
87 | {$IFEND}
88 |
89 | {$IFDEF LEAKCHECK_TRACE_FILE}
90 | initialization
91 | finalization
92 | if TTextRec(GOutput).Handle <> 0 then
93 | Flush(GOutput);
94 | // Do not close the file in case it is needed by other units' finalization
95 | // we'll leak some memory and handles but since this is debugging function,
96 | // this is not an issue - it will be released by the system momentarily.
97 | {$ENDIF}
98 |
99 | end.
100 |
--------------------------------------------------------------------------------
/Source/LeakCheck.Types.inc:
--------------------------------------------------------------------------------
1 | {***************************************************************************}
2 | { }
3 | { LeakCheck for Delphi }
4 | { }
5 | { Copyright (c) 2015 Honza Rames }
6 | { }
7 | { https://bitbucket.org/shadow_cs/delphi-leakcheck }
8 | { }
9 | {***************************************************************************}
10 | { }
11 | { Licensed under the Apache License, Version 2.0 (the "License"); }
12 | { you may not use this file except in compliance with the License. }
13 | { You may obtain a copy of the License at }
14 | { }
15 | { http://www.apache.org/licenses/LICENSE-2.0 }
16 | { }
17 | { Unless required by applicable law or agreed to in writing, software }
18 | { distributed under the License is distributed on an "AS IS" BASIS, }
19 | { WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. }
20 | { See the License for the specific language governing permissions and }
21 | { limitations under the License. }
22 | { }
23 | {***************************************************************************}
24 |
25 | type
26 | TFieldInfo = packed record
27 | TypeInfo: PPTypeInfo;
28 | case Integer of
29 | 0: ( Offset: Cardinal );
30 | 1: ( _Dummy: NativeUInt );
31 | end;
32 |
33 | PFieldTable = ^TFieldTable;
34 | TFieldTable = packed record
35 | X: Word;
36 | Size: Cardinal;
37 | Count: Cardinal;
38 | Fields: array [0..0] of TFieldInfo;
39 | end;
40 |
41 | PDynArrayRec = ^TDynArrayRec;
42 | TDynArrayRec = packed record
43 | {$IF SizeOf(Pointer) = 8}
44 | _Padding: Integer; // Make 16 byte align for payload..
45 | {$IFEND}
46 | RefCnt: Integer;
47 | Length: NativeInt;
48 | end;
49 |
50 | PNativeUInt = ^NativeUInt;
51 |
52 | PValueData = ^TValueData;
53 | PValue = ^TValue;
--------------------------------------------------------------------------------
/Source/LeakCheck.inc:
--------------------------------------------------------------------------------
1 | {***************************************************************************}
2 | { }
3 | { LeakCheck for Delphi }
4 | { }
5 | { Copyright (c) 2015 Honza Rames }
6 | { }
7 | { https://bitbucket.org/shadow_cs/delphi-leakcheck }
8 | { }
9 | {***************************************************************************}
10 | { }
11 | { Licensed under the Apache License, Version 2.0 (the "License"); }
12 | { you may not use this file except in compliance with the License. }
13 | { You may obtain a copy of the License at }
14 | { }
15 | { http://www.apache.org/licenses/LICENSE-2.0 }
16 | { }
17 | { Unless required by applicable law or agreed to in writing, software }
18 | { distributed under the License is distributed on an "AS IS" BASIS, }
19 | { WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. }
20 | { See the License for the specific language governing permissions and }
21 | { limitations under the License. }
22 | { }
23 | {***************************************************************************}
24 |
25 | {$IFNDEF LEAKCHECK_DEBUG}
26 | // Define so that users don't step through memory manager code if they do not
27 | // choose to. And also to make it a bit faster.
28 | {$DEBUGINFO OFF}
29 | {$OPTIMIZATION ON}
30 | {$INLINE ON}
31 | {$ENDIF}
32 |
33 | // Some internal types have smaller array boundaries.
34 | {$RANGECHECKS OFF}
35 | // Disable this as well to make the code faster and to ensure some internal
36 | // pointer arithmetics don't generate errors.
37 | {$OVERFLOWCHECKS OFF}
38 |
39 | {$IF CompilerVersion >= 25} // >= XE4
40 | {$LEGACYIFEND ON}
41 | {$IFEND}
42 |
43 | {$IF SizeOf(Pointer) = 4}
44 | {$ALIGN 4}
45 | {$ELSEIF SizeOf(Pointer) = 8}
46 | {$ALIGN 8}
47 | {$ELSE}
48 | {$MESSAGE FATAL 'Unsupported platform'}
49 | {$IFEND}
50 |
51 | {$MINENUMSIZE 1}
52 |
53 |
--------------------------------------------------------------------------------
/Test/.gitignore:
--------------------------------------------------------------------------------
1 | *.dsk
2 | *.~dsk
3 | *.identcache
4 | *.local
5 | *.tvsconfig
6 |
7 | AndroidManifest.template.xml
8 |
9 | XE*/Win32
10 | XE*/Win64
11 | XE*/Android
12 |
--------------------------------------------------------------------------------
/Test/LeakCheck.TestDUnit.pas:
--------------------------------------------------------------------------------
1 | {***************************************************************************}
2 | { }
3 | { LeakCheck for Delphi }
4 | { }
5 | { Copyright (c) 2015 Honza Rames }
6 | { }
7 | { https://bitbucket.org/shadow_cs/delphi-leakcheck }
8 | { }
9 | {***************************************************************************}
10 | { }
11 | { Licensed under the Apache License, Version 2.0 (the "License"); }
12 | { you may not use this file except in compliance with the License. }
13 | { You may obtain a copy of the License at }
14 | { }
15 | { http://www.apache.org/licenses/LICENSE-2.0 }
16 | { }
17 | { Unless required by applicable law or agreed to in writing, software }
18 | { distributed under the License is distributed on an "AS IS" BASIS, }
19 | { WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. }
20 | { See the License for the specific language governing permissions and }
21 | { limitations under the License. }
22 | { }
23 | {***************************************************************************}
24 |
25 | unit LeakCheck.TestDUnit;
26 |
27 | interface
28 |
29 | uses
30 | SysUtils,
31 | Rtti,
32 | TestFramework;
33 |
34 | type
35 | TLeakCheckConfiguration = record
36 | private
37 | {$I ..\..\Source\LeakCheck.Configuration.inc}
38 | end;
39 |
40 | TTestLeaks = class(TTestCase)
41 | published
42 | procedure TestNoLeaks;
43 | procedure TestWithLeaks;
44 | end;
45 |
46 | TTestSetup = class(TTestCase)
47 | protected
48 | FObj: Pointer;
49 | procedure SetUp; override;
50 | published
51 | procedure TestReleased;
52 | procedure TestNotReleased;
53 | end;
54 |
55 | TTestTeardown = class(TTestCase)
56 | protected
57 | FObj: Pointer;
58 | procedure TearDown; override;
59 | published
60 | procedure TestReleased;
61 | end;
62 |
63 | TTestTeardownThatLeaks = class(TTestCase)
64 | protected
65 | procedure TearDown; override;
66 | published
67 | procedure TestNotReleased;
68 | end;
69 |
70 | TTestStatusDoesNotLeak = class(TTestCase)
71 | published
72 | procedure TestStatus;
73 | end;
74 |
75 | TTestIgnoreTValue = class(TTestCase)
76 | published
77 | procedure TestCallsIgnoreForObject;
78 | procedure TestDoesNotCallIgnoreForNonObject;
79 | end;
80 |
81 | TTestIgnoreTMonitor = class(TTestCase)
82 | published
83 | procedure TestIgnoreTMonitor;
84 | end;
85 |
86 | TTestCorruptionDetection = class(TTestCase)
87 | private
88 | procedure CheckAV(Offset: NativeInt);
89 | published
90 | procedure TestFooter;
91 | procedure TestSeparator;
92 | end;
93 |
94 | implementation
95 |
96 | uses
97 | LeakCheck,
98 | LeakCheck.Utils;
99 |
100 | var
101 | KnownLeaks: TArray;
102 |
103 | type
104 | TTestCaseHelper = class helper for TTestCase
105 | procedure AddKnownLeak(Leak: Pointer);
106 | end;
107 |
108 | procedure TTestCaseHelper.AddKnownLeak(Leak: Pointer);
109 | var
110 | Len: Integer;
111 | begin
112 | Status('This test will fail due to memory leak which is expected');
113 | TLeakCheck.BeginIgnore;
114 | Len := Length(KnownLeaks);
115 | SetLength(KnownLeaks, Len + 1);
116 | KnownLeaks[Len] := Leak;
117 | TLeakCheck.EndIgnore;
118 | end;
119 |
120 | { TTestLeaks }
121 |
122 | procedure TTestLeaks.TestNoLeaks;
123 | begin
124 | TObject.Create{$IFNDEF AUTOREFCOUNT}.Free{$ENDIF};
125 | Check(True);
126 | end;
127 |
128 | procedure TTestLeaks.TestWithLeaks;
129 | var
130 | O: Pointer;
131 | begin
132 | {$IFDEF AUTOREFCOUNT}
133 | O := nil;
134 | {$ENDIF}
135 | TObject(O) := TObject.Create;
136 | AddKnownLeak(O);
137 | Check(True);
138 | end;
139 |
140 | { TTestSetup }
141 |
142 | procedure TTestSetup.SetUp;
143 | begin
144 | inherited;
145 | TObject(FObj) := TObject.Create;
146 | end;
147 |
148 | procedure TTestSetup.TestNotReleased;
149 | begin
150 | AddKnownLeak(FObj);
151 | Check(True);
152 | end;
153 |
154 | procedure TTestSetup.TestReleased;
155 | begin
156 | TObject(FObj).Free;
157 | Check(True);
158 | end;
159 |
160 | { TTestTeardown }
161 |
162 | procedure TTestTeardown.TearDown;
163 | begin
164 | inherited;
165 | TObject(FObj).Free;
166 | end;
167 |
168 | procedure TTestTeardown.TestReleased;
169 | begin
170 | TObject(FObj) := TObject.Create;
171 | Check(True);
172 | end;
173 |
174 | { TTestTeardownThatLeaks }
175 |
176 | procedure TTestTeardownThatLeaks.TearDown;
177 | var
178 | O: Pointer;
179 | begin
180 | inherited;
181 | {$IFDEF AUTOREFCOUNT}
182 | O := nil;
183 | {$ENDIF}
184 | TObject(O) := TObject.Create;
185 | AddKnownLeak(O);
186 | end;
187 |
188 | procedure TTestTeardownThatLeaks.TestNotReleased;
189 | begin
190 | Check(True);
191 | end;
192 |
193 | procedure FinalizeLeaks;
194 | var
195 | O: Pointer;
196 | begin
197 | for O in KnownLeaks do
198 | {$IFNDEF AUTOREFCOUNT}
199 | TObject(O).Free;
200 | {$ELSE}
201 | TObject(O).__ObjRelease;
202 | {$ENDIF}
203 | Finalize(KnownLeaks);
204 | end;
205 |
206 | { TTestStatusDoesNotLeak }
207 |
208 | procedure TTestStatusDoesNotLeak.TestStatus;
209 | var
210 | s: string;
211 | begin
212 | s := 'This is a status test';
213 | UniqueString(s); // Make this a dynamic text
214 | Status(s);
215 | Check(True);
216 | end;
217 |
218 | type
219 | TValueDataImpl = class(TInterfacedObject, IValueData)
220 | private class var
221 | Dummy: Pointer;
222 | private
223 | FIsObject: Boolean;
224 | public
225 | function GetDataSize: Integer;
226 | procedure ExtractRawData(ABuffer: Pointer);
227 | procedure ExtractRawDataNoCopy(ABuffer: Pointer);
228 | function GetReferenceToRawData: Pointer;
229 | constructor Create(IsObject: Boolean);
230 | function QueryInterface(const IID: TGUID; out Obj): HRESULT; stdcall;
231 | end;
232 |
233 | { TValueDataImpl }
234 |
235 | constructor TValueDataImpl.Create(IsObject: Boolean);
236 | begin
237 | inherited Create;
238 | FIsObject := IsObject;
239 | end;
240 |
241 | procedure TValueDataImpl.ExtractRawData(ABuffer: Pointer);
242 | begin
243 |
244 | end;
245 |
246 | procedure TValueDataImpl.ExtractRawDataNoCopy(ABuffer: Pointer);
247 | begin
248 |
249 | end;
250 |
251 | function TValueDataImpl.GetDataSize: Integer;
252 | begin
253 | Result := 0;
254 | end;
255 |
256 | function TValueDataImpl.GetReferenceToRawData: Pointer;
257 | begin
258 | Result := @Dummy;
259 | end;
260 |
261 | function TValueDataImpl.QueryInterface(const IID: TGUID; out Obj): HRESULT;
262 | begin
263 | if FIsObject then
264 | Result := inherited
265 | else
266 | Result := 1;
267 | end;
268 |
269 | { TTestIgnoreTValue }
270 |
271 | procedure TTestIgnoreTValue.TestCallsIgnoreForObject;
272 | var
273 | Snapshot: TLeakCheck.TSnapshot;
274 | Value: Rtti.TValueData;
275 | begin
276 | Snapshot.Create;
277 | Value.FTypeInfo := TypeInfo(TObject);
278 | Value.FValueData := TValueDataImpl.Create(True);
279 | IgnoreTValue(@Value);
280 | CheckEquals(0, Snapshot.LeakSize);
281 | end;
282 |
283 | procedure TTestIgnoreTValue.TestDoesNotCallIgnoreForNonObject;
284 | var
285 | Snapshot: TLeakCheck.TSnapshot;
286 | Value: Rtti.TValueData;
287 | begin
288 | Snapshot.Create;
289 | Value.FValueData := TValueDataImpl.Create(False);
290 | IgnoreTValue(@Value);
291 | CheckEquals(TValueDataImpl.InstanceSize, Snapshot.LeakSize);
292 | end;
293 |
294 | { TTestIgnoreTMonitor }
295 |
296 | procedure TTestIgnoreTMonitor.TestIgnoreTMonitor;
297 | var
298 | Inst: TObject;
299 | begin
300 | Inst := TObject.Create;
301 | TMonitor.Enter(Inst);
302 | TMonitor.Exit(Inst);
303 | AddKnownLeak(Inst);
304 | RegisterExpectedMemoryLeak(Inst);
305 | IgnoreAllManagedFields(Inst, TObject);
306 | Pointer(Inst) := nil;
307 | Check(true);
308 | end;
309 |
310 | { TTestCorruptionDetection }
311 |
312 | procedure TTestCorruptionDetection.CheckAV(Offset: NativeInt);
313 | var
314 | Data: PByte;
315 | begin
316 | GetMem(Data, 1);
317 | (Data + Offset)^ := $AA;
318 | try
319 | FreeMem(Data);
320 | Check(False, 'EAccessViolation expected');
321 | except
322 | on EAccessViolation do
323 | Check(True);
324 | else Check(False, 'EAccessViolation expected, got different');
325 | end;
326 | end;
327 |
328 | procedure TTestCorruptionDetection.TestFooter;
329 | begin
330 | {$IF TLeakCheckConfiguration.FooterSize > 0}
331 | CheckAV(1);
332 | CheckAV(TLeakCheckConfiguration.FooterSize * SizeOf(Pointer));
333 | {$IFEND}
334 | end;
335 |
336 | procedure TTestCorruptionDetection.TestSeparator;
337 | begin
338 | {$IF TLeakCheckConfiguration.SeparatorSize > 0}
339 | CheckAV(-1);
340 | CheckAV(-TLeakCheckConfiguration.SeparatorSize * SizeOf(Pointer));
341 | {$IFEND}
342 | end;
343 |
344 | initialization
345 | RegisterTests([
346 | TTestLeaks.Suite,
347 | TTestSetup.Suite,
348 | TTestTeardown.Suite,
349 | TTestTeardownThatLeaks.Suite,
350 | TTestStatusDoesNotLeak.Suite,
351 | TTestIgnoreTValue.Suite,
352 | TTestIgnoreTMonitor.Suite,
353 | TTestCorruptionDetection.Suite
354 | ]);
355 |
356 | finalization
357 | FinalizeLeaks;
358 | end.
359 |
--------------------------------------------------------------------------------
/Test/LeakCheck.TestDUnitX.pas:
--------------------------------------------------------------------------------
1 | {***************************************************************************}
2 | { }
3 | { LeakCheck for Delphi }
4 | { }
5 | { Copyright (c) 2015 Honza Rames }
6 | { }
7 | { https://bitbucket.org/shadow_cs/delphi-leakcheck }
8 | { }
9 | {***************************************************************************}
10 | { }
11 | { Licensed under the Apache License, Version 2.0 (the "License"); }
12 | { you may not use this file except in compliance with the License. }
13 | { You may obtain a copy of the License at }
14 | { }
15 | { http://www.apache.org/licenses/LICENSE-2.0 }
16 | { }
17 | { Unless required by applicable law or agreed to in writing, software }
18 | { distributed under the License is distributed on an "AS IS" BASIS, }
19 | { WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. }
20 | { See the License for the specific language governing permissions and }
21 | { limitations under the License. }
22 | { }
23 | {***************************************************************************}
24 |
25 | unit LeakCheck.TestDUnitX;
26 |
27 | interface
28 |
29 | uses
30 | LeakCheck,
31 | SysUtils,
32 | DUnitX.TestFramework,
33 | DUnitX.Attributes;
34 |
35 | type
36 | {$M+}
37 | TTestLeaks = class
38 | published
39 | procedure TestNoLeaks;
40 | procedure TestWithLeaks;
41 | end;
42 |
43 | TTestSetup = class
44 | protected
45 | FObj: Pointer;
46 | public
47 | [SetUp]
48 | procedure SetUp;
49 | published
50 | procedure TestReleased;
51 | procedure TestNotReleased;
52 | end;
53 |
54 | TTestTeardown = class
55 | protected
56 | FObj: Pointer;
57 | public
58 | [TearDown]
59 | procedure TearDown;
60 | published
61 | procedure TestReleased;
62 | end;
63 |
64 | TTestTeardownThatLeaks = class
65 | public
66 | [TearDown]
67 | procedure TearDown;
68 | published
69 | procedure TestNotReleased;
70 | end;
71 |
72 | TTestExceptionDisablesLeakIgnoreBase = class
73 | public
74 | destructor Destroy; override;
75 |
76 | end;
77 |
78 | TTestExceptionDisablesLeakIgnore = class(TTestExceptionDisablesLeakIgnoreBase)
79 | published
80 | procedure Test;
81 | end;
82 |
83 | TTestSetUpExceptionDisablesLeakIgnore = class(TTestExceptionDisablesLeakIgnoreBase)
84 | public
85 | [SetUp]
86 | procedure SetUp;
87 | published
88 | procedure Test;
89 | end;
90 |
91 | TTestTearDownExceptionDisablesLeakIgnore = class(TTestExceptionDisablesLeakIgnoreBase)
92 | public
93 | [TearDown]
94 | procedure TearDown;
95 | published
96 | procedure Test;
97 | end;
98 |
99 | TTestStatusDoesNotLeak = class
100 | published
101 | procedure TestStatus;
102 | end;
103 |
104 | implementation
105 |
106 | uses
107 | LeakCheck.Utils,
108 | TypInfo,
109 | Rtti;
110 |
111 | {$ASSERTIONS ON}
112 |
113 | var
114 | KnownLeaks: TArray;
115 |
116 | procedure AddKnownLeak(Leak: Pointer);
117 | var
118 | Len: Integer;
119 | begin
120 | Len := Length(KnownLeaks);
121 | SetLength(KnownLeaks, Len + 1);
122 | KnownLeaks[Len] := Leak;
123 | IgnoreDynamicArray(KnownLeaks);
124 | end;
125 |
126 | { TTestLeaks }
127 |
128 | procedure TTestLeaks.TestNoLeaks;
129 | begin
130 | TObject.Create{$IFNDEF AUTOREFCOUNT}.Free{$ENDIF};
131 | Assert.Pass;
132 | end;
133 |
134 | procedure TTestLeaks.TestWithLeaks;
135 | var
136 | O: Pointer;
137 | begin
138 | {$IFDEF AUTOREFCOUNT}
139 | O := nil;
140 | {$ENDIF}
141 | TObject(O) := TObject.Create;
142 | AddKnownLeak(O);
143 | Assert.Pass;
144 | end;
145 |
146 | { TTestSetup }
147 |
148 | procedure TTestSetup.SetUp;
149 | begin
150 | inherited;
151 | TObject(FObj) := TObject.Create;
152 | end;
153 |
154 | procedure TTestSetup.TestNotReleased;
155 | begin
156 | AddKnownLeak(FObj);
157 | Assert.Pass;
158 | end;
159 |
160 | procedure TTestSetup.TestReleased;
161 | begin
162 | TObject(FObj).Free;
163 | Assert.Pass;
164 | end;
165 |
166 | procedure TTestTeardown.TearDown;
167 | begin
168 | inherited;
169 | TObject(FObj).Free;
170 | end;
171 |
172 | procedure TTestTeardown.TestReleased;
173 | begin
174 | TObject(FObj) := TObject.Create;
175 | Assert.Pass;
176 | end;
177 |
178 | { TTestTeardownThatLeaks }
179 |
180 | procedure TTestTeardownThatLeaks.TearDown;
181 | var
182 | O: Pointer;
183 | begin
184 | inherited;
185 | {$IFDEF AUTOREFCOUNT}
186 | O := nil;
187 | {$ENDIF}
188 | TObject(O) := TObject.Create;
189 | AddKnownLeak(O);
190 | end;
191 |
192 | procedure TTestTeardownThatLeaks.TestNotReleased;
193 | begin
194 | Assert.Pass;
195 | end;
196 |
197 | { TTestStatusDoesNotLeak }
198 |
199 | procedure TTestStatusDoesNotLeak.TestStatus;
200 | var
201 | s: string;
202 | begin
203 | s := 'This is a status test';
204 | UniqueString(s); // Make this a dynamic text
205 | with TDUnitX.CurrentRunner do
206 | Status(s);
207 | Assert.Pass;
208 | end;
209 |
210 | { TTestExceptionDisablesLeakIgnoreBase }
211 |
212 | destructor TTestExceptionDisablesLeakIgnoreBase.Destroy;
213 | var
214 | Snapshot: TLeakCheck.TSnapshot;
215 | s: string;
216 | begin
217 | Snapshot.Create;
218 | SetLength(s, 1);
219 | System.Assert(Snapshot.LeakSize > 0);
220 | s:='';
221 | inherited;
222 | end;
223 |
224 | { TTestExceptionDisablesLeakIgnore }
225 |
226 | procedure TTestExceptionDisablesLeakIgnore.Test;
227 | begin
228 | Abort;
229 | end;
230 |
231 | { TTestSetUpExceptionDisablesLeakIgnore }
232 |
233 | procedure TTestSetUpExceptionDisablesLeakIgnore.SetUp;
234 | begin
235 | Abort;
236 | end;
237 |
238 | procedure TTestSetUpExceptionDisablesLeakIgnore.Test;
239 | begin
240 |
241 | end;
242 |
243 | { TTestTearDownExceptionDisablesLeakIgnore }
244 |
245 | procedure TTestTearDownExceptionDisablesLeakIgnore.TearDown;
246 | begin
247 | Abort;
248 | end;
249 |
250 | procedure TTestTearDownExceptionDisablesLeakIgnore.Test;
251 | begin
252 |
253 | end;
254 |
255 | procedure FinalizeLeaks;
256 | var
257 | O: Pointer;
258 | begin
259 | for O in KnownLeaks do
260 | {$IFNDEF AUTOREFCOUNT}
261 | TObject(O).Free;
262 | {$ELSE}
263 | TObject(O).__ObjRelease;
264 | {$ENDIF}
265 | Finalize(KnownLeaks);
266 | end;
267 |
268 | initialization
269 | TDUnitX.RegisterTestFixture(TTestLeaks);
270 | TDUnitX.RegisterTestFixture(TTestSetup);
271 | TDUnitX.RegisterTestFixture(TTestTeardown);
272 | TDUnitX.RegisterTestFixture(TTestTeardownThatLeaks);
273 | TDUnitX.RegisterTestFixture(TTestExceptionDisablesLeakIgnore);
274 | TDUnitX.RegisterTestFixture(TTestSetUpExceptionDisablesLeakIgnore);
275 | TDUnitX.RegisterTestFixture(TTestTearDownExceptionDisablesLeakIgnore);
276 | TDUnitX.RegisterTestFixture(TTestStatusDoesNotLeak);
277 |
278 | finalization
279 | FinalizeLeaks;
280 |
281 | end.
282 |
--------------------------------------------------------------------------------
/Test/LeakCheck.TestForm.fmx:
--------------------------------------------------------------------------------
1 | object frmLeakCheckTest: TfrmLeakCheckTest
2 | Left = 0
3 | Top = 0
4 | Caption = 'Form1'
5 | ClientHeight = 480
6 | ClientWidth = 640
7 | FormFactor.Width = 320
8 | FormFactor.Height = 480
9 | FormFactor.Devices = [Desktop]
10 | DesignerMasterStyle = 0
11 | object lblText: TText
12 | Align = Center
13 | AutoSize = True
14 | Size.Width = 261.490875244140600000
15 | Size.Height = 26.601562500000000000
16 | Size.PlatformDefault = False
17 | Text = 'Testing form, will now close...'
18 | TextSettings.Font.Size = 20.000000000000000000
19 | TextSettings.WordWrap = False
20 | end
21 | object tmrClose: TTimer
22 | Interval = 500
23 | OnTimer = tmrCloseTimer
24 | Left = 24
25 | Top = 36
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/Test/LeakCheck.TestForm.pas:
--------------------------------------------------------------------------------
1 | {***************************************************************************}
2 | { }
3 | { LeakCheck for Delphi }
4 | { }
5 | { Copyright (c) 2015 Honza Rames }
6 | { }
7 | { https://bitbucket.org/shadow_cs/delphi-leakcheck }
8 | { }
9 | {***************************************************************************}
10 | { }
11 | { Licensed under the Apache License, Version 2.0 (the "License"); }
12 | { you may not use this file except in compliance with the License. }
13 | { You may obtain a copy of the License at }
14 | { }
15 | { http://www.apache.org/licenses/LICENSE-2.0 }
16 | { }
17 | { Unless required by applicable law or agreed to in writing, software }
18 | { distributed under the License is distributed on an "AS IS" BASIS, }
19 | { WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. }
20 | { See the License for the specific language governing permissions and }
21 | { limitations under the License. }
22 | { }
23 | {***************************************************************************}
24 |
25 | unit LeakCheck.TestForm;
26 |
27 | interface
28 |
29 | uses
30 | System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
31 | FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Objects;
32 |
33 | type
34 | TfrmLeakCheckTest = class(TForm)
35 | tmrClose: TTimer;
36 | lblText: TText;
37 | procedure tmrCloseTimer(Sender: TObject);
38 | private
39 | { Private declarations }
40 | public
41 | { Public declarations }
42 | end;
43 |
44 | var
45 | frmLeakCheckTest: TfrmLeakCheckTest;
46 |
47 | implementation
48 |
49 | {$R *.fmx}
50 |
51 | procedure TfrmLeakCheckTest.tmrCloseTimer(Sender: TObject);
52 | begin
53 | tmrClose.Enabled := False;
54 | Close;
55 | end;
56 |
57 | end.
58 |
--------------------------------------------------------------------------------
/Test/LeakCheck.TestUnit.pas:
--------------------------------------------------------------------------------
1 | {***************************************************************************}
2 | { }
3 | { LeakCheck for Delphi }
4 | { }
5 | { Copyright (c) 2015 Honza Rames }
6 | { }
7 | { https://bitbucket.org/shadow_cs/delphi-leakcheck }
8 | { }
9 | {***************************************************************************}
10 | { }
11 | { Licensed under the Apache License, Version 2.0 (the "License"); }
12 | { you may not use this file except in compliance with the License. }
13 | { You may obtain a copy of the License at }
14 | { }
15 | { http://www.apache.org/licenses/LICENSE-2.0 }
16 | { }
17 | { Unless required by applicable law or agreed to in writing, software }
18 | { distributed under the License is distributed on an "AS IS" BASIS, }
19 | { WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. }
20 | { See the License for the specific language governing permissions and }
21 | { limitations under the License. }
22 | { }
23 | {***************************************************************************}
24 |
25 | unit LeakCheck.TestUnit;
26 |
27 | {$ASSERTIONS ON}
28 |
29 | interface
30 |
31 | procedure RunTests;
32 |
33 | implementation
34 |
35 | uses LeakCheck;
36 |
37 | {$I ../Source/LeakCheck.Configuration.inc}
38 |
39 | var
40 | LeakSnapshot: Pointer;
41 |
42 | procedure TestFirstLast;
43 | var
44 | P: Pointer;
45 | L: TLeaks;
46 | begin
47 | L := TLeakCheck.GetLeaks(LeakSnapshot);
48 | Assert(L.Length = 0);
49 | GetMem(P, 16);
50 | L := TLeakCheck.GetLeaks(LeakSnapshot);
51 | Assert(L.Length = 1);
52 | Assert(L[0] = P);
53 | L.Free;
54 | FreeMem(P);
55 | L := TLeakCheck.GetLeaks(LeakSnapshot);
56 | Assert(L.Length = 0);
57 | end;
58 |
59 | procedure TestLast;
60 | var
61 | P1, P2, P3: Pointer;
62 | L: TLeaks;
63 | begin
64 | GetMem(P1, 16);
65 | GetMem(P2, 16);
66 | GetMem(P3, 16);
67 | FreeMem(P3);
68 | L := TLeakCheck.GetLeaks(LeakSnapshot);
69 | Assert(L.Length = 2);
70 | Assert(L[0] = P1);
71 | Assert(L[1] = P2);
72 | L.Free;
73 | FreeMem(P2);
74 | FreeMem(P1);
75 | end;
76 |
77 | procedure TestFirst;
78 | var
79 | P1, P2, P3: Pointer;
80 | L: TLeaks;
81 | begin
82 | GetMem(P1, 16);
83 | GetMem(P2, 16);
84 | GetMem(P3, 16);
85 | FreeMem(P1);
86 | L := TLeakCheck.GetLeaks(LeakSnapshot);
87 | Assert(L.Length = 2);
88 | Assert(L[0] = P2);
89 | Assert(L[1] = P3);
90 | L.Free;
91 | FreeMem(P2);
92 | FreeMem(P3);
93 | end;
94 |
95 | procedure TestMid;
96 | var
97 | P1, P2, P3: Pointer;
98 | L: TLeaks;
99 | begin
100 | GetMem(P1, 16);
101 | GetMem(P2, 16);
102 | GetMem(P3, 16);
103 | FreeMem(P2);
104 | L := TLeakCheck.GetLeaks(LeakSnapshot);
105 | Assert(L.Length = 2);
106 | Assert(L[0] = P1);
107 | Assert(L[1] = P3);
108 | L.Free;
109 | FreeMem(P1);
110 | FreeMem(P3);
111 | end;
112 |
113 | procedure TestReport;
114 | var
115 | P, PP: PByte;
116 | Leak: LeakString;
117 | i: Integer;
118 | {$IFNDEF NEXTGEN}
119 | s: AnsiString;
120 | {$ENDIF}
121 | us: UnicodeString;
122 | o: TObject;
123 | intf: IInterface;
124 | begin
125 | GetMem(P, 48);
126 | PP := P;
127 | for i := 0 to 48 - 1 do
128 | begin
129 | PP^ := i;
130 | Inc(PP);
131 | end;
132 | TLeakCheck.Report(LeakSnapshot, True);
133 | Leak := TLeakCheck.GetReport(LeakSnapshot);
134 | FreeMem(P);
135 | Assert(not Leak.IsEmpty);
136 | Leak.Free;
137 | TLeakCheck.Report(LeakSnapshot);
138 | Leak := TLeakCheck.GetReport(LeakSnapshot);
139 | Assert(Leak.IsEmpty);
140 | Leak.Free;
141 |
142 | {$IFNDEF NEXTGEN}
143 | s := 'ATest';
144 | UniqueString(s);
145 | TLeakCheck.Report(LeakSnapshot);
146 | s := '';
147 | Assert(TLeakCheck.GetReport(LeakSnapshot).IsEmpty);
148 | {$ENDIF}
149 |
150 | us := 'UTest';
151 | UniqueString(us);
152 | TLeakCheck.Report(LeakSnapshot);
153 | us := '';
154 | Assert(TLeakCheck.GetReport(LeakSnapshot).IsEmpty);
155 |
156 | o := TObject.Create;
157 | TLeakCheck.Report(LeakSnapshot);
158 | o.Free;
159 | Assert(TLeakCheck.GetReport(LeakSnapshot).IsEmpty);
160 |
161 | intf := TInterfacedObject.Create;
162 | TLeakCheck.Report(LeakSnapshot);
163 | intf := nil;
164 | Assert(TLeakCheck.GetReport(LeakSnapshot).IsEmpty);
165 | end;
166 |
167 | procedure TestIgnores;
168 | var
169 | o: Pointer;
170 | intf: IInterface;
171 | s: string;
172 | P: Pointer;
173 | L: TLeaks;
174 | begin
175 | o := TInterfacedObject.Create;
176 | intf := TInterfacedObject(o);
177 | s := 'Leak';
178 | UniqueString(s);
179 | P:=AllocMem(48);
180 | TLeakCheck.IgnoredLeakTypes := [tkUString, tkClass, tkUnknown];
181 | Assert(TLeakCheck.GetLeaks(LeakSnapshot).IsEmpty);
182 |
183 | TLeakCheck.IgnoredLeakTypes := [tkUString, tkClass];
184 | L := TLeakCheck.GetLeaks(LeakSnapshot);
185 | Assert(not L.IsEmpty);
186 | Assert(L[0] = P);
187 | L.Free;
188 |
189 | TLeakCheck.IgnoredLeakTypes := [tkUString, tkUnknown];
190 | L := TLeakCheck.GetLeaks(LeakSnapshot);
191 | Assert(not L.IsEmpty);
192 | Assert(L[0] = o);
193 | L.Free;
194 |
195 | TLeakCheck.IgnoredLeakTypes := [tkClass, tkUnknown];
196 | L := TLeakCheck.GetLeaks(LeakSnapshot);
197 | Assert(not L.IsEmpty);
198 | Assert(L[0] = Pointer(NativeUInt(Pointer(s)) - TLeakCheck.StringSkew));
199 | L.Free;
200 |
201 | FreeMem(P);
202 | TLeakCheck.IgnoredLeakTypes := [];
203 | end;
204 |
205 | type
206 | TTestFreedObject = class(TObject)
207 | protected
208 | procedure VirtualProc1; virtual;
209 | procedure VirtualProc2; virtual;
210 | procedure VirtualProc3; virtual;
211 | end;
212 |
213 | { TTestFreedObject }
214 |
215 | procedure TTestFreedObject.VirtualProc1;
216 | begin
217 | Assert(False);
218 | end;
219 |
220 | procedure TTestFreedObject.VirtualProc2;
221 | begin
222 | Assert(False);
223 | end;
224 |
225 | procedure TTestFreedObject.VirtualProc3;
226 | begin
227 | Assert(False);
228 | end;
229 |
230 | procedure TestObjectCleanup;
231 | var
232 | O: TObject;
233 | F: TTestFreedObject;
234 | P: Pointer;
235 | begin
236 | O := TObject.Create;
237 | P := O;
238 | O.Free;
239 | Assert(TLeakCheck.GetObjectClass(TObject(P)) = nil);
240 | Assert(TObject(P).ClassType.ClassParent = TLeakCheck.TFreedObject);
241 | Assert(TObject(P).ClassType.ClassName = 'TFreedObjectImpl');
242 | F := TTestFreedObject.Create;
243 | F.Destroy; //Try to use both Destroy and Free (see below)
244 | try
245 | F.VirtualProc1;
246 | Assert(false);
247 | except
248 | end;
249 | Pointer(F) := nil;
250 | // Allocate again to ensure we don't have the info block corrupted
251 | F := TTestFreedObject.Create;
252 | P := F;
253 | F.Free;
254 | try
255 | TTestFreedObject(P).Destroy;
256 | Assert(false);
257 | except
258 | end;
259 | try
260 | TTestFreedObject(P).VirtualProc3;
261 | Assert(false);
262 | except
263 | end;
264 | try
265 | TTestFreedObject(P).Destroy;
266 | Assert(false);
267 | except
268 | end;
269 | end;
270 |
271 | type
272 | ITestInterface = interface
273 | procedure VirtualProc1;
274 | procedure VirtualProc2;
275 | procedure NonVirtualProc1;
276 | procedure NonVirtualProc2;
277 | procedure StdProc; stdcall;
278 | end;
279 |
280 | TTestFreedIntfObject = class(TInterfacedObject, ITestInterface, IInterface)
281 | protected
282 | procedure VirtualProc1; virtual;
283 | procedure VirtualProc2; virtual;
284 | procedure NonVirtualProc1;
285 | procedure NonVirtualProc2;
286 | procedure StdProc; stdcall;
287 | end;
288 |
289 | { TTestFreedIntfObject }
290 |
291 | procedure TTestFreedIntfObject.NonVirtualProc1;
292 | begin
293 | Assert(False);
294 | end;
295 |
296 | procedure TTestFreedIntfObject.NonVirtualProc2;
297 | begin
298 | Assert(False);
299 | end;
300 |
301 | procedure TTestFreedIntfObject.StdProc;
302 | begin
303 | Assert(False);
304 | end;
305 |
306 | procedure TTestFreedIntfObject.VirtualProc1;
307 | begin
308 | Assert(False);
309 | end;
310 |
311 | procedure TTestFreedIntfObject.VirtualProc2;
312 | begin
313 | Assert(False);
314 | end;
315 |
316 | procedure TestInterfaceCleanup;
317 | var
318 | O: TTestFreedIntfObject;
319 | I: ITestInterface;
320 | begin
321 | O := TTestFreedIntfObject.Create;
322 | // O.VirtualProc2;
323 | // O.NonVirtualProc2;
324 | I := O;
325 | {$IFDEF AUTOREFCOUNT}
326 | O := nil;
327 | {$ENDIF}
328 | I._Release;
329 | try
330 | I._Release;
331 | Assert(False);
332 | except
333 | end;
334 | Pointer(I) := nil;
335 | I := TTestFreedIntfObject.Create;
336 | I._Release;
337 | try
338 | I.StdProc;
339 | Assert(False);
340 | except
341 | end;
342 | Pointer(I) := nil;
343 | I := TTestFreedIntfObject.Create;
344 | I._Release;
345 | try
346 | I.VirtualProc2;
347 | Assert(False);
348 | except
349 | end;
350 | Pointer(I) := nil;
351 | I := TTestFreedIntfObject.Create;
352 | I._Release;
353 | try
354 | I.NonVirtualProc2;
355 | Assert(False);
356 | except
357 | end;
358 | Pointer(I) := nil;
359 | end;
360 |
361 | procedure RunTests;
362 | begin
363 | LeakSnapshot := TLeakCheck.CreateSnapshot;
364 | TestFirstLast;
365 | TestLast;
366 | TestFirst;
367 | TestMid;
368 | TestReport;
369 | TestIgnores;
370 | {$IFNDEF LEAKCHECK_DEFER}
371 | {$IF EnableVirtualCallsOnFreedObjectInterception}
372 | TestObjectCleanup;
373 | {$IFEND}
374 | {$IF EnableInterfaceCallsOnFreedObjectInterception}
375 | TestInterfaceCleanup;
376 | {$IFEND}
377 | {$ENDIF}
378 | end;
379 |
380 | end.
381 |
--------------------------------------------------------------------------------
/Test/XE/TestGroup.groupproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | {1D710B2F-CD49-426A-A70B-95E1C15AF843}
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | Default.Personality.12
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/Test/XE/TestProject.dpr:
--------------------------------------------------------------------------------
1 | {***************************************************************************}
2 | { }
3 | { LeakCheck for Delphi }
4 | { }
5 | { Copyright (c) 2015 Honza Rames }
6 | { }
7 | { https://bitbucket.org/shadow_cs/delphi-leakcheck }
8 | { }
9 | {***************************************************************************}
10 | { }
11 | { Licensed under the Apache License, Version 2.0 (the "License"); }
12 | { you may not use this file except in compliance with the License. }
13 | { You may obtain a copy of the License at }
14 | { }
15 | { http://www.apache.org/licenses/LICENSE-2.0 }
16 | { }
17 | { Unless required by applicable law or agreed to in writing, software }
18 | { distributed under the License is distributed on an "AS IS" BASIS, }
19 | { WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. }
20 | { See the License for the specific language governing permissions and }
21 | { limitations under the License. }
22 | { }
23 | {***************************************************************************}
24 |
25 | program TestProject;
26 |
27 | uses
28 | {$IFDEF WIN32}
29 | // If used together with LeakCheck registering expected leaks may not bubble
30 | // to the internal system memory manager and thus may be reported to the user.
31 | // This behavior is due to FastMM not calling parent RegisterExpectedMemoryLeak
32 | // and is not LeakCheck issue. This is only exposed if LEAKCHECK_DEFER is
33 | // defined.
34 | {$IFDEF LEAKCHECK_DEFER}
35 | FastMM4,
36 | {$ENDIF}
37 | {$ENDIF }
38 | LeakCheck in '..\..\Source\LeakCheck.pas',
39 | LeakCheck.Utils in '..\..\Source\LeakCheck.Utils.pas',
40 | TestFramework in '..\..\External\DUnit\TestFramework.pas',
41 | TestInsight.DUnit,
42 | LeakCheck.TestUnit in '..\LeakCheck.TestUnit.pas',
43 | LeakCheck.TestDUnit in '..\LeakCheck.TestDUnit.pas',
44 | LeakCheck.DUnit in '..\..\Source\LeakCheck.DUnit.pas',
45 | LeakCheck.Collections in '..\..\Source\LeakCheck.Collections.pas',
46 | LeakCheck.Cycle in '..\..\Source\LeakCheck.Cycle.pas',
47 | LeakCheck.Cycle.Utils in '..\..\Source\LeakCheck.Cycle.Utils.pas',
48 | LeakCheck.DUnitCycle in '..\..\Source\LeakCheck.DUnitCycle.pas',
49 | {$IFDEF MSWINDOWS}
50 | LeakCheck.Trace.DbgHelp in '..\..\Source\LeakCheck.Trace.DbgHelp.pas',
51 | LeakCheck.Trace.WinApi in '..\..\Source\LeakCheck.Trace.WinApi.pas',
52 | LeakCheck.Trace.Jcl in '..\..\Source\LeakCheck.Trace.Jcl.pas',
53 | {$ENDIF}
54 | LeakCheck.TestCycle in '..\LeakCheck.TestCycle.pas';
55 |
56 | {$R *.res}
57 |
58 | begin
59 | // Simple test of functionality
60 | RunTests;
61 |
62 | ReportMemoryLeaksOnShutdown := True;
63 |
64 | //TLeakCheck.GetStackTraceProc := WinApiStackTrace;
65 | //TLeakCheck.GetStackTraceProc := DbgHelpStackTrace;
66 | TLeakCheck.GetStackTraceProc := JclRawStackTrace;
67 | //TLeakCheck.GetStackTraceProc := JclFramesStackTrace;
68 | TLeakCheck.GetStackTraceFormatterProc := JclStackTraceFormatter;
69 |
70 | // DUnit integration
71 | {$IFDEF WEAKREF}
72 | TLeakCheck.IgnoredLeakTypes := [tkUnknown];
73 | {$ENDIF}
74 | TLeakCheckCycleMonitor.UseExtendedRtti := True;
75 | MemLeakMonitorClass := TLeakCheckCycleGraphMonitor;
76 | RunRegisteredTests;
77 | end.
78 |
79 |
--------------------------------------------------------------------------------
/Test/XE/TestProject.dproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | {75899CB4-E3C4-4F01-B8EE-3C98C17A3369}
4 | TestProject.dpr
5 | 12.3
6 | True
7 | Debug
8 | Win32
9 | Console
10 | None
11 | DCC32
12 |
13 |
14 | true
15 |
16 |
17 | true
18 | Base
19 | true
20 |
21 |
22 | true
23 | Base
24 | true
25 |
26 |
27 | true
28 | true
29 | true
30 | $(JCL_DIR)\include;$(JCL_DIR)\common;$(JCL_DIR)\windows;$(DCC_UnitSearchPath)
31 | 3
32 | true
33 | LEAKCHECK_DEBUG;TESTINSIGHT;NO_MESSAGEBOX;$(DCC_Define)
34 | 00400000
35 | .\$(Platform)\$(Config)
36 | WinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;$(DCC_UnitAlias)
37 | .\$(Platform)\$(Config)
38 | false
39 | false
40 | false
41 | false
42 | false
43 |
44 |
45 | DEBUG;$(DCC_Define)
46 | false
47 | true
48 |
49 |
50 | false
51 | RELEASE;$(DCC_Define)
52 | 0
53 | false
54 |
55 |
56 |
57 | MainSource
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 | Cfg_2
77 | Base
78 |
79 |
80 | Base
81 |
82 |
83 | Cfg_1
84 | Base
85 |
86 |
87 |
88 |
89 |
90 | Delphi.Personality.12
91 |
92 |
93 |
94 |
95 | False
96 | False
97 | 1
98 | 0
99 | 0
100 | 0
101 | False
102 | False
103 | False
104 | False
105 | False
106 | 1029
107 | 1250
108 |
109 |
110 |
111 |
112 | 1.0.0.0
113 |
114 |
115 |
116 |
117 |
118 | 1.0.0.0
119 |
120 |
121 |
122 | TestProject.dpr
123 |
124 |
125 |
126 | True
127 |
128 |
129 | 12
130 |
131 |
132 |
--------------------------------------------------------------------------------
/Test/XE/TestProject.res:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shadow-cs/delphi-leakcheck/67cc1bdfb670242b5b15d5942174c0024d7117b2/Test/XE/TestProject.res
--------------------------------------------------------------------------------
/Test/XE/TestProjectX.dpr:
--------------------------------------------------------------------------------
1 | {***************************************************************************}
2 | { }
3 | { LeakCheck for Delphi }
4 | { }
5 | { Copyright (c) 2015 Honza Rames }
6 | { }
7 | { https://bitbucket.org/shadow_cs/delphi-leakcheck }
8 | { }
9 | {***************************************************************************}
10 | { }
11 | { Licensed under the Apache License, Version 2.0 (the "License"); }
12 | { you may not use this file except in compliance with the License. }
13 | { You may obtain a copy of the License at }
14 | { }
15 | { http://www.apache.org/licenses/LICENSE-2.0 }
16 | { }
17 | { Unless required by applicable law or agreed to in writing, software }
18 | { distributed under the License is distributed on an "AS IS" BASIS, }
19 | { WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. }
20 | { See the License for the specific language governing permissions and }
21 | { limitations under the License. }
22 | { }
23 | {***************************************************************************}
24 |
25 | program TestProjectX;
26 |
27 | // Make sure to have DUnitX in your global search path or point DUNITX_DIR
28 | // environmental variable to DUnitX base source directory.
29 |
30 | // Note that in order to run this project you have to have the updated DUnitX
31 | // framework that supports extended leak checking.
32 |
33 | uses
34 | {$IFDEF WIN32}
35 | FastMM4,
36 | {$ENDIF }
37 | LeakCheck in '..\..\Source\LeakCheck.pas',
38 | LeakCheck.Utils in '..\..\Source\LeakCheck.Utils.pas',
39 | DUnitX.TestFramework,
40 | DUnitX.IoC,
41 | TestInsight.DUnitX,
42 | LeakCheck.TestDUnitX in '..\LeakCheck.TestDUnitX.pas',
43 | LeakCheck.Collections in '..\..\Source\LeakCheck.Collections.pas',
44 | LeakCheck.Cycle in '..\..\Source\LeakCheck.Cycle.pas',
45 | LeakCheck.Cycle.Utils in '..\..\Source\LeakCheck.Cycle.Utils.pas',
46 | DUnitX.MemoryLeakMonitor.LeakCheck in '..\..\External\DUnitX\DUnitX.MemoryLeakMonitor.LeakCheck.pas',
47 | DUnitX.MemoryLeakMonitor.LeakCheckCycle in '..\..\External\DUnitX\DUnitX.MemoryLeakMonitor.LeakCheckCycle.pas',
48 | {$IFDEF MSWINDOWS}
49 | {$IFDEF CPUX32}
50 | LeakCheck.Trace.DbgHelp in '..\..\Source\LeakCheck.Trace.DbgHelp.pas',
51 | {$ENDIF }
52 | LeakCheck.Trace.WinApi in '..\..\Source\LeakCheck.Trace.WinApi.pas',
53 | LeakCheck.Trace.Jcl in '..\..\Source\LeakCheck.Trace.Jcl.pas',
54 | LeakCheck.MapFile in '..\..\Source\LeakCheck.MapFile.pas',
55 | LeakCheck.Trace.Map in '..\..\Source\LeakCheck.Trace.Map.pas',
56 | {$ENDIF }
57 | {$IFDEF POSIX}
58 | LeakCheck.Trace.Backtrace in '..\..\Source\LeakCheck.Trace.Backtrace.pas',
59 | {$ENDIF }
60 | LeakCheck.TestCycle in '..\LeakCheck.TestCycle.pas';
61 |
62 | procedure Run;
63 | begin
64 | ReportMemoryLeaksOnShutdown := True;
65 |
66 | {$IFDEF MSWINDOWS}
67 | {$IFDEF CPUX64}
68 | TLeakCheck.GetStackTraceProc := WinApiStackTrace;
69 | {$ELSE}
70 | TLeakCheck.GetStackTraceProc := JclRawStackTrace;
71 | {$ENDIF}
72 | TLeakCheck.GetStackTraceFormatterProc := MapStackTraceFormatter;
73 | {$ENDIF}
74 | {$IFDEF POSIX}
75 | TLeakCheck.GetStackTraceProc := BacktraceStackTrace;
76 | TLeakCheck.GetStackTraceFormatterProc := PosixProcStackTraceFormatter;
77 | {$ENDIF}
78 |
79 | TDUnitX.RegisterTestFixture(TTestCycle);
80 | TDUnitX.RegisterTestFixture(TTestLeaksWithACycle);
81 | TDUnitX.RegisterTestFixture(TTestIgnoreGraphSimple);
82 | TDUnitX.RegisterTestFixture(TTestIgnoreGraphComplex);
83 | TDUnitXIoC.DefaultContainer.RegisterType(
84 | function : IMemoryLeakMonitor
85 | begin
86 | result := TDUnitXLeakCheckGraphMemoryLeakMonitor.Create;
87 | end);
88 | RunRegisteredTests;
89 | end;
90 |
91 | begin
92 | Run;
93 | end.
94 |
--------------------------------------------------------------------------------
/Test/XE/TestProjectX.dproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | {674ED35D-807D-471A-A95A-644D415829CE}
4 | TestProjectX.dpr
5 | 12.3
6 | True
7 | Debug
8 | Win32
9 | Console
10 | None
11 | DCC32
12 |
13 |
14 | true
15 |
16 |
17 | true
18 | Base
19 | true
20 |
21 |
22 | true
23 | Base
24 | true
25 |
26 |
27 | 3
28 | $(DUNITX_DIR);$(JCL_DIR)\include;$(JCL_DIR)\common;$(JCL_DIR)\windows;$(DCC_UnitSearchPath)
29 | LEAKCHECK_DEBUG;TESTINSIGHT;NO_MESSAGEBOX;DUNITX-DEBUG;DUNITX;$(DCC_Define)
30 | 00400000
31 | .\$(Platform)\$(Config)
32 | WinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;$(DCC_UnitAlias)
33 | .\$(Platform)\$(Config)
34 | false
35 | false
36 | false
37 | false
38 | false
39 |
40 |
41 | LEAKCHECK_DEFER;CHECK_LEAKS;DEBUG;$(DCC_Define)
42 | false
43 | true
44 |
45 |
46 | false
47 | RELEASE;$(DCC_Define)
48 | 0
49 | false
50 |
51 |
52 |
53 | MainSource
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 | Cfg_2
70 | Base
71 |
72 |
73 | Base
74 |
75 |
76 | Cfg_1
77 | Base
78 |
79 |
80 |
81 |
82 |
83 | Delphi.Personality.12
84 |
85 |
86 |
87 |
88 | False
89 | False
90 | 1
91 | 0
92 | 0
93 | 0
94 | False
95 | False
96 | False
97 | False
98 | False
99 | 1029
100 | 1250
101 |
102 |
103 |
104 |
105 | 1.0.0.0
106 |
107 |
108 |
109 |
110 |
111 | 1.0.0.0
112 |
113 |
114 |
115 | TestProjectX.dpr
116 |
117 |
118 |
119 | True
120 |
121 |
122 | 12
123 |
124 |
125 |
--------------------------------------------------------------------------------
/Test/XE6/TestProject.deployproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 12
5 |
6 |
7 |
8 |
9 | TestProject\library\lib\mips\
10 | libTestProject.so
11 | 1
12 |
13 |
14 | True
15 |
16 |
17 | TestProject\res\drawable-mdpi\
18 | ic_launcher.png
19 | 1
20 |
21 |
22 | True
23 |
24 |
25 | TestProject\
26 | AndroidManifest.xml
27 | 1
28 |
29 |
30 | True
31 |
32 |
33 | TestProject\classes\
34 | classes.dex
35 | 1
36 |
37 |
38 | True
39 |
40 |
41 | TestProject\res\drawable-hdpi\
42 | ic_launcher.png
43 | 1
44 |
45 |
46 | True
47 |
48 |
49 | TestProject\
50 | AndroidManifest.xml
51 | 1
52 |
53 |
54 | True
55 |
56 |
57 | TestProject\classes\
58 | classes.dex
59 | 1
60 |
61 |
62 | True
63 |
64 |
65 | TestProject\res\drawable-xxhdpi\
66 | ic_launcher.png
67 | 1
68 |
69 |
70 | True
71 |
72 |
73 | TestProject\library\lib\mips\
74 | libTestProject.so
75 | 1
76 |
77 |
78 | True
79 |
80 |
81 | TestProject\res\drawable-xhdpi\
82 | ic_launcher.png
83 | 1
84 |
85 |
86 | True
87 |
88 |
89 | TestProject\library\lib\x86\
90 | libTestProject.so
91 | 1
92 |
93 |
94 | True
95 |
96 |
97 | TestProject\library\lib\armeabi-v7a\
98 | gdbserver
99 | 1
100 |
101 |
102 | True
103 |
104 |
105 | TestProject\library\lib\armeabi\
106 | libTestProject.so
107 | 1
108 |
109 |
110 | True
111 |
112 |
113 | TestProject\res\drawable-ldpi\
114 | ic_launcher.png
115 | 1
116 |
117 |
118 | True
119 |
120 |
121 | TestProject\res\drawable-mdpi\
122 | ic_launcher.png
123 | 1
124 |
125 |
126 | True
127 |
128 |
129 | TestProject\res\drawable-ldpi\
130 | ic_launcher.png
131 | 1
132 |
133 |
134 | True
135 |
136 |
137 | TestProject\library\lib\armeabi-v7a\
138 | libTestProject.so
139 | 1
140 |
141 |
142 | True
143 | True
144 |
145 |
146 | TestProject\res\drawable-hdpi\
147 | ic_launcher.png
148 | 1
149 |
150 |
151 | True
152 |
153 |
154 | TestProject\library\lib\armeabi\
155 | libTestProject.so
156 | 1
157 |
158 |
159 | True
160 |
161 |
162 | TestProject\library\lib\armeabi-v7a\
163 | libTestProject.so
164 | 1
165 |
166 |
167 | True
168 | True
169 |
170 |
171 | TestProject\library\lib\x86\
172 | libTestProject.so
173 | 1
174 |
175 |
176 | True
177 |
178 |
179 | TestProject\library\lib\armeabi-v7a\
180 | gdbserver
181 | 1
182 |
183 |
184 | True
185 |
186 |
187 | TestProject\res\drawable-xhdpi\
188 | ic_launcher.png
189 | 1
190 |
191 |
192 | True
193 |
194 |
195 | TestProject\res\drawable-xxhdpi\
196 | ic_launcher.png
197 | 1
198 |
199 |
200 | True
201 |
202 |
203 |
204 |
205 |
206 | TestProject.app\Contents\MacOS\
207 | libcgunwind.1.0.dylib
208 | 1
209 |
210 |
211 | True
212 |
213 |
214 |
215 |
216 |
217 | TestProject.app\
218 | libcgunwind.1.0.dylib
219 | 1
220 |
221 |
222 | True
223 |
224 |
225 |
226 |
--------------------------------------------------------------------------------
/Test/XE6/TestProject.dpr:
--------------------------------------------------------------------------------
1 | {***************************************************************************}
2 | { }
3 | { LeakCheck for Delphi }
4 | { }
5 | { Copyright (c) 2015 Honza Rames }
6 | { }
7 | { https://bitbucket.org/shadow_cs/delphi-leakcheck }
8 | { }
9 | {***************************************************************************}
10 | { }
11 | { Licensed under the Apache License, Version 2.0 (the "License"); }
12 | { you may not use this file except in compliance with the License. }
13 | { You may obtain a copy of the License at }
14 | { }
15 | { http://www.apache.org/licenses/LICENSE-2.0 }
16 | { }
17 | { Unless required by applicable law or agreed to in writing, software }
18 | { distributed under the License is distributed on an "AS IS" BASIS, }
19 | { WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. }
20 | { See the License for the specific language governing permissions and }
21 | { limitations under the License. }
22 | { }
23 | {***************************************************************************}
24 |
25 | program TestProject;
26 |
27 | uses
28 | {$IFDEF WIN32}
29 | // If used together with LeakCheck registering expected leaks may not bubble
30 | // to the internal system memory manager and thus may be reported to the user.
31 | // This behavior is due to FastMM not calling parent RegisterExpectedMemoryLeak
32 | // and is not LeakCheck issue. This is only exposed if LEAKCHECK_DEFER is
33 | // defined.
34 | {$IFDEF LEAKCHECK_DEFER}
35 | FastMM4,
36 | {$ENDIF}
37 | {$ENDIF }
38 | LeakCheck in '..\..\Source\LeakCheck.pas',
39 | System.StartUpCopy,
40 | LeakCheck.Utils in '..\..\Source\LeakCheck.Utils.pas',
41 | FMX.Forms,
42 | TestFramework in '..\..\External\DUnit\TestFramework.pas',
43 | TestInsight.DUnit,
44 | Posix.Proc in '..\..\External\Backtrace\Source\Posix.Proc.pas',
45 | LeakCheck.TestUnit in '..\LeakCheck.TestUnit.pas',
46 | LeakCheck.TestDUnit in '..\LeakCheck.TestDUnit.pas',
47 | LeakCheck.TestForm in '..\LeakCheck.TestForm.pas' {frmLeakCheckTest},
48 | LeakCheck.DUnit in '..\..\Source\LeakCheck.DUnit.pas',
49 | LeakCheck.Cycle in '..\..\Source\LeakCheck.Cycle.pas',
50 | LeakCheck.TestCycle in '..\LeakCheck.TestCycle.pas',
51 | LeakCheck.DUnitCycle in '..\..\Source\LeakCheck.DUnitCycle.pas';
52 |
53 | {$R *.res}
54 |
55 | begin
56 | ReportMemoryLeaksOnShutdown := True;
57 |
58 | // Simple test of functionality
59 | RunTests;
60 |
61 | // DUnit integration
62 | {$IFDEF WEAKREF}
63 | TLeakCheck.IgnoredLeakTypes := [tkUnknown];
64 | {$ENDIF}
65 | MemLeakMonitorClass := TLeakCheckCycleMonitor;
66 | RunRegisteredTests;
67 |
68 | {$IFDEF GUI}
69 | // FMX Leak detection
70 | Application.Initialize;
71 | Application.CreateForm(TfrmLeakCheckTest, frmLeakCheckTest);
72 | Application.Run;
73 | {$ENDIF}
74 | end.
75 |
76 |
--------------------------------------------------------------------------------
/Test/XE6/TestProject.res:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shadow-cs/delphi-leakcheck/67cc1bdfb670242b5b15d5942174c0024d7117b2/Test/XE6/TestProject.res
--------------------------------------------------------------------------------
/Test/XE7/TestGroup.groupproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | {E1F123A0-B05F-4E6A-A3F3-B8F3995F2B06}
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | Default.Personality.12
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/Test/XE7/TestProject.deployproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 12
5 |
6 |
7 |
8 | TestProject\res\drawable-hdpi\
9 | ic_launcher.png
10 | 1
11 |
12 |
13 | True
14 |
15 |
16 | TestProject\res\drawable-xlarge\
17 | splash_image.png
18 | 1
19 |
20 |
21 | True
22 |
23 |
24 | TestProject\library\lib\armeabi-v7a\
25 | libTestProject.so
26 | 1
27 |
28 |
29 | True
30 | True
31 |
32 |
33 | TestProject\res\drawable-large\
34 | splash_image.png
35 | 1
36 |
37 |
38 | True
39 |
40 |
41 | TestProject\library\lib\armeabi\
42 | libTestProject.so
43 | 1
44 |
45 |
46 | True
47 |
48 |
49 | TestProject\res\drawable-xxhdpi\
50 | ic_launcher.png
51 | 1
52 |
53 |
54 | True
55 |
56 |
57 | TestProject\res\drawable-mdpi\
58 | ic_launcher.png
59 | 1
60 |
61 |
62 | True
63 |
64 |
65 | TestProject\library\lib\mips\
66 | libTestProject.so
67 | 1
68 |
69 |
70 | True
71 |
72 |
73 | TestProject\res\drawable\
74 | splash_image_def.xml
75 | 1
76 |
77 |
78 | True
79 |
80 |
81 | TestProject\
82 | AndroidManifest.xml
83 | 1
84 |
85 |
86 | True
87 |
88 |
89 | TestProject\res\drawable-small\
90 | splash_image.png
91 | 1
92 |
93 |
94 | True
95 |
96 |
97 | TestProject\classes\
98 | classes.dex
99 | 1
100 |
101 |
102 | True
103 |
104 |
105 | TestProject\res\values\
106 | styles.xml
107 | 1
108 |
109 |
110 | True
111 |
112 |
113 | TestProject\res\drawable-xhdpi\
114 | ic_launcher.png
115 | 1
116 |
117 |
118 | True
119 |
120 |
121 | TestProject\library\lib\armeabi-v7a\
122 | gdbserver
123 | 1
124 |
125 |
126 | True
127 |
128 |
129 | TestProject\res\drawable-ldpi\
130 | ic_launcher.png
131 | 1
132 |
133 |
134 | True
135 |
136 |
137 | TestProject\library\lib\x86\
138 | libTestProject.so
139 | 1
140 |
141 |
142 | True
143 |
144 |
145 | TestProject\res\drawable-normal\
146 | splash_image.png
147 | 1
148 |
149 |
150 | True
151 |
152 |
153 | TestProject\library\lib\armeabi-v7a\
154 | gdbserver
155 | 1
156 |
157 |
158 | True
159 |
160 |
161 |
162 |
163 |
164 | TestProject\
165 | TestProject.exe
166 | 0
167 |
168 |
169 | True
170 | True
171 |
172 |
173 |
174 |
175 | TestProject.app\Contents\MacOS\
176 | libcgunwind.1.0.dylib
177 | 1
178 |
179 |
180 | True
181 |
182 |
183 |
184 |
185 |
186 | TestProject.app\
187 | libcgunwind.1.0.dylib
188 | 1
189 |
190 |
191 | True
192 |
193 |
194 |
195 |
--------------------------------------------------------------------------------
/Test/XE7/TestProject.dpr:
--------------------------------------------------------------------------------
1 | {***************************************************************************}
2 | { }
3 | { LeakCheck for Delphi }
4 | { }
5 | { Copyright (c) 2015 Honza Rames }
6 | { }
7 | { https://bitbucket.org/shadow_cs/delphi-leakcheck }
8 | { }
9 | {***************************************************************************}
10 | { }
11 | { Licensed under the Apache License, Version 2.0 (the "License"); }
12 | { you may not use this file except in compliance with the License. }
13 | { You may obtain a copy of the License at }
14 | { }
15 | { http://www.apache.org/licenses/LICENSE-2.0 }
16 | { }
17 | { Unless required by applicable law or agreed to in writing, software }
18 | { distributed under the License is distributed on an "AS IS" BASIS, }
19 | { WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. }
20 | { See the License for the specific language governing permissions and }
21 | { limitations under the License. }
22 | { }
23 | {***************************************************************************}
24 |
25 | program TestProject;
26 |
27 | uses
28 | {$IFDEF WIN32}
29 | // If used together with LeakCheck registering expected leaks may not bubble
30 | // to the internal system memory manager and thus may be reported to the user.
31 | // This behavior is due to FastMM not calling parent RegisterExpectedMemoryLeak
32 | // and is not LeakCheck issue. This is only exposed if LEAKCHECK_DEFER is
33 | // defined.
34 | {$IFDEF LEAKCHECK_DEFER}
35 | FastMM4,
36 | {$ENDIF}
37 | {$ENDIF }
38 | {$IFDEF MSWINDOWS}
39 | Windows,
40 | {$ENDIF }
41 | LeakCheck in '..\..\Source\LeakCheck.pas',
42 | {$IFDEF MSWINDOWS}
43 | LeakCheck.Setup.JclTrace,
44 | {$ELSE}
45 | LeakCheck.Setup.Trace,
46 | {$ENDIF}
47 | System.StartUpCopy,
48 | LeakCheck.Utils in '..\..\Source\LeakCheck.Utils.pas',
49 | FMX.Forms,
50 | TestFramework in '..\..\External\DUnit\TestFramework.pas',
51 | TestInsight.DUnit,
52 | Posix.Proc in '..\..\External\Backtrace\Source\Posix.Proc.pas',
53 | LeakCheck.TestUnit in '..\LeakCheck.TestUnit.pas',
54 | LeakCheck.TestDUnit in '..\LeakCheck.TestDUnit.pas',
55 | LeakCheck.TestForm in '..\LeakCheck.TestForm.pas' {frmLeakCheckTest},
56 | LeakCheck.DUnit in '..\..\Source\LeakCheck.DUnit.pas',
57 | LeakCheck.Collections in '..\..\Source\LeakCheck.Collections.pas',
58 | LeakCheck.Cycle in '..\..\Source\LeakCheck.Cycle.pas',
59 | LeakCheck.Cycle.Utils in '..\..\Source\LeakCheck.Cycle.Utils.pas',
60 | LeakCheck.DUnitCycle in '..\..\Source\LeakCheck.DUnitCycle.pas',
61 | {$IFDEF MSWINDOWS}
62 | {$IFDEF CPUX32}
63 | LeakCheck.Trace.DbgHelp in '..\..\Source\LeakCheck.Trace.DbgHelp.pas',
64 | {$ENDIF }
65 | LeakCheck.Trace.WinApi in '..\..\Source\LeakCheck.Trace.WinApi.pas',
66 | LeakCheck.Trace.Jcl in '..\..\Source\LeakCheck.Trace.Jcl.pas',
67 | LeakCheck.MapFile in '..\..\Source\LeakCheck.MapFile.pas',
68 | LeakCheck.Trace.Map in '..\..\Source\LeakCheck.Trace.Map.pas',
69 | {$ENDIF }
70 | {$IFDEF POSIX}
71 | LeakCheck.Trace.Backtrace in '..\..\Source\LeakCheck.Trace.Backtrace.pas',
72 | {$ENDIF }
73 | LeakCheck.TestCycle in '..\LeakCheck.TestCycle.pas';
74 |
75 | {$R *.res}
76 |
77 | begin
78 | ReportMemoryLeaksOnShutdown := True;
79 |
80 | {$IFDEF MSWINDOWS}
81 | TLeakCheck.GetStackTraceProc := WinApiStackTrace;
82 | //TLeakCheck.GetStackTraceProc := DbgHelpStackTrace;
83 | {$IFDEF CPUX64}
84 | TLeakCheck.GetStackTraceProc := JclFramesStackTrace;
85 | {$ELSE}
86 | TLeakCheck.GetStackTraceProc := JclRawStackTrace;
87 | {$ENDIF}
88 | //TLeakCheck.GetStackTraceProc := JclFramesStackTrace;
89 |
90 | TLeakCheck.GetStackTraceFormatterProc := JclStackTraceFormatter;
91 | //TLeakCheck.GetStackTraceFormatterProc := MapStackTraceFormatter;
92 | {$ENDIF}
93 | {$IFDEF POSIX}
94 | TLeakCheck.GetStackTraceProc := BacktraceStackTrace;
95 | TLeakCheck.GetStackTraceFormatterProc := PosixProcStackTraceFormatter;
96 | {$ENDIF}
97 |
98 | // Simple test of functionality
99 | RunTests;
100 |
101 | // DUnit integration
102 | {$IFDEF WEAKREF}
103 | TLeakCheck.IgnoredLeakTypes := [tkUnknown];
104 | {$ENDIF}
105 | MemLeakMonitorClass := TLeakCheckCycleMonitor;
106 | RunRegisteredTests;
107 |
108 | {$IFDEF GUI}
109 | // FMX Leak detection
110 | Application.Initialize;
111 | Application.CreateForm(TfrmLeakCheckTest, frmLeakCheckTest);
112 | Application.Run;
113 | {$ENDIF}
114 | end.
115 |
116 |
--------------------------------------------------------------------------------
/Test/XE7/TestProject.res:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shadow-cs/delphi-leakcheck/67cc1bdfb670242b5b15d5942174c0024d7117b2/Test/XE7/TestProject.res
--------------------------------------------------------------------------------
/Test/XE7/TestProjectX.dpr:
--------------------------------------------------------------------------------
1 | {***************************************************************************}
2 | { }
3 | { LeakCheck for Delphi }
4 | { }
5 | { Copyright (c) 2015 Honza Rames }
6 | { }
7 | { https://bitbucket.org/shadow_cs/delphi-leakcheck }
8 | { }
9 | {***************************************************************************}
10 | { }
11 | { Licensed under the Apache License, Version 2.0 (the "License"); }
12 | { you may not use this file except in compliance with the License. }
13 | { You may obtain a copy of the License at }
14 | { }
15 | { http://www.apache.org/licenses/LICENSE-2.0 }
16 | { }
17 | { Unless required by applicable law or agreed to in writing, software }
18 | { distributed under the License is distributed on an "AS IS" BASIS, }
19 | { WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. }
20 | { See the License for the specific language governing permissions and }
21 | { limitations under the License. }
22 | { }
23 | {***************************************************************************}
24 |
25 | program TestProjectX;
26 |
27 | // Make sure to have DUnitX in your global search path or point DUNITX_DIR
28 | // environmental variable to DUnitX base source directory.
29 |
30 | // Note that in order to run this project you have to have the updated DUnitX
31 | // framework that supports extended leak checking.
32 |
33 | uses
34 | {$IFDEF WIN32}
35 | FastMM4,
36 | {$ENDIF }
37 | LeakCheck in '..\..\Source\LeakCheck.pas',
38 | System.StartUpCopy,
39 | LeakCheck.Utils in '..\..\Source\LeakCheck.Utils.pas',
40 | FMX.Forms,
41 | DUnitX.TestFramework,
42 | DUnitX.IoC,
43 | TestInsight.DUnitX,
44 | Posix.Proc in '..\..\External\Backtrace\Source\Posix.Proc.pas',
45 | LeakCheck.TestForm in '..\LeakCheck.TestForm.pas' {frmLeakCheckTest},
46 | LeakCheck.TestDUnitX in '..\LeakCheck.TestDUnitX.pas',
47 | LeakCheck.Collections in '..\..\Source\LeakCheck.Collections.pas',
48 | LeakCheck.Cycle in '..\..\Source\LeakCheck.Cycle.pas',
49 | LeakCheck.Cycle.Utils in '..\..\Source\LeakCheck.Cycle.Utils.pas',
50 | DUnitX.MemoryLeakMonitor.LeakCheck in '..\..\External\DUnitX\DUnitX.MemoryLeakMonitor.LeakCheck.pas',
51 | DUnitX.MemoryLeakMonitor.LeakCheckCycle in '..\..\External\DUnitX\DUnitX.MemoryLeakMonitor.LeakCheckCycle.pas',
52 | {$IFDEF MSWINDOWS}
53 | {$IFDEF CPUX32}
54 | LeakCheck.Trace.DbgHelp in '..\..\Source\LeakCheck.Trace.DbgHelp.pas',
55 | {$ENDIF }
56 | LeakCheck.Trace.WinApi in '..\..\Source\LeakCheck.Trace.WinApi.pas',
57 | LeakCheck.Trace.Jcl in '..\..\Source\LeakCheck.Trace.Jcl.pas',
58 | LeakCheck.MapFile in '..\..\Source\LeakCheck.MapFile.pas',
59 | LeakCheck.Trace.Map in '..\..\Source\LeakCheck.Trace.Map.pas',
60 | {$ENDIF }
61 | {$IFDEF POSIX}
62 | LeakCheck.Trace.Backtrace in '..\..\Source\LeakCheck.Trace.Backtrace.pas',
63 | {$ENDIF }
64 | LeakCheck.TestCycle in '..\LeakCheck.TestCycle.pas';
65 |
66 | {$R *.res}
67 |
68 | procedure Run;
69 | begin
70 | ReportMemoryLeaksOnShutdown := True;
71 |
72 | {$IFDEF MSWINDOWS}
73 | {$IFDEF CPUX64}
74 | TLeakCheck.GetStackTraceProc := WinApiStackTrace;
75 | {$ELSE}
76 | TLeakCheck.GetStackTraceProc := JclRawStackTrace;
77 | {$ENDIF}
78 | TLeakCheck.GetStackTraceFormatterProc := MapStackTraceFormatter;
79 | {$ENDIF}
80 | {$IFDEF POSIX}
81 | TLeakCheck.GetStackTraceProc := BacktraceStackTrace;
82 | TLeakCheck.GetStackTraceFormatterProc := PosixProcStackTraceFormatter;
83 | {$ENDIF}
84 |
85 | TDUnitX.RegisterTestFixture(TTestCycle);
86 | TDUnitX.RegisterTestFixture(TTestLeaksWithACycle);
87 | TDUnitX.RegisterTestFixture(TTestIgnoreGraphSimple);
88 | TDUnitX.RegisterTestFixture(TTestIgnoreGraphComplex);
89 | TDUnitXIoC.DefaultContainer.RegisterType(
90 | function : IMemoryLeakMonitor
91 | begin
92 | result := TDUnitXLeakCheckGraphMemoryLeakMonitor.Create;
93 | end);
94 | RunRegisteredTests;
95 | end;
96 |
97 | begin
98 | Run;
99 | end.
100 |
--------------------------------------------------------------------------------
/Test/XE7/TestProjectX.res:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shadow-cs/delphi-leakcheck/67cc1bdfb670242b5b15d5942174c0024d7117b2/Test/XE7/TestProjectX.res
--------------------------------------------------------------------------------
/Test/readme.txt:
--------------------------------------------------------------------------------
1 | Make sure to either add JCL sources to global search path or register JCL_DIR
2 | environmental variable pointing to JCL base `source` directory.
3 |
--------------------------------------------------------------------------------