1 unit DebugThreadCommand;
2
3 {$mode objfpc}{$H+}
4
5 {$ifndef VER2}
6 {$define disassemblernestedproc}
7 {$endif VER2}
8
9 {$ifdef disassemblernestedproc}
10 {$modeswitch nestedprocvars}
11 {$endif disassemblernestedproc}
12
13 interface
14
15 uses
16 Classes,
17 FPDbgController,
18 FpDbgClasses,
19 FpDbgUtil,
20 FpDbgInfo,
21 FpPascalParser,
22 FpPascalBuilder,
23 FpErrorMessages,
24 DbgIntfDebuggerBase,
25 DbgIntfBaseTypes,
26 strutils,
27 debugthread,
28 CustApp,
29 Maps,
30 SysUtils;
31
32 type
33
34 { TFpDebugThreadCommandList }
35
36 TFpDebugThreadCommandList = class(TFPList)
37 public
instancenull38 class function instance: TFpDebugThreadCommandList;
GetCommandByNamenull39 function GetCommandByName(ATextName: string): TFpDebugThreadCommandClass;
40 end;
41
42 { TFpDebugThreadQuitDebugServerCommand }
43
44 TFpDebugThreadQuitDebugServerCommand = class(TFpDebugThreadCommand)
45 public
PreExecutenull46 function PreExecute(AController: TFpServerDbgController; out DoQueueCommand: boolean): boolean; override;
Executenull47 function Execute(AController: TFpServerDbgController; out DoProcessLoop: boolean): boolean; override;
TextNamenull48 class function TextName: string; override;
49 end;
50
51 { TFpDebugThreadSetFilenameCommand }
52
53 TFpDebugThreadSetFilenameCommand = class(TFpDebugThreadCommand)
54 private
55 FFileName: string;
56 public
Executenull57 function Execute(AController: TFpServerDbgController; out DoProcessLoop: boolean): boolean; override;
TextNamenull58 class function TextName: string; override;
59 published
60 property Filename: string read FFileName write FFileName;
61 end;
62
63 { TFpDebugThreadSetRedirectConsoleOutputCommand }
64
65 TFpDebugThreadSetConsoleTtyCommand = class(TFpDebugThreadCommand)
66 private
67 FConsoleTty: String;
68 public
Executenull69 function Execute(AController: TFpServerDbgController; out DoProcessLoop: boolean): boolean; override;
TextNamenull70 class function TextName: string; override;
71 published
72 property ConsoleTty: String read FConsoleTty write FConsoleTty;
73 end;
74
75 { TFpDebugThreadRunCommand }
76
77 TFpDebugThreadRunCommand = class(TFpDebugThreadCommand)
78 public
Executenull79 function Execute(AController: TFpServerDbgController; out DoProcessLoop: boolean): boolean; override;
TextNamenull80 class function TextName: string; override;
81 end;
82
83 { TFpDebugThreadContinueCommand }
84
85 TFpDebugThreadContinueCommand = class(TFpDebugThreadCommand)
86 public
Executenull87 function Execute(AController: TFpServerDbgController; out DoProcessLoop: boolean): boolean; override;
TextNamenull88 class function TextName: string; override;
89 end;
90
91 { TFpDebugThreadNextCommand }
92
93 TFpDebugThreadNextCommand = class(TFpDebugThreadCommand)
94 public
Executenull95 function Execute(AController: TFpServerDbgController; out DoProcessLoop: boolean): boolean; override;
TextNamenull96 class function TextName: string; override;
97 end;
98
99 { TFpDebugThreadStepCommand }
100
101 TFpDebugThreadStepCommand = class(TFpDebugThreadCommand)
102 public
Executenull103 function Execute(AController: TFpServerDbgController; out DoProcessLoop: boolean): boolean; override;
TextNamenull104 class function TextName: string; override;
105 end;
106
107 { TFpDebugThreadStepOutCommand }
108
109 TFpDebugThreadStepOutCommand = class(TFpDebugThreadCommand)
110 public
Executenull111 function Execute(AController: TFpServerDbgController; out DoProcessLoop: boolean): boolean; override;
TextNamenull112 class function TextName: string; override;
113 end;
114
115 { TFpDebugThreadStepIntoInstrCommand }
116
117 TFpDebugThreadStepIntoInstrCommand = class(TFpDebugThreadCommand)
118 public
Executenull119 function Execute(AController: TFpServerDbgController; out DoProcessLoop: boolean): boolean; override;
TextNamenull120 class function TextName: string; override;
121 end;
122
123 { TFpDebugThreadStepOverInstrCommand }
124
125 TFpDebugThreadStepOverInstrCommand = class(TFpDebugThreadCommand)
126 public
Executenull127 function Execute(AController: TFpServerDbgController; out DoProcessLoop: boolean): boolean; override;
TextNamenull128 class function TextName: string; override;
129 end;
130
131 { TFpDebugThreadStopCommand }
132
133 TFpDebugThreadStopCommand = class(TFpDebugThreadCommand)
134 public
Executenull135 function Execute(AController: TFpServerDbgController; out DoProcessLoop: boolean): boolean; override;
TextNamenull136 class function TextName: string; override;
137 end;
138
139 { TFpDebugThreadAddBreakpointCommand }
140
141 TFpDebugThreadAddBreakpointCommand = class(TFpDebugThreadCommand)
142 private
143 FFileName: string;
144 FLine: integer;
145 FBreakPoint: TFpInternalBreakpoint;
146 FBreakServerId: Integer;
147 public
148 procedure ComposeSuccessEvent(var AnEvent: TFpDebugEvent); override;
Executenull149 function Execute(AController: TFpServerDbgController; out DoProcessLoop: boolean): boolean; override;
TextNamenull150 class function TextName: string; override;
151 published
152 property Filename: string read FFileName write FFileName;
153 property Line: integer read FLine write FLine;
154 end;
155
156 { TFpDebugThreadRemoveBreakpointCommand }
157
158 TFpDebugThreadRemoveBreakpointCommand = class(TFpDebugThreadCommand)
159 private
160 FBreakpointServerIdr: Integer;
161 public
Executenull162 function Execute(AController: TFpServerDbgController; out DoProcessLoop: boolean): boolean; override;
TextNamenull163 class function TextName: string; override;
164 published
165 property BreakpointServerIdr: Integer read FBreakpointServerIdr write FBreakpointServerIdr;
166 end;
167
168 { TFpDebugThreadGetLocationInfoCommand }
169
170 TFpDebugThreadGetLocationInfoCommand = class(TFpDebugThreadCommand)
171 private
172 FLocationRec: TDBGLocationRec;
173 FAddressValue: TDBGPtr;
GetAddressnull174 function GetAddress: string;
175 procedure SetAddress(AValue: string);
176 protected
177 procedure ComposeSuccessEvent(var AnEvent: TFpDebugEvent); override;
178 public
Executenull179 function Execute(AController: TFpServerDbgController; out DoProcessLoop: boolean): boolean; override;
TextNamenull180 class function TextName: string; override;
181 published
182 property Address: string read GetAddress write SetAddress;
183 end;
184
185 { TFpDebugThreadEvaluateCommand }
186
187 TFpDebugThreadEvaluateCommand = class(TFpDebugThreadCommand)
188 private
189 FExpression: string;
190 FResText: string;
191 FValidity: TDebuggerDataState;
192 public
Executenull193 function Execute(AController: TFpServerDbgController; out DoProcessLoop: boolean): boolean; override;
TextNamenull194 class function TextName: string; override;
195 procedure ComposeSuccessEvent(var AnEvent: TFpDebugEvent); override;
196 published
197 property Expression: string read FExpression write FExpression;
198 end;
199
200 { TFpDebugThreadStackTraceCommand }
201
202 TFpDebugThreadStackTraceCommand = class(TFpDebugThreadCommand)
203 private
204 FStackEntryArray: TFpDebugEventCallStackEntryArray;
205 public
Executenull206 function Execute(AController: TFpServerDbgController; out DoProcessLoop: boolean): boolean; override;
TextNamenull207 class function TextName: string; override;
208 procedure ComposeSuccessEvent(var AnEvent: TFpDebugEvent); override;
209 end;
210
211 { TFpDebugThreadDisassembleCommand }
212
213 TFpDebugThreadDisassembleCommand = class(TFpDebugThreadCommand)
214 private
215 FAddressValue: TDBGPtr;
216 FLinesAfter: integer;
217 FLinesBefore: integer;
218 FDisassemblerEntryArray: TFpDebugEventDisassemblerEntryArray;
219 FStartAddr: TDBGPtr;
220 FEndAddr: TDBGPtr;
221 FLastEntryEndAddr: TDBGPtr;
GetAddressnull222 function GetAddress: string;
223 procedure SetAddress(AValue: string);
224 {$ifndef disassemblernestedproc}
225 private
226 FController: TFpServerDbgController;
OnAdjustToKnowFunctionStartnull227 function OnAdjustToKnowFunctionStart(var AStartAddr: TDisassemblerAddress): Boolean;
OnDoDisassembleRangenull228 function OnDoDisassembleRange(AnEntryRanges: TDBGDisassemblerEntryMap; AFirstAddr, ALastAddr: TDisassemblerAddress; AStopAfterAddress: TDBGPtr; AStopAfterNumLines: Integer): Boolean;
229 {$endif}
230 public
231 constructor Create(AListenerIdentifier: integer; AnUID: variant; AOnLog: TOnLog); override;
Executenull232 function Execute(AController: TFpServerDbgController; out DoProcessLoop: boolean): boolean; override;
TextNamenull233 class function TextName: string; override;
234 procedure ComposeSuccessEvent(var AnEvent: TFpDebugEvent); override;
235 published
236 property Address: string read GetAddress write SetAddress;
237 property LinesAfter: integer read FLinesAfter write FLinesAfter;
238 property LinesBefore: integer read FLinesBefore write FLinesBefore;
239 end;
240
241 { TFpDebugLocalsCommand }
242
243 TFpDebugLocalsCommand = class(TFpDebugThreadCommand)
244 private
245 FWatchEntryArray: TFpDebugEventWatchEntryArray;
246 public
Executenull247 function Execute(AController: TFpServerDbgController; out DoProcessLoop: boolean): boolean; override;
TextNamenull248 class function TextName: string; override;
249 procedure ComposeSuccessEvent(var AnEvent: TFpDebugEvent); override;
250 end;
251
252 { TFpDebugRegistersCommand }
253
254 TFpDebugRegistersCommand = class(TFpDebugThreadCommand)
255 private
256 FWatchEntryArray: TFpDebugEventWatchEntryArray;
257 public
Executenull258 function Execute(AController: TFpServerDbgController; out DoProcessLoop: boolean): boolean; override;
TextNamenull259 class function TextName: string; override;
260 procedure ComposeSuccessEvent(var AnEvent: TFpDebugEvent); override;
261 end;
262
263 implementation
264
265 uses
266 FpDbgDisasX86;
267
268 { TFpDebugRegistersCommand }
269
Executenull270 function TFpDebugRegistersCommand.Execute(AController: TFpServerDbgController; out DoProcessLoop: boolean): boolean;
271 var
272 ARegisterList: TDbgRegisterValueList;
273 i: Integer;
274 begin
275 result := false;
276 if (AController = nil) or (AController.CurrentProcess = nil) or
277 (AController.CurrentProcess.DbgInfo = nil) then
278 exit;
279
280 ARegisterList := AController.CurrentProcess.MainThread.RegisterValueList;
281 SetLength(FWatchEntryArray, ARegisterList.Count);
282 for i := 0 to ARegisterList.Count-1 do
283 begin
284 FWatchEntryArray[i].Expression := ARegisterList[i].Name;
285 FWatchEntryArray[i].TextValue := ARegisterList[i].StrValue;
286 FWatchEntryArray[i].NumValue := ARegisterList[i].NumValue;
287 FWatchEntryArray[i].Size := ARegisterList[i].Size;
288 end;
289 result := true;
290 DoProcessLoop := false;
291 end;
292
TFpDebugRegistersCommand.TextNamenull293 class function TFpDebugRegistersCommand.TextName: string;
294 begin
295 result := 'registers';
296 end;
297
298 procedure TFpDebugRegistersCommand.ComposeSuccessEvent(var AnEvent: TFpDebugEvent);
299 begin
300 inherited ComposeSuccessEvent(AnEvent);
301 AnEvent.WatchEntryArray := FWatchEntryArray;
302 end;
303
304 { TFpDebugLocalsCommand }
305
Executenull306 function TFpDebugLocalsCommand.Execute(AController: TFpServerDbgController; out DoProcessLoop: boolean): boolean;
307 var
308 AContext: TFpDbgInfoContext;
309 ProcVal: TFpDbgValue;
310 i: Integer;
311 m: TFpDbgValue;
312 n, v: String;
313 Reg: TDBGPtr;
314 PrettyPrinter: TFpPascalPrettyPrinter;
315 begin
316 result := false;
317 if (AController = nil) or (AController.CurrentProcess = nil) or
318 (AController.CurrentProcess.DbgInfo = nil) then
319 exit;
320
321 Reg := AController.CurrentThread.GetInstructionPointerRegisterValue;
322 AContext := AController.CurrentProcess.DbgInfo.FindContext(AController.CurrentThread.ID, 0, Reg);
323
324 if (AContext = nil) or (AContext.SymbolAtAddress = nil) then
325 exit;
326
327 ProcVal := AContext.ProcedureAtAddress;
328
329 if (ProcVal = nil) then
330 exit;
331
332 PrettyPrinter := TFpPascalPrettyPrinter.Create(sizeof(pointer));
333 try
334 PrettyPrinter.AddressSize := AContext.SizeOfAddress;
335
336 SetLength(FWatchEntryArray, ProcVal.MemberCount);
337 for i := 0 to ProcVal.MemberCount - 1 do
338 begin
339 m := ProcVal.Member[i];
340 if m <> nil then
341 begin
342 if m.DbgSymbol <> nil then
343 n := m.DbgSymbol.Name
344 else
345 n := '';
346 PrettyPrinter.PrintValue(v, m);
347 FWatchEntryArray[i].TextValue := v;
348 FWatchEntryArray[i].Expression := n;
349 end;
350 end;
351 finally
352 PrettyPrinter.Free;
353 end;
354
355 AContext.ReleaseReference;
356 DoProcessLoop:=false;
357 result := true;
358 end;
359
TFpDebugLocalsCommand.TextNamenull360 class function TFpDebugLocalsCommand.TextName: string;
361 begin
362 result := 'locals';
363 end;
364
365 procedure TFpDebugLocalsCommand.ComposeSuccessEvent(var AnEvent: TFpDebugEvent);
366 begin
367 inherited ComposeSuccessEvent(AnEvent);
368 AnEvent.WatchEntryArray := FWatchEntryArray;
369 end;
370
371 { TFpDebugThreadDisassembleCommand }
372
GetAddressnull373 function TFpDebugThreadDisassembleCommand.GetAddress: string;
374 begin
375 result := FormatAddress(FAddressValue);
376 end;
377
378 procedure TFpDebugThreadDisassembleCommand.SetAddress(AValue: string);
379 begin
380 FAddressValue := Hex2Dec(AValue);
381 end;
382
383 constructor TFpDebugThreadDisassembleCommand.Create(AListenerIdentifier: integer; AnUID: variant; AOnLog: TOnLog);
384 begin
385 inherited Create(AListenerIdentifier, AnUID, AOnLog);
386 FLinesAfter:=10;
387 FLinesBefore:=5;
388 end;
389
390 {$ifdef disassemblernestedproc}
Executenull391 function TFpDebugThreadDisassembleCommand.Execute(AController: TFpServerDbgController; out DoProcessLoop: boolean): boolean;
392 {$endif}
393
394 function {$ifndef disassemblernestedproc}TFpDebugThreadDisassembleCommand.{$endif}OnAdjustToKnowFunctionStart(var AStartAddr: TDisassemblerAddress): Boolean;
395 var
396 Sym: TFpDbgSymbol;
397 begin
398 Sym := {$ifndef disassemblernestedproc}FController{$else}AController{$endif}.CurrentProcess.FindSymbol(AStartAddr.GuessedValue);
399 if assigned(Sym) and (Sym.Kind in [skProcedure, skFunction]) then
400 begin
401 AStartAddr.Value:=Sym.Address.Address;
402 AStartAddr.Offset:=0;
403 AStartAddr.Validity:=avFoundFunction;
resultnull404 result := true;
405 end
406 else
407 result := false;
408 end;
409
410 function {$ifndef disassemblernestedproc}TFpDebugThreadDisassembleCommand.{$endif}OnDoDisassembleRange(AnEntryRanges: TDBGDisassemblerEntryMap; AFirstAddr, ALastAddr: TDisassemblerAddress; AStopAfterAddress: TDBGPtr; AStopAfterNumLines: Integer): Boolean;
411
412 var
413 AnAddr: TDBGPtr;
414 CodeBin: array[0..20] of byte;
415 AnEntry: TDisassemblerEntry;
416 p: pointer;
417 ADump,
418 AStatement,
419 ASrcFileName: string;
420 ASrcFileLine: cardinal;
421 i,j: Integer;
422 Sym: TFpDbgSymbol;
423 StatIndex: integer;
424 FirstIndex: integer;
425 AResultList: TDBGDisassemblerEntryRange;
426
427 begin
428 result := false;
429 AResultList := TDBGDisassemblerEntryRange.Create;
430 AResultList.RangeStartAddr := AFirstAddr.Value;
431
432 Sym:=nil;
433 ASrcFileLine:=0;
434 ASrcFileName:='';
435 StatIndex:=0;
436 FirstIndex:=0;
437 AnEntry.Offset:=-1;
438 AnAddr:=AFirstAddr.Value;
439
440 i := 0;
441 while ((AStopAfterAddress=0) or (AStopAfterNumLines > -1)) and (AnAddr <= ALastAddr.Value) do
442 begin
443 AnEntry.Addr:=AnAddr;
444 if not {$ifndef disassemblernestedproc}FController{$else}AController{$endif}.CurrentProcess.ReadData(AnAddr, sizeof(CodeBin),CodeBin) then
445 begin
446 Log(Format('Disassemble: Failed to read memory at %s.', [FormatAddress(AnAddr)]), dllDebug);
447 AnEntry.Statement := 'Failed to read memory';
448 inc(AnAddr);
449 end
450 else
451 begin
452 p := @CodeBin;
453 FpDbgDisasX86.Disassemble(p, {$ifndef disassemblernestedproc}FController{$else}AController{$endif}.CurrentProcess.Mode=dm64, ADump, AStatement);
454
455 Sym := {$ifndef disassemblernestedproc}FController{$else}AController{$endif}.CurrentProcess.FindSymbol(AnAddr);
456
457 // If this is the last statement for this source-code-line, fill the
458 // SrcStatementCount from the prior statements.
459 if (assigned(sym) and ((ASrcFileName<>sym.FileName) or (ASrcFileLine<>sym.Line))) or
460 (not assigned(sym) and ((ASrcFileLine<>0) or (ASrcFileName<>''))) then
461 begin
462 for j := 0 to StatIndex-1 do
463 AResultList.EntriesPtr[FirstIndex+j]^.SrcStatementCount:=StatIndex;
464 StatIndex:=0;
465 FirstIndex:=i;
466 end;
467
468 if assigned(sym) then
469 begin
470 ASrcFileName:=sym.FileName;
471 ASrcFileLine:=sym.Line;
472 end
473 else
474 begin
475 ASrcFileName:='';
476 ASrcFileLine:=0;
477 end;
478 AnEntry.Dump := ADump;
479 AnEntry.Statement := AStatement;
480 AnEntry.SrcFileLine:=ASrcFileLine;
481 AnEntry.SrcFileName:=ASrcFileName;
482 AnEntry.SrcStatementIndex:=StatIndex;
483 inc(StatIndex);
484 AResultList.RangeEndAddr:=AnAddr;
485 Inc(AnAddr, {%H-}PtrUInt(p) - {%H-}PtrUInt(@CodeBin));
486 end;
487 AResultList.Append(@AnEntry);
488 if (AnAddr>AStopAfterAddress) then
489 dec(AStopAfterNumLines);
490 inc(i);
491 end;
492 AResultList.LastEntryEndAddr:=AnAddr;
493
494 if AResultList.Count>0 then
495 AnEntryRanges.AddRange(AResultList)
496 else
497 AResultList.Free;
498
499 result := true;
500 end;
501
502 {$ifndef disassemblernestedproc}
Executenull503 function TFpDebugThreadDisassembleCommand.Execute(AController: TFpServerDbgController; out DoProcessLoop: boolean): boolean;
504 {$endif disassemblernestedproc}
505
506 var
507 i: Integer;
508 DisassembleRangeExtender: TDBGDisassemblerRangeExtender;
509 DisassemblerEntryRange: TDBGDisassemblerEntryRange;
510 DisassemblerEntryRangeMap: TDBGDisassemblerEntryMap;
511 RangeIterator: TDBGDisassemblerEntryMapIterator;
512 ARange: TDBGDisassemblerEntryRange;
513
514 begin
515 {$ifndef disassemblernestedproc}
516 FController := AController;
517 {$endif}
518
519 result := false;
520 DoProcessLoop:=false;
521 if not assigned(AController.CurrentProcess) then
522 begin
523 log('Failed to dissasemble: No process', dllInfo);
524 exit;
525 end;
526
527 if FAddressValue=0 then
528 FStartAddr:=AController.CurrentThread.GetInstructionPointerRegisterValue
529 else
530 FStartAddr:=FAddressValue;
531
532 DisassemblerEntryRangeMap := TDBGDisassemblerEntryMap.Create(itu8, SizeOf(TDBGDisassemblerEntryRange));
533 try
534 DisassembleRangeExtender := TDBGDisassemblerRangeExtender.Create(DisassemblerEntryRangeMap);
535 try
536 DisassembleRangeExtender.OnDoDisassembleRange:=@OnDoDisassembleRange;
537 DisassembleRangeExtender.OnAdjustToKnowFunctionStart:=@OnAdjustToKnowFunctionStart;
538 DisassembleRangeExtender.DisassembleRange(FLinesBefore, FLinesAfter, FStartAddr, FStartAddr);
539 finally
540 DisassembleRangeExtender.Free;
541 end;
542
543 // Convert the DisassemblerEntryRangeMap to the FDisassemblerEntryArray
544 DisassemblerEntryRange := TDBGDisassemblerEntryRange.Create;
545 try
546 RangeIterator := TDBGDisassemblerEntryMapIterator.Create(DisassemblerEntryRangeMap);
547 try
548 RangeIterator.First;
549 RangeIterator.GetData(ARange);
550 repeat
551 DisassemblerEntryRange.Merge(ARange);
552
553 ARange := RangeIterator.NextRange;
554 until RangeIterator.EOM;
555
556 setlength(FDisassemblerEntryArray, DisassemblerEntryRange.Count);
557 for i := 0 to DisassemblerEntryRange.Count-1 do
558 begin
559 FDisassemblerEntryArray[i] := DisassemblerEntryRange.Entries[i];
560 end;
561 FStartAddr:=DisassemblerEntryRange.RangeStartAddr;
562 FEndAddr:=DisassemblerEntryRange.RangeEndAddr;
563 FLastEntryEndAddr:=DisassemblerEntryRange.LastEntryEndAddr;
564 finally
565 RangeIterator.Free;
566 end;
567 finally
568 DisassemblerEntryRange.Free;;
569 end;
570 finally
571 DisassemblerEntryRangeMap.Free;
572 end;
573
574 result := true;
575 end;
576
TFpDebugThreadDisassembleCommand.TextNamenull577 class function TFpDebugThreadDisassembleCommand.TextName: string;
578 begin
579 result := 'disassemble';
580 end;
581
582 procedure TFpDebugThreadDisassembleCommand.ComposeSuccessEvent(var AnEvent: TFpDebugEvent);
583 begin
584 inherited ComposeSuccessEvent(AnEvent);
585 AnEvent.DisassemblerEntryArray := FDisassemblerEntryArray;
586 AnEvent.Addr1:=FStartAddr;
587 AnEvent.Addr2:=FEndAddr;
588 AnEvent.Addr3:=FLastEntryEndAddr;
589 end;
590
591 { TFpDebugThreadSetConsoleTtyCommand }
592
Executenull593 function TFpDebugThreadSetConsoleTtyCommand.Execute(AController: TFpServerDbgController; out DoProcessLoop: boolean): boolean;
594 begin
595 AController.ConsoleTty:=FConsoleTty;
596 AController.RedirectConsoleOutput:=(AController.ConsoleTty='');
597 DoProcessLoop:=false;
598 result:=true;
599 end;
600
TFpDebugThreadSetConsoleTtyCommand.TextNamenull601 class function TFpDebugThreadSetConsoleTtyCommand.TextName: string;
602 begin
603 result := 'setconsoletty';
604 end;
605
606 { TFpDebugThreadStackTraceCommand }
607
TFpDebugThreadStackTraceCommand.Executenull608 function TFpDebugThreadStackTraceCommand.Execute(AController: TFpServerDbgController; out DoProcessLoop: boolean): boolean;
609 var
610 ThreadCallStack: TDbgCallstackEntryList;
611 i: integer;
612 begin
613 result := false;
614 DoProcessLoop:=false;
615 if not assigned(AController.CurrentProcess) then
616 begin
617 log('Failed to get call stack: No process', dllInfo);
618 exit;
619 end;
620
621 AController.CurrentProcess.MainThread.PrepareCallStackEntryList;
622 ThreadCallStack := AController.CurrentProcess.MainThread.CallStackEntryList;
623 SetLength(FStackEntryArray, ThreadCallStack.Count);
624 for i := 0 to ThreadCallStack.Count-1 do
625 begin
626 FStackEntryArray[i].AnAddress:=ThreadCallStack[i].AnAddress;
627 FStackEntryArray[i].FrameAdress:=ThreadCallStack[i].FrameAdress;
628 FStackEntryArray[i].FunctionName:=ThreadCallStack[i].FunctionName+ThreadCallStack[i].GetParamsAsString;
629 FStackEntryArray[i].Line:=ThreadCallStack[i].Line;
630 FStackEntryArray[i].SourceFile:=ThreadCallStack[i].SourceFile;
631 end;
632 // Clear the callstack immediately. Doing this each time the process continous is
633 // cumbersome. And the chances that this command is called twice, so that
634 // caching the result is usefull, are slim.
635 AController.CurrentProcess.MainThread.ClearCallStack;
636 result := true;
637 end;
638
639
TFpDebugThreadStackTraceCommand.TextNamenull640 class function TFpDebugThreadStackTraceCommand.TextName: string;
641 begin
642 result := 'stacktrace';
643 end;
644
645 procedure TFpDebugThreadStackTraceCommand.ComposeSuccessEvent(var AnEvent: TFpDebugEvent);
646 begin
647 inherited ComposeSuccessEvent(AnEvent);
648 AnEvent.StackEntryArray:=FStackEntryArray;
649 end;
650
651 { TFpDebugThreadEvaluateCommand }
652
653 procedure TFpDebugThreadEvaluateCommand.ComposeSuccessEvent(var AnEvent: TFpDebugEvent);
654 begin
655 inherited ComposeSuccessEvent(AnEvent);
656 AnEvent.Message:=FResText;
657 AnEvent.Validity:=FValidity;
658 end;
659
TFpDebugThreadEvaluateCommand.Executenull660 function TFpDebugThreadEvaluateCommand.Execute(AController: TFpServerDbgController; out DoProcessLoop: boolean): boolean;
661 var
662 AContext: TFpDbgInfoContext;
663 APasExpr: TFpPascalExpression;
664 ADbgInfo: TDbgInfo;
665 Res: Boolean;
666 APrettyPrinter: TFpPascalPrettyPrinter;
667 ATypeInfo: TDBGType;
668
669 begin
670 Result := False;
671 DoProcessLoop:=false;
672 if not assigned(AController.CurrentProcess) then
673 begin
674 log('Failed to evaluate expression: No process', dllInfo);
675 exit;
676 end;
677
678 ADbgInfo := AController.CurrentProcess.DbgInfo;
679 AContext := ADbgInfo.FindContext(AController.CurrentThread.ID, 0, AController.CurrentThread.GetInstructionPointerRegisterValue);
680 if AContext = nil then
681 begin
682 FValidity:=ddsInvalid;
683 exit;
684 end;
685
686 Result := True;
687 AContext.MemManager.DefaultContext := AContext;
688 APasExpr := TFpPascalExpression.Create(FExpression, AContext);
689 try
690 APasExpr.ResultValue; // trigger full validation
691 if not APasExpr.Valid then
692 begin
693 FResText := ErrorHandler.ErrorAsString(APasExpr.Error);
694 FValidity := ddsError;
695 end
696 else
697 begin
698 APrettyPrinter := TFpPascalPrettyPrinter.Create(sizeof(pointer));
699 try
700 APrettyPrinter.AddressSize:=AContext.SizeOfAddress;
701 APrettyPrinter.MemManager := AContext.MemManager;
702 Res := APrettyPrinter.PrintValue(FResText, ATypeInfo, APasExpr.ResultValue);
703 if Res then
704 begin
705 FValidity:=ddsValid;
706 end
707 else
708 begin
709 FResText := 'Error';
710 FValidity:=ddsValid;
711 end;
712 finally
713 APrettyPrinter.Free;
714 end;
715 end;
716 finally
717 APasExpr.Free;
718 AContext.ReleaseReference;
719 end;
720 end;
721
TFpDebugThreadEvaluateCommand.TextNamenull722 class function TFpDebugThreadEvaluateCommand.TextName: string;
723 begin
724 result := 'evaluate';
725 end;
726
727 { TFpDebugThreadQuitDebugServerCommand }
728
TFpDebugThreadQuitDebugServerCommand.PreExecutenull729 function TFpDebugThreadQuitDebugServerCommand.PreExecute(AController: TFpServerDbgController; out DoQueueCommand: boolean): boolean;
730 begin
731 DoQueueCommand:=false;
732 CustomApplication.Terminate;
733 result := true;
734 end;
735
Executenull736 function TFpDebugThreadQuitDebugServerCommand.Execute(AController: TFpServerDbgController; out DoProcessLoop: boolean): boolean;
737 begin
738 result := true;
739 DoProcessLoop := false;
740 end;
741
TFpDebugThreadQuitDebugServerCommand.TextNamenull742 class function TFpDebugThreadQuitDebugServerCommand.TextName: string;
743 begin
744 result := 'quitdebugserver';
745 end;
746
747 { TFpDebugThreadRemoveBreakpointCommand }
748
TFpDebugThreadRemoveBreakpointCommand.Executenull749 function TFpDebugThreadRemoveBreakpointCommand.Execute(AController: TFpServerDbgController; out DoProcessLoop: boolean): boolean;
750 var
751 Brk: TFpInternalBreakpoint;
752 begin
753 result := false;
754 DoProcessLoop:=false;
755 if not assigned(AController.CurrentProcess) then
756 begin
757 log('Failed to remove breakpoint: No process', dllInfo);
758 exit;
759 end;
760 if (FBreakpointServerIdr<>0) then begin
761 Brk := AController.GetInternalBreakPointFromId(FBreakpointServerIdr);
762 result := AController.CurrentProcess.RemoveBreak(Brk);
763 Brk.Free; // actually removes it from target process
764 AController.RemoveInternalBreakPoint(FBreakpointServerIdr);
765 end
766 else
767 log('Failed to remove breakpoint: No location given', dllInfo);
768 end;
769
TFpDebugThreadRemoveBreakpointCommand.TextNamenull770 class function TFpDebugThreadRemoveBreakpointCommand.TextName: string;
771 begin
772 result := 'removebreakpoint';
773 end;
774
775 { TFpDebugThreadStopCommand }
776
Executenull777 function TFpDebugThreadStopCommand.Execute(AController: TFpServerDbgController; out
778 DoProcessLoop: boolean): boolean;
779 begin
780 AController.Stop;
781 DoProcessLoop:=true;
782 result := true;
783 end;
784
TFpDebugThreadStopCommand.TextNamenull785 class function TFpDebugThreadStopCommand.TextName: string;
786 begin
787 result := 'stop';
788 end;
789
790 { TFpDebugThreadStepOutCommand }
791
Executenull792 function TFpDebugThreadStepOutCommand.Execute(AController: TFpServerDbgController; out
793 DoProcessLoop: boolean): boolean;
794 begin
795 AController.StepOut;
796 DoProcessLoop:=true;
797 result := true;
798 end;
799
TFpDebugThreadStepOutCommand.TextNamenull800 class function TFpDebugThreadStepOutCommand.TextName: string;
801 begin
802 result := 'stepout';
803 end;
804
805 { TFpDebugThreadStepOverInstrCommand }
806
Executenull807 function TFpDebugThreadStepOverInstrCommand.Execute(
808 AController: TFpServerDbgController; out DoProcessLoop: boolean): boolean;
809 begin
810 AController.StepOverInstr;
811 DoProcessLoop:=true;
812 result := true;
813 end;
814
TFpDebugThreadStepOverInstrCommand.TextNamenull815 class function TFpDebugThreadStepOverInstrCommand.TextName: string;
816 begin
817 result := 'stepoverinstr';
818 end;
819
820 { TFpDebugThreadStepIntoInstrCommand }
821
TFpDebugThreadStepIntoInstrCommand.Executenull822 function TFpDebugThreadStepIntoInstrCommand.Execute(
823 AController: TFpServerDbgController; out DoProcessLoop: boolean): boolean;
824 begin
825 AController.StepIntoInstr;
826 DoProcessLoop:=true;
827 result := true;
828 end;
829
TFpDebugThreadStepIntoInstrCommand.TextNamenull830 class function TFpDebugThreadStepIntoInstrCommand.TextName: string;
831 begin
832 result := 'stepintoinstr';
833 end;
834
835 { TFpDebugThreadStepCommand }
836
Executenull837 function TFpDebugThreadStepCommand.Execute(AController: TFpServerDbgController; out
838 DoProcessLoop: boolean): boolean;
839 begin
840 AController.Step;
841 DoProcessLoop:=true;
842 result := true;
843 end;
844
TFpDebugThreadStepCommand.TextNamenull845 class function TFpDebugThreadStepCommand.TextName: string;
846 begin
847 result := 'step';
848 end;
849
850 { TFpDebugThreadNextCommand }
851
Executenull852 function TFpDebugThreadNextCommand.Execute(AController: TFpServerDbgController; out
853 DoProcessLoop: boolean): boolean;
854 begin
855 AController.Next;
856 DoProcessLoop:=true;
857 result := true;
858 end;
859
TFpDebugThreadNextCommand.TextNamenull860 class function TFpDebugThreadNextCommand.TextName: string;
861 begin
862 result := 'next';
863 end;
864
865 { TFpDebugThreadGetLocationInfoCommand }
866
TFpDebugThreadGetLocationInfoCommand.GetAddressnull867 function TFpDebugThreadGetLocationInfoCommand.GetAddress: string;
868 begin
869 result := FormatAddress(FAddressValue);
870 end;
871
872 procedure TFpDebugThreadGetLocationInfoCommand.SetAddress(AValue: string);
873 begin
874 FAddressValue := Hex2Dec(AValue);
875 end;
876
877 procedure TFpDebugThreadGetLocationInfoCommand.ComposeSuccessEvent(var AnEvent: TFpDebugEvent);
878 begin
879 inherited ComposeSuccessEvent(AnEvent);
880 AnEvent.LocationRec:=FLocationRec;
881 end;
882
Executenull883 function TFpDebugThreadGetLocationInfoCommand.Execute(AController: TFpServerDbgController; out DoProcessLoop: boolean): boolean;
884 var
885 sym, symproc: TFpDbgSymbol;
886 begin
887 DoProcessLoop:=false;
888 result := false;
889
890 if not assigned(AController.CurrentProcess) then
891 begin
892 log('Failed to get location info: No process', dllInfo);
893 exit;
894 end
895 else
896 begin
897 FLocationRec.FuncName:='';
898 FLocationRec.SrcFile:='';
899 FLocationRec.SrcFullName:='';
900 FLocationRec.SrcLine:=0;
901
902 if FAddressValue=0 then
903 FLocationRec.Address := AController.CurrentThread.GetInstructionPointerRegisterValue
904 else
905 FLocationRec.Address := FAddressValue;
906
907 sym := AController.CurrentProcess.FindSymbol(FLocationRec.Address);
908 if sym = nil then
909 Exit;
910
911 FLocationRec.SrcFile := ExtractFileName(sym.FileName);
912 FLocationRec.SrcLine := sym.Line;
913 FLocationRec.SrcFullName := sym.FileName;
914
915 symproc := sym;
916 while not (symproc.kind in [skProcedure, skFunction]) do
917 symproc := symproc.Parent;
918
919 if assigned(symproc) then
920 FLocationRec.FuncName:=symproc.Name;
921 sym.free;
922 result := true;
923 end;
924 end;
925
TFpDebugThreadGetLocationInfoCommand.TextNamenull926 class function TFpDebugThreadGetLocationInfoCommand.TextName: string;
927 begin
928 result := 'getlocationinfo'
929 end;
930
931 { TFpDebugThreadAddBreakpointCommand }
932
933 procedure TFpDebugThreadAddBreakpointCommand.ComposeSuccessEvent(var AnEvent: TFpDebugEvent);
934 begin
935 inherited ComposeSuccessEvent(AnEvent);
936 AnEvent.BreakpointServerIdr:=FBreakServerId;
937 end;
938
TFpDebugThreadAddBreakpointCommand.Executenull939 function TFpDebugThreadAddBreakpointCommand.Execute(AController: TFpServerDbgController; out DoProcessLoop: boolean): boolean;
940 begin
941 result := false;
942 FBreakServerId := 0;
943 DoProcessLoop:=false;
944 if not assigned(AController.CurrentProcess) then
945 begin
946 log('Failed to add breakpoint: No process', dllInfo);
947 exit;
948 end;
949 if (Filename<>'') and (line>-1) then
950 begin
951 FBreakPoint := AController.CurrentProcess.AddBreak(FileName, Line);
952 result := assigned(FBreakPoint);
953 if Result then
954 FBreakServerId := AController.AddInternalBreakPointToId(FBreakPoint);
955 end
956 else
957 log('Failed to add breakpoint: No filename and line-number given', dllInfo);
958 end;
959
TFpDebugThreadAddBreakpointCommand.TextNamenull960 class function TFpDebugThreadAddBreakpointCommand.TextName: string;
961 begin
962 result := 'addbreakpoint';
963 end;
964
965 { TFpDebugThreadCommandList }
966
967 var
968 GFpDebugThreadCommandList: TFpDebugThreadCommandList = nil;
969
TFpDebugThreadCommandList.instancenull970 class function TFpDebugThreadCommandList.instance: TFpDebugThreadCommandList;
971 begin
972 if not assigned(GFpDebugThreadCommandList) then
973 GFpDebugThreadCommandList := TFpDebugThreadCommandList.Create;
974 result := GFpDebugThreadCommandList;
975 end;
976
GetCommandByNamenull977 function TFpDebugThreadCommandList.GetCommandByName(ATextName: string): TFpDebugThreadCommandClass;
978 var
979 i: Integer;
980 begin
981 result := nil;
982 for i := 0 to count -1 do
983 begin
984 if TFpDebugThreadCommandClass(Items[i]).TextName=ATextName then
985 result := TFpDebugThreadCommandClass(Items[i]);
986 end;
987 end;
988
989 { TFpDebugThreadContinueCommand }
990
Executenull991 function TFpDebugThreadContinueCommand.Execute(AController: TFpServerDbgController; out
992 DoProcessLoop: boolean): boolean;
993 begin
994 DoProcessLoop:=true;
995 result := true;
996 end;
997
TFpDebugThreadContinueCommand.TextNamenull998 class function TFpDebugThreadContinueCommand.TextName: string;
999 begin
1000 result := 'continue';
1001 end;
1002
1003 { TFpDebugThreadRunCommand }
1004
Executenull1005 function TFpDebugThreadRunCommand.Execute(AController: TFpServerDbgController; out
1006 DoProcessLoop: boolean): boolean;
1007 begin
1008 DoProcessLoop := AController.Run;
1009 result := DoProcessLoop;
1010 end;
1011
TFpDebugThreadRunCommand.TextNamenull1012 class function TFpDebugThreadRunCommand.TextName: string;
1013 begin
1014 result := 'run';
1015 end;
1016
1017 { TFpDebugThreadSetFilenameCommand }
1018
Executenull1019 function TFpDebugThreadSetFilenameCommand.Execute(AController: TFpServerDbgController;
1020 out DoProcessLoop: boolean): boolean;
1021 begin
1022 AController.ExecutableFilename:=FFileName;
1023 DoProcessLoop:=false;
1024 result:=true;
1025 end;
1026
TFpDebugThreadSetFilenameCommand.TextNamenull1027 class function TFpDebugThreadSetFilenameCommand.TextName: string;
1028 begin
1029 result := 'filename'
1030 end;
1031
1032 initialization
1033 TFpDebugThreadCommandList.instance.Add(TFpDebugThreadQuitDebugServerCommand);
1034 TFpDebugThreadCommandList.instance.Add(TFpDebugThreadSetFilenameCommand);
1035 TFpDebugThreadCommandList.instance.Add(TFpDebugThreadSetConsoleTtyCommand);
1036 TFpDebugThreadCommandList.instance.Add(TFpDebugThreadRunCommand);
1037 TFpDebugThreadCommandList.instance.Add(TFpDebugThreadContinueCommand);
1038 TFpDebugThreadCommandList.instance.Add(TFpDebugThreadStepOverInstrCommand);
1039 TFpDebugThreadCommandList.instance.Add(TFpDebugThreadStepIntoInstrCommand);
1040 TFpDebugThreadCommandList.instance.Add(TFpDebugThreadNextCommand);
1041 TFpDebugThreadCommandList.instance.Add(TFpDebugThreadStepCommand);
1042 TFpDebugThreadCommandList.instance.Add(TFpDebugThreadStepOutCommand);
1043 TFpDebugThreadCommandList.instance.Add(TFpDebugThreadStopCommand);
1044 TFpDebugThreadCommandList.instance.Add(TFpDebugThreadAddBreakpointCommand);
1045 TFpDebugThreadCommandList.instance.Add(TFpDebugThreadRemoveBreakpointCommand);
1046 TFpDebugThreadCommandList.instance.Add(TFpDebugThreadGetLocationInfoCommand);
1047 TFpDebugThreadCommandList.instance.Add(TFpDebugThreadEvaluateCommand);
1048 TFpDebugThreadCommandList.instance.Add(TFpDebugThreadStackTraceCommand);
1049 TFpDebugThreadCommandList.instance.Add(TFpDebugThreadDisassembleCommand);
1050 TFpDebugThreadCommandList.instance.Add(TFpDebugLocalsCommand);
1051 TFpDebugThreadCommandList.instance.Add(TFpDebugRegistersCommand);
1052 finalization
1053 GFpDebugThreadCommandList.Free;
1054 end.
1055
1056