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