-
Notifications
You must be signed in to change notification settings - Fork 22
Expand file tree
/
Copy pathLightCore.Debugger.pas
More file actions
303 lines (218 loc) · 9.84 KB
/
LightCore.Debugger.pas
File metadata and controls
303 lines (218 loc) · 9.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
UNIT LightCore.Debugger;
{=============================================================================================================
2026.01.29
www.GabrielMoraru.com
--------------------------------------------------------------------------------------------------------------
Debugging and profiling utilities:
- Code execution timing (using high-resolution TStopwatch)
- Debugger detection (check if running under Delphi IDE)
- Compiler optimization status
- Crash/leak generators for testing error handlers
- Simple file-based logging
Platform: Cross-platform via System.Diagnostics.TStopwatch
(DebugHook is Windows-specific)
WARNING: Timer functions use global state and are NOT thread-safe.
For multi-threaded timing, create local TStopwatch instances.
Tester:
c:\Projects\LightSaber\Demo\VCL\Demo SystemReport\VCL_Demo_SystemReport.dpr
c:\Projects\LightSaber\Demo\FMX\Demo SystemReport\FMX_Demo_SystemReport.dpr
Also see:
QuickImageFX\Quick.Chrono.pas
=============================================================================================================}
INTERFACE
USES
System.Diagnostics, System.SysUtils;
{ ANTI-DEBUGGER PROTECTION }
function IsRunningUnderDelphiDebugger: Boolean;
{ COMPILER INFO }
function CompilerOptimization : Boolean;
function CompilerOptimizationS: String;
{ CRASH ME }
procedure GenerateCrashNIL;
procedure GenerateCrashException;
procedure GenerateLeak;
{ CODE TIMING }
procedure TimerStart; { use it with: SetPriorityMax }
function TimerElapsed: Double; { In miliseconds }
function TimerElapsedS: string; { In miliseconds or seconds }
function ShowTransferSpeed(FileSize: Cardinal): string; { Shows the disk/internet speed }
procedure LogFile_Init (FullFileName: string); { Init the log }
procedure LogFile_Add (s: string); { Write in Log }
{ Reports }
function GenerateCompilerReport: string;
IMPLEMENTATION
USES
{$IFDEF POSIX}Posix.Unistd,{$ENDIF}
LightCore, LightCore.Platform, LightCore.IO, LightCore.TextFile, LightCore.AppData, LightCore.Types;
{--------------------------------------------------------------------------------------------------
PROTECTION
Also see c:\MyProjects\Packages\Third party packages\uDebugger.pas
--------------------------------------------------------------------------------------------------}
{ Is the process running as part of Delphi? }
{.$IFDEF msWindows}
{$WARN SYMBOL_PLATFORM OFF} // prevent W1002 Symbol 'DebugHook' is specific to a platform
function IsRunningUnderDelphiDebugger: Boolean;
begin
Result := (DebugHook <> 0);
end;
{$WARN SYMBOL_PLATFORM ON}
{.$ENDIF}
{-------------------------------------------------------------------------------------------------------------
COMPILER
-------------------------------------------------------------------------------------------------------------}
{ Important note:
$O+ has LOCAL scope - the result reflects the optimization state at THIS specific location.
If you use $O+ / $O- switches inline to optimize specific code sections, this function
must be INLINED at each call site to reflect the local setting.
If you only use the global switch (Project Options), it works as a normal function. }
{ Returns true in the compiler optimization is on (probably we are in release mode, in this case) }
function CompilerOptimization: Boolean;
begin
{$IfOpt O+}
Result:= TRUE;
{$Else}
Result:= FALSE;
{$EndIf}
end;
{ Same as above }
function CompilerOptimizationS: String;
begin
Result:= 'Compiler optimization is ' +
{$IfOpt O+}
'enabled'
{$Else}
'disabled'
{$EndIf}
end;
{--------------------------------------------------------------------------------------------------
CODE TIMING
--------------------------------------------------------------------------------------------------
TStopwatch is a wrap arround QueryPerformanceCounter which according to Microsoft has resolution < 1us.
WARNING!
The value of Elapsed is only updated if the stopwatch is priorly stopped. Reading the Elapsed property while the stopwatch is running does not yield any difference.
How to use it:
OnFormCreate -> SetPriorityMax;
TimerStart;
MySlowFunction;
Caption:= TimerElapsedS;
Use it only for small intervals (way under 1 day)!
Source: http://stackoverflow.com/questions/6420051/why-queryperformancecounter-timed-different-from-wall-clock
https://blogs.msdn.microsoft.com/oldnewthing/20050902-00/?p=34333
--------------------------------------------------------------------------------------------------}
VAR
sw: TStopWatch; { This is a record not an class! }
procedure TimerStart;
begin
sw := TStopWatch.Create; { Hint: We can use directly: TStopWatch.CreateNew but SilverWarior says there is a bug in it. Maybe this one? https://codeverge.com/embarcadero.delphi.win32/tstopwatch-a-bug-delphi-2010/1046096 }
sw.Start;
end;
{ Returns the time elapsed, in miliseconds }
function TimerElapsed: Double;
begin
sw.Stop;
Result:= sw.ElapsedMilliseconds; {WARNING! The value of Elapsed is only updated if the stopwatch is priorly stopped. Reading the Elapsed property while the stopwatch is running does not yield any difference. }
end;
{ Converts elapsed time to human-readable format with appropriate units.
Returns the most suitable unit based on magnitude:
- ns (nanoseconds) for < 1 microsecond
- us (microseconds) for < 1 millisecond
- ms (milliseconds) for < 1 second
- s (seconds) for < 1 minute
- m (minutes) for >= 1 minute }
function TimerElapsedS: string;
VAR NanoSec: Int64;
begin
sw.Stop;
NanoSec:= sw.Elapsed.Ticks * 100; { Elapsed.Ticks is in 100ns increments }
if NanoSec < NanosPerMicroSec
then Result:= IntToStr(NanoSec) + 'ns'
else
if NanoSec < NanosPerMileSec
then Result:= Real2Str(NanoSec / NanosPerMicroSec, 3) + 'us'
else
if NanoSec < NanosPerSecond
then Result:= Real2Str(NanoSec / NanosPerMileSec, 3) + 'ms'
else
if NanoSec < NanosPerMinute
then Result:= Real2Str(NanoSec / NanosPerSecond, 3) + 's'
else Result:= Real2Str(NanoSec / NanosPerMinute, 3) + 'm'; { BUG FIX: was NanoSec / 60*1000000000 (wrong precedence) }
end;
{ Calculates and formats transfer speed (disk/network).
Call TimerStart before the operation, then pass the transferred size.
Example:
TimerStart;
CopyFile(FileName);
Caption:= ShowTransferSpeed(GetFileSize(FileName)); }
function ShowTransferSpeed(FileSize: Cardinal): string;
VAR
ElapsedSec: Double;
begin
ElapsedSec:= TimerElapsed / 1000;
if ElapsedSec > 0
then Result:= 'Speed: ' + FormatBytes(Round(FileSize / ElapsedSec), 1) + '/sec'
else Result:= 'Speed: UFO (too fast to measure)';
end;
{--------------------------------------------------------------------------------------------------
LOGGING
Simple file-based logging. NOT thread-safe (uses global LogFile variable).
Call LogFile_Init once before using LogFile_Add.
WARNING: Uses global state. For thread-safe logging, use a proper logging framework
or create a TLogFile class with instance-based state.
--------------------------------------------------------------------------------------------------}
VAR
LogFile: string; { Global: path to the log file. Set by LogFile_Init. }
{ Initializes the log file. Creates parent directories if needed.
Clears any existing log file and writes a header with app name and timestamp. }
procedure LogFile_Init(FullFileName: string);
begin
Assert(FullFileName <> '', 'LogFile_Init: FullFileName cannot be empty');
LogFile:= FullFileName;
ForceDirectories(ExtractFilePath(FullFileName));
if FileExists(LogFile)
then DeleteFile(LogFile);
StringToFile(LogFile, LogFile + CRLF + TAppDataCore.AppName + CRLF + DateTimeToStr(Now) + CRLF + LBRK, woAppend);
end;
{ Appends a line to the log file. Only writes if the log file exists (was initialized).
Silently ignores writes if LogFile_Init was not called. }
procedure LogFile_Add(s: string);
begin
if (LogFile <> '')
AND FileExists(LogFile)
then StringToFile(LogFile, s + CRLF, woAppend);
end;
{--------------------------------------------------------------------------------------------------
CRASH/LEAK GENERATORS (for testing error handlers and memory leak detection)
--------------------------------------------------------------------------------------------------}
{ Intentionally causes an Access Violation by calling a method on a nil reference.
Use this to test exception handlers, crash reporters, or madExcept/EurekaLog. }
procedure GenerateCrashNIL;
VAR T: TObject;
begin
T:= NIL;
T.ClassName; { Access Violation: calling virtual method on nil }
end;
TYPE
TMyMadsiTestException = class(Exception);
procedure GenerateCrashException;
begin
RAISE TMyMadsiTestException.Create('MadShi exception!'); // madShi is strongly recommended over EurekaLog!
end;
{ Intentionally creates a memory leak by allocating an object without freeing it.
Use this to test ReportMemoryLeaksOnShutdown or memory leak detection tools. }
procedure GenerateLeak;
VAR T: TObject;
begin
T:= TObject.Create;
T.ToString; { Reference T to prevent compiler hint, but never free it }
end;
{--------------------------------------------------------------------------------------------------
REPORTS
--------------------------------------------------------------------------------------------------}
function GenerateCompilerReport: string;
begin
Result:= ' [COMPILER]'+ CRLF;
Result:= Result+' RunningUnderDelphi: ' + BoolToStrYesNo(IsRunningUnderDelphiDebugger)+ CRLF;
Result:= Result+' AppBitnessEx: ' + Tab + AppBitnessEx+ CRLF;
Result:= Result+' CompilerOptim: ' + Tab + BoolToStrYesNo(CompilerOptimization)+ CRLF;
end;
end.