Menu

[327ada]: / Source / Debugger.pas  Maximize  Restore  History

Download this file

435 lines (355 with data), 12.6 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
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
{
This file is part of Dev-C++
Copyright (c) 2004 Bloodshed Software
Dev-C++ is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Dev-C++ is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Dev-C++; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
}
unit Debugger;
interface
uses
Sysutils, Windows, Messages, Forms, Classes, Controls,
debugreader, version, editor, ComCtrls, Dialogs, MultiLangSupport;
type
TEvalReadyEvent = procedure(const evalvalue: String) of object;
TDebugger = class(TObject)
private
fOutputRead: THandle;
fOutputWrite: THandle;
fInputRead: THandle;
fInputWrite: THandle;
fProcessID: THandle;
fExecuting: boolean;
fCommandChanged: boolean;
fDebugView: TTreeView;
fLeftPageIndexBackup: integer;
fBreakPointList: TList;
fWatchVarList: TList;
fOnEvalReady: TEvalReadyEvent;
fReader: TDebugReader;
function GetBreakPointFile: String;
public
constructor Create;
destructor Destroy; override;
// Play/pause
procedure Start;
procedure Stop;
procedure SendCommand(const command, params: String; viewinui: boolean = false);
// breakpoints
procedure AddBreakPoint(i: integer); overload;
procedure RemoveBreakPoint(i: integer); overload;
procedure AddBreakPoint(Linein: integer; e: TEditor); overload;
procedure RemoveBreakPoint(Linein: integer; e: TEditor); overload;
procedure DeleteBreakPointsOf(editor: TEditor);
// watch var
procedure AddWatchVar(i: integer); overload;
procedure RemoveWatchVar(i: integer); overload;
procedure AddWatchVar(const namein: String); overload;
procedure RemoveWatchVar(nodein: TTreeNode); overload;
procedure RefreshWatchVars;
procedure DeleteWatchVars(deleteparent: boolean);
// Access
property Executing: boolean read fExecuting write fExecuting;
property LeftPageIndexBackup: integer read fLeftPageIndexBackup write fLeftPageIndexBackup;
property WatchVarList: TList read fWatchVarList write fWatchVarList;
property BreakPointList: TList read fBreakPointList write fBreakPointList;
property CommandChanged: boolean read fCommandChanged write fCommandChanged;
property DebugView: TTreeView read fDebugView write fDebugView;
property OnEvalReady: TEvalReadyEvent read fOnEvalReady write fOnEvalReady;
property Reader: TDebugReader read fReader write fReader;
property BreakPointFile: String read GetBreakPointFile;
end;
implementation
uses
System.UItypes, main, devcfg, utils, cpufrm;
constructor TDebugger.Create;
begin
inherited;
BreakPointList := TList.Create;
WatchVarList := TList.Create;
end;
destructor TDebugger.Destroy;
var
I: integer;
begin
Stop;
// Remove watch vars
for i := 0 to WatchVarList.Count - 1 do
Dispose(PWatchVar(WatchVarList.Items[i]));
WatchVarList.Free;
// Remove the breakpoints
for i := 0 to BreakPointList.Count - 1 do
Dispose(PBreakPoint(BreakPointList.Items[i]));
BreakPointList.Free;
inherited;
end;
procedure TDebugger.Start;
var
pi: TProcessInformation;
si: TStartupInfo;
sa: TSecurityAttributes;
GDBFile, GDBCommand: String;
CompilerSet: TdevCompilerSet;
begin
Executing := true;
// Set up the security attributes struct.
sa.nLength := sizeof(TSecurityAttributes);
sa.lpSecurityDescriptor := nil;
sa.bInheritHandle := true;
// Create the child output pipe.
if not CreatePipe(fOutputread, fOutputwrite, @sa, 0) then
Exit;
if not SetHandleInformation(fOutputread, HANDLE_FLAG_INHERIT, 0) then
Exit;
// Create the child input pipe.
if not CreatePipe(fInputread, fInputwrite, @sa, 0) then
Exit;
if not SetHandleInformation(fInputwrite, HANDLE_FLAG_INHERIT, 0) then
Exit;
// Set up the start up info struct.
FillChar(si, sizeof(TStartupInfo), 0);
si.cb := sizeof(TStartupInfo);
si.dwFlags := STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW or STARTF_USESHOWWINDOW;
si.hStdInput := fInputread;
si.hStdOutput := fOutputwrite;
si.hStdError := fOutputwrite;
si.wShowWindow := SW_HIDE;
// Use the GDB provided in the project if needed
CompilerSet := devCompilerSets.CompilationSet;
// Assume it's present in the first bin dir
if CompilerSet.BinDir.Count > 0 then begin
GDBFile := CompilerSet.BinDir[0] + pd + CompilerSet.gdbName;
GDBCommand := '"' + GDBFile + '"' + ' --annotate=2 --silent';
if not CreateProcess(nil, PChar(GDBCommand), nil, nil, true, CREATE_NEW_CONSOLE, nil, nil, si, pi) then begin
MessageDlg(Format(Lang[ID_ERR_ERRORLAUNCHINGGDB], [GDBFile, SysErrorMessage(GetLastError)]), mtError,
[mbOK], 0);
Executing := false;
Exit;
end;
end else
MessageDlg(Lang[ID_ERR_GDBNOUTFOUND], mtError, [mbOK], 0);
fProcessID := pi.hProcess;
// Create a thread that will read GDB output.
Reader := TDebugReader.Create(true);
Reader.PipeRead := fOutputRead;
Reader.FreeOnTerminate := true;
Reader.BreakpointList := BreakPointList;
Reader.WatchVarList := WatchVarList;
Reader.DebugView := DebugView;
Reader.Start;
MainForm.UpdateAppTitle;
Application.HintHidePause := 5000;
end;
procedure TDebugger.Stop;
begin
if Executing then begin
Executing := false;
if WatchVarList.Count = 0 then // nothing worth showing, restore view
MainForm.LeftPageControl.ActivePageIndex := LeftPageIndexBackup;
// Close CPU window
if Assigned(CPUForm) then
CPUForm.Close;
TerminateProcess(fProcessID, 0); // stop gdb
Reader.Terminate;
Reader := nil;
// Free resources
if not CloseHandle(fProcessID) then
Exit;
if not CloseHandle(fOutputwrite) then
Exit;
if not CloseHandle(fInputread) then
Exit;
MainForm.RemoveActiveBreakpoints;
MainForm.UpdateAppTitle;
Application.HintHidePause := 2500;
end;
end;
procedure TDebugger.SendCommand(const Command, Params: String; ViewInUI: boolean);
var
nBytesWrote: DWORD;
Buff: TBytes;
begin
if Executing then begin
Buff := TEncoding.ANSI.GetBytes((command + ' ' + params).Trim) + [10];
if not WriteFile(fInputwrite, (@Buff[Low(Buff)])^, Length(Buff), nBytesWrote, nil) then
MessageDlg(Lang[ID_ERR_WRITEGDB], mtError, [mbOK], 0);
if ViewInUI then
if (not CommandChanged) or (MainForm.edGdbCommand.Text = '') then begin
// Convert command to C string
if Length(params) > 0 then
MainForm.edGdbCommand.Text := Command + ' ' + params
else
MainForm.edGdbCommand.Text := Command;
CommandChanged := false;
end;
end;
end;
function TDebugger.GetBreakPointFile: String;
begin
if Executing then
Result := fReader.BreakPointFile
else
Result := '';
end;
procedure TDebugger.AddBreakPoint(i: integer);
var
filename: String;
begin
// "filename":linenum
filename := StringReplace(PBreakPoint(BreakPointList.Items[i])^.editor.FileName, '\', '/', [rfReplaceAll]);
SendCommand('break', '"' + filename + '":' + inttostr(PBreakPoint(BreakPointList.Items[i])^.line), true);
end;
procedure TDebugger.RemoveBreakPoint(i: integer);
var
filename: String;
begin
// "filename":linenum
filename := StringReplace(PBreakPoint(BreakPointList.Items[i])^.editor.FileName, '\', '/', [rfReplaceAll]);
SendCommand('clear', '"' + filename + '":' + inttostr(PBreakPoint(BreakPointList.Items[i])^.line), true);
end;
procedure TDebugger.AddBreakPoint(linein: integer; e: TEditor);
var
APBreakPoint: PBreakPoint;
begin
APBreakPoint := new(PBreakPoint);
with APBreakPoint^ do begin
line := Linein;
editor := e;
end;
BreakPointList.Add(APBreakPoint);
// Debugger already running? Add it to GDB
if Executing then
AddBreakPoint(BreakPointList.Count - 1);
end;
procedure TDebugger.RemoveBreakPoint(Linein: integer; e: TEditor);
var
i: integer;
begin
for i := 0 to BreakPointList.Count - 1 do begin
if (PBreakPoint(BreakPointList.Items[i])^.line = Linein) and (PBreakPoint(BreakPointList.Items[i])^.editor = e) then
begin
// Debugger already running? Remove it from GDB
if Executing then
RemoveBreakPoint(i);
// Remove from list
Dispose(PBreakPoint(BreakPointList.Items[i]));
BreakPointList.Delete(i);
break;
end;
end;
end;
procedure TDebugger.DeleteBreakPointsOf(editor: TEditor);
var
I: integer;
begin
// Breakpoints in closed files need to be deleted
for i := BreakPointList.Count - 1 downto 0 do
if PBreakPoint(BreakPointList.Items[i])^.editor = editor then begin
// Remove from list
Dispose(PBreakPoint(BreakPointList.Items[i]));
BreakPointList.Delete(i);
end;
end;
procedure TDebugger.AddWatchVar(i: integer);
begin
SendCommand('display', PWatchVar(WatchVarList.Items[i])^.name, true);
end;
procedure TDebugger.RemoveWatchVar(i: integer);
begin
SendCommand('undisplay', IntToStr(PWatchVar(WatchVarList.Items[i])^.gdbindex), true);
end;
procedure TDebugger.AddWatchVar(const namein: String);
var
parentnode: TTreeNode;
I: integer;
wparent: PWatchVar;
begin
// Don't allow duplicates...
for I := 0 to WatchVarList.Count - 1 do
if SameStr(PWatchVar(WatchVarList.Items[i])^.name, namein) then
Exit;
// Add parent to list
wparent := New(PWatchVar);
wparent^.name := namein;
// wparent^.value := 'Execute to evaluate';
wparent^.gdbindex := -1; // filled by GDB
WatchVarList.Add(wparent);
// Add parent to GUI
parentnode := DebugView.Items.AddObject(nil, wparent^.name + ' = Execute to evaluate', wparent);
parentnode.ImageIndex := 21;
parentnode.SelectedIndex := 21;
// Refer to list from GUI
wparent^.node := parentnode;
// Debugger already running? Add it to GDB
if Executing then
AddWatchVar(WatchVarList.Count - 1);
end;
procedure TDebugger.RemoveWatchVar(nodein: TTreeNode);
var
I: integer;
wparent: PWatchVar;
begin
for i := 0 to WatchVarList.Count - 1 do begin
wparent := PWatchVar(WatchVarList.Items[I]);
if SameStr(wparent^.name, PWatchVar(nodein.Data)^.name) then begin
// Debugger already running and GDB scanned this one? Remove it from GDB
if Executing and (wparent^.gdbindex <> -1) then
RemoveWatchVar(i);
// Remove from UI
nodein.DeleteChildren;
nodein.Delete;
// Remove from list
Dispose(PWatchVar(WatchVarList.Items[i]));
WatchVarList.Delete(i);
break;
end;
end;
end;
procedure TDebugger.RefreshWatchVars;
var
I: integer;
begin
// Variables that aren't found need to be re-displayed!
for i := 0 to WatchVarList.Count - 1 do
if PWatchVar(WatchVarList.Items[i])^.gdbindex = -1 then
AddWatchVar(i); // resends command to display to GDB
end;
procedure TDebugger.DeleteWatchVars(deleteparent: boolean);
var
I: integer;
wparent: PWatchVar;
begin
DebugView.Items.BeginUpdate;
try
for I := WatchVarList.Count - 1 downto 0 do begin
wparent := PWatchVar(WatchVarList.Items[I]);
if deleteparent then begin
// Remove from UI
if wparent^.node.HasChildren then
wparent^.node.DeleteChildren;
wparent^.node.Delete;
// Remove from list
Dispose(PWatchVar(WatchVarList.Items[i]));
WatchVarList.Delete(i);
end else begin
// Remove from UI
if wparent^.node.HasChildren then
wparent^.node.DeleteChildren;
// Leave parent node intact...
wparent^.gdbindex := -1;
wparent^.node.Text := wparent^.name + ' = Execute to evaluate';
end;
end;
finally
DebugView.Items.EndUpdate;
end;
end;
end.
Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.