1 {
2  ---------------------------------------------------------------------------
3  FpDebugDebuggerWorkThreads
4  ---------------------------------------------------------------------------
5 
6  ***************************************************************************
7  *                                                                         *
8  *   This source is free software; you can redistribute it and/or modify   *
9  *   it under the terms of the GNU General Public License as published by  *
10  *   the Free Software Foundation; either version 2 of the License, or     *
11  *   (at your option) any later version.                                   *
12  *                                                                         *
13  *   This code is distributed in the hope that it will be useful, but      *
14  *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
15  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
16  *   General Public License for more details.                              *
17  *                                                                         *
18  *   A copy of the GNU General Public License is available on the World    *
19  *   Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also      *
20  *   obtain it by writing to the Free Software Foundation,                 *
21  *   Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA.   *
22  *                                                                         *
23  ***************************************************************************
24 }
25 
26 unit FpDebugDebuggerWorkThreads;
27 
28 (*
29   This unit contains the classes for executing work in the worker thread:
30   - The general structure of the classes
31   - The code that is to be executed in the worker thread
32       procedure DoExecute;
33 
34   - The classes are extended in the main FpDebugDebugger unit with any code
35     running in the main debugger thread.
36 
37   This split accross the units should help with identifying what may be accessed
38   in the worker thread.
39 *)
40 
41 {$mode objfpc}{$H+}
42 {$TYPEDADDRESS on}
43 {$ModeSwitch advancedrecords}
44 
45 interface
46 
47 uses
48   FpDebugDebuggerUtils, DbgIntfDebuggerBase, DbgIntfBaseTypes, FpDbgClasses,
49   FpDbgUtil, FPDbgController, FpPascalBuilder, FpdMemoryTools, FpDbgInfo,
50   FpPascalParser, FpErrorMessages, FpDbgCallContextInfo, FpDbgDwarf,
51   FpDbgDwarfDataClasses, Forms, fgl, math, Classes, sysutils, {$ifdef FORCE_LAZLOGGER_DUMMY} LazLoggerDummy {$else} LazLoggerBase {$endif};
52 
53 type
54 
55   TFpDbgAsyncMethod = procedure() of object;
56 
57   TFpDebugDebuggerBase = class(TDebuggerIntf)
58   protected
59     FDbgController: TDbgController;
60     FMemManager: TFpDbgMemManager;
61     FMemReader: TDbgMemReader;
62     FMemConverter: TFpDbgMemConvertorLittleEndian;
63     FLockList: TFpDbgLockList;
64     FWorkQueue: TFpThreadPriorityWorkerQueue;
65   end;
66 
67   { TFpDbgDebggerThreadWorkerItem }
68 
69   TFpDbgDebggerThreadWorkerItem = class(TFpThreadPriorityWorkerItem)
70   protected type
71     THasQueued = (hqNotQueued, hqQueued, hqBlocked);
72   protected
73     FDebugger: TFpDebugDebuggerBase;
74     FHasQueued: THasQueued;
75   public
76     constructor Create(ADebugger: TFpDebugDebuggerBase; APriority: TFpThreadWorkerPriority);
77 
78     procedure Queue(aMethod: TDataEvent; Data: PtrInt = 0);
79     (* Unqueue_DecRef also prevents new queuing
80        Unqueue_DecRef allows for destruction (no more access to object)
81        => therefor UnQueue_DecRef and ALL/most methods executing  unqueue_DecRef are named *_DecRef
82     *)
83     procedure UnQueue_DecRef(ABlockQueuing: Boolean = True);
84   end;
85 
86   { TFpDbgDebggerThreadWorkerLinkedItem }
87 
88   TFpDbgDebggerThreadWorkerLinkedItem = class(TFpDbgDebggerThreadWorkerItem)
89   protected
90     FNextWorker: TFpDbgDebggerThreadWorkerLinkedItem; // linked list for use by TFPCallStackSupplier
91     procedure DoRemovedFromLinkedList; virtual;
92   end;
93 
94   { TFpDbgDebggerThreadWorkerLinkedList }
95 
96   TFpDbgDebggerThreadWorkerLinkedList = object
97   private
98     FNextWorker: TFpDbgDebggerThreadWorkerLinkedItem;
99     FLocked: Boolean;
100   public
101     procedure Add(AWorkItem: TFpDbgDebggerThreadWorkerLinkedItem); // Does not add ref / uses existing ref
102     procedure ClearFinishedWorkers;
103     procedure RequestStopForWorkers;
104     procedure WaitForWorkers(AStop: Boolean); // Only call in IDE thread (main thread)
105   end;
106 
107   { TFpThreadWorkerControllerRun }
108 
109   TFpThreadWorkerControllerRun = class(TFpDbgDebggerThreadWorkerItem)
110   private
111     FWorkerThreadId: TThreadID;
112   protected
113     FStartSuccessfull: boolean;
114     procedure DoExecute; override;
115   public
116     constructor Create(ADebugger: TFpDebugDebuggerBase);
117     property StartSuccesfull: boolean read FStartSuccessfull;
118     property WorkerThreadId: TThreadID read FWorkerThreadId;
119   end;
120 
121   { TFpThreadWorkerRunLoop }
122 
123   TFpThreadWorkerRunLoop = class(TFpDbgDebggerThreadWorkerItem)
124   protected
125     procedure LoopFinished_DecRef(Data: PtrInt = 0); virtual; abstract;
126     procedure DoExecute; override;
127   public
128     constructor Create(ADebugger: TFpDebugDebuggerBase);
129   end;
130 
131   { TFpThreadWorkerRunLoopAfterIdle }
132 
133   TFpThreadWorkerRunLoopAfterIdle = class(TFpDbgDebggerThreadWorkerItem)
134   protected
135     procedure CheckIdleOrRun_DecRef(Data: PtrInt = 0); virtual; abstract;
136     procedure DoExecute; override;
137   public
138     constructor Create(ADebugger: TFpDebugDebuggerBase);
139   end;
140 
141   { TFpThreadWorkerAsyncMeth }
142 
143   TFpThreadWorkerAsyncMeth = class(TFpDbgDebggerThreadWorkerItem)
144   protected
145     FAsyncMethod: TFpDbgAsyncMethod;
146     procedure DoExecute; override;
147   public
148     constructor Create(ADebugger: TFpDebugDebuggerBase; AnAsyncMethod: TFpDbgAsyncMethod);
149   end;
150 
151   { TFpThreadWorkerPrepareCallStackEntryList }
152 
153   TFpThreadWorkerPrepareCallStackEntryList = class(TFpDbgDebggerThreadWorkerLinkedItem)
154   (* Do not accesss   CallStackEntryList.Items[]   while this is running *)
155   protected
156     FRequiredMinCount: Integer;
157     FThread: TDbgThread;
158     procedure PrepareCallStackEntryList(AFrameRequired: Integer; AThread: TDbgThread);
159     procedure DoExecute; override;
160   public
161     constructor Create(ADebugger: TFpDebugDebuggerBase; ARequiredMinCount: Integer; APriority: TFpThreadWorkerPriority = twpStack);
162     constructor Create(ADebugger: TFpDebugDebuggerBase; ARequiredMinCount: Integer; AThread: TDbgThread);
163   end;
164 
165   { TFpThreadWorkerCallStackCount }
166 
167   TFpThreadWorkerCallStackCount = class(TFpThreadWorkerPrepareCallStackEntryList)
168   protected
169     procedure UpdateCallstack_DecRef(Data: PtrInt = 0); virtual; abstract;
170     procedure DoExecute; override;
171   end;
172 
173   { TFpThreadWorkerCallEntry }
174 
175   TFpThreadWorkerCallEntry = class(TFpThreadWorkerPrepareCallStackEntryList)
176   protected
177     FCallstackIndex: Integer;
178     FDbgCallStack: TDbgCallstackEntry;
179     FParamAsString: String;
180     procedure UpdateCallstackEntry_DecRef(Data: PtrInt = 0); virtual; abstract;
181     procedure DoExecute; override;
182   end;
183 
184   { TFpThreadWorkerThreads }
185 
186   TFpThreadWorkerThreads = class(TFpThreadWorkerPrepareCallStackEntryList)
187   protected
188     procedure UpdateThreads_DecRef(Data: PtrInt = 0); virtual; abstract;
189     procedure DoExecute; override;
190   public
191     constructor Create(ADebugger: TFpDebugDebuggerBase);
192   end;
193 
194   { TFpThreadWorkerLocals }
195 
196   TFpThreadWorkerLocals = class(TFpDbgDebggerThreadWorkerLinkedItem)
197   protected type
198     TResultEntry = record
199       Name, Value: String;
200       class operator = (a, b: TResultEntry): Boolean;
201     end;
202     TResultList = specialize TFPGList<TResultEntry>;
203   protected
204     FThreadId, FStackFrame: Integer;
205     FResults: TResultList;
206     procedure UpdateLocals_DecRef(Data: PtrInt = 0); virtual; abstract;
207     procedure DoExecute; override;
208   public
209     destructor Destroy; override;
210   end;
211 
212   { TFpThreadWorkerModify }
213 
214   TFpThreadWorkerModify = class(TFpDbgDebggerThreadWorkerLinkedItem)
215   private
216     FExpression, FNewVal: String;
217     FStackFrame, FThreadId: Integer;
218     FSuccess: Boolean;
219   protected
220     procedure DoCallback_DecRef(Data: PtrInt = 0); virtual; abstract;
221     procedure DoExecute; override;
222     property Success: Boolean read FSuccess;
223   public
224     constructor Create(ADebugger: TFpDebugDebuggerBase;
225                        //APriority: TFpThreadWorkerPriority;
226                        const AnExpression, ANewValue: String;
227                        AStackFrame, AThreadId: Integer
228                       );
DebugTextnull229     function DebugText: String; override;
230   end;
231 
232   { TFpThreadWorkerEvaluate }
233 
234   TFpThreadWorkerEvaluate = class(TFpDbgDebggerThreadWorkerLinkedItem)
235   private
236     FAllowFunctions: Boolean;
237     FExpressionScope: TFpDbgSymbolScope;
238 
DoWatchFunctionCallnull239     function DoWatchFunctionCall(AnExpressionPart: TFpPascalExpressionPart;
240       AFunctionValue, ASelfValue: TFpValue; AParams: TFpPascalExpressionPartList;
241       out AResult: TFpValue; var AnError: TFpError): boolean;
242   protected
EvaluateExpressionnull243     function EvaluateExpression(const AnExpression: String;
244                                 AStackFrame, AThreadId: Integer;
245                                 ADispFormat: TWatchDisplayFormat;
246                                 ARepeatCnt: Integer;
247                                 AnEvalFlags: TDBGEvaluateFlags;
248                                 out AResText: String;
249                                 out ATypeInfo: TDBGType
250                                ): Boolean;
251   public
252   end;
253 
254   { TFpThreadWorkerEvaluateExpr }
255 
256   TFpThreadWorkerEvaluateExpr = class(TFpThreadWorkerEvaluate)
257   private
258     FExpression: String;
259     FStackFrame, FThreadId: Integer;
260     FDispFormat: TWatchDisplayFormat;
261     FRepeatCnt: Integer;
262     FEvalFlags: TDBGEvaluateFlags;
263   protected
264     FRes: Boolean;
265     FResText: String;
266     FResDbgType: TDBGType;
267     procedure DoExecute; override;
268   public
269     constructor Create(ADebugger: TFpDebugDebuggerBase;
270                        APriority: TFpThreadWorkerPriority;
271                        const AnExpression: String;
272                        AStackFrame, AThreadId: Integer;
273                        ADispFormat: TWatchDisplayFormat;
274                        ARepeatCnt: Integer;
275                        AnEvalFlags: TDBGEvaluateFlags
276                       );
DebugTextnull277     function DebugText: String; override;
278   end;
279 
280   { TFpThreadWorkerCmdEval }
281 
282   TFpThreadWorkerCmdEval = class(TFpThreadWorkerEvaluateExpr)
283   protected
284     FCallback: TDBGEvaluateResultCallback;
285     procedure DoCallback_DecRef(Data: PtrInt = 0);
286     procedure DoExecute; override;
287   public
288     constructor Create(ADebugger: TFpDebugDebuggerBase;
289                        APriority: TFpThreadWorkerPriority;
290                        const AnExpression: String;
291                        AStackFrame, AThreadId: Integer;
292                        AnEvalFlags: TDBGEvaluateFlags;
293                        ACallback: TDBGEvaluateResultCallback
294                       );
295     destructor Destroy; override;
296     procedure Abort;
297   end;
298 
299   { TFpThreadWorkerWatchValueEval }
300 
301   TFpThreadWorkerWatchValueEval = class(TFpThreadWorkerEvaluateExpr)
302   protected
303     procedure UpdateWatch_DecRef(Data: PtrInt = 0); virtual; abstract;
304     procedure DoExecute; override;
305   end;
306 
307   { TFpThreadWorkerBreakPoint }
308 
309   TFpThreadWorkerBreakPoint = class(TFpDbgDebggerThreadWorkerItem)
310   public
311     procedure RemoveBreakPoint_DecRef; virtual;
312     procedure AbortSetBreak; virtual;
313   end;
314 
315   { TFpThreadWorkerBreakPointSet }
316 
317   TFpThreadWorkerBreakPointSet = class(TFpThreadWorkerBreakPoint)
318   private
319     FInternalBreakpoint: FpDbgClasses.TFpDbgBreakpoint;
320     FKind: TDBGBreakPointKind;
321     FAddress: TDBGPtr;
322     FSource: String;
323     FLine: Integer;
324     FStackFrame, FThreadId: Integer;
325     FWatchData: String;
326     FWatchScope: TDBGWatchPointScope;
327     FWatchKind: TDBGWatchPointKind;
328   protected
329     FResetBreakPoint: Boolean;
330     procedure UpdateBrkPoint_DecRef(Data: PtrInt = 0); virtual; abstract;
331     procedure DoExecute; override;
332   public
333     constructor Create(ADebugger: TFpDebugDebuggerBase; AnAddress: TDBGPtr);
334     constructor Create(ADebugger: TFpDebugDebuggerBase; ASource: String; ALine: Integer);
335     constructor Create(ADebugger: TFpDebugDebuggerBase;
336       AWatchData: String; AWatchScope: TDBGWatchPointScope; AWatchKind: TDBGWatchPointKind;
337       AStackFrame, AThreadId: Integer);
338     property InternalBreakpoint: FpDbgClasses.TFpDbgBreakpoint read FInternalBreakpoint;
339   end;
340 
341   { TFpThreadWorkerBreakPointRemove }
342 
343   TFpThreadWorkerBreakPointRemove = class(TFpThreadWorkerBreakPoint)
344   protected
345     FInternalBreakpoint: FpDbgClasses.TFpDbgBreakpoint;
346     procedure DoExecute; override;
347   public
348     constructor Create(ADebugger: TFpDebugDebuggerBase; AnInternalBreakpoint: FpDbgClasses.TFpDbgBreakpoint);
349     property InternalBreakpoint: FpDbgClasses.TFpDbgBreakpoint read FInternalBreakpoint;
350   end;
351 
352 implementation
353 
354 { TFpDbgDebggerThreadWorkerItem }
355 
356 constructor TFpDbgDebggerThreadWorkerItem.Create(ADebugger: TFpDebugDebuggerBase;
357   APriority: TFpThreadWorkerPriority);
358 begin
359   inherited Create(APriority);
360   FDebugger := ADebugger;
361   AddRef;
362 end;
363 
364 procedure TFpDbgDebggerThreadWorkerItem.Queue(aMethod: TDataEvent; Data: PtrInt
365   );
366 begin
367   FDebugger.FLockList.Lock;
368   try
369     if (FHasQueued <> hqBlocked) then begin
370       assert(FHasQueued = hqNotQueued, 'TFpDbgDebggerThreadWorkerItem.Queue: FHasQueued = hqNotQueued');
371       FHasQueued := hqQueued;
372       AddRef;
373       Application.QueueAsyncCall(aMethod, 0);
374     end;
375   finally
376     FDebugger.FLockList.UnLock;
377   end;
378 end;
379 
380 procedure TFpDbgDebggerThreadWorkerItem.UnQueue_DecRef(ABlockQueuing: Boolean);
381 var
382   HasQ: THasQueued;
383 begin
384   FDebugger.FLockList.Lock;
385   HasQ := FHasQueued;
386   if ABlockQueuing then begin
387     FHasQueued := hqBlocked;
388     FDebugger.FLockList.UnLock; // unlock first.
389     Application.RemoveAsyncCalls(Self);
390   end
391   else begin
392     FHasQueued := hqNotQueued;
393     try
394       Application.RemoveAsyncCalls(Self);
395     finally
396       FDebugger.FLockList.UnLock;
397     end;
398   end;
399 
400   if HasQ = hqQueued then
401     DecRef; // may call destroy
402 end;
403 
404 { TFpDbgDebggerThreadWorkerLinkedItem }
405 
406 procedure TFpDbgDebggerThreadWorkerLinkedItem.DoRemovedFromLinkedList;
407 begin
408   UnQueue_DecRef;
409 end;
410 
411 { TFpDbgDebggerThreadWorkerLinkedList }
412 
413 procedure TFpDbgDebggerThreadWorkerLinkedList.Add(
414   AWorkItem: TFpDbgDebggerThreadWorkerLinkedItem);
415 begin
416   AWorkItem.FNextWorker := FNextWorker;
417   FNextWorker := AWorkItem;
418 end;
419 
420 procedure TFpDbgDebggerThreadWorkerLinkedList.ClearFinishedWorkers;
421 var
422   WorkItem, w: TFpDbgDebggerThreadWorkerLinkedItem;
423 begin
424   assert(system.ThreadID = classes.MainThreadID, 'TFpDbgDebggerThreadWorkerLinkedList.ClearFinishedCountWorkers: system.ThreadID = classes.MainThreadID');
425   if FLocked then
426     exit;
427   FLocked := True;
428   WorkItem := FNextWorker;
429   while (WorkItem <> nil) and (WorkItem.RefCount = 1) do begin
430     w := WorkItem;
431     WorkItem := w.FNextWorker;
432     w.DoRemovedFromLinkedList;
433     w.DecRef;
434   end;
435   FNextWorker := WorkItem;
436   FLocked := False;
437 end;
438 
439 procedure TFpDbgDebggerThreadWorkerLinkedList.RequestStopForWorkers;
440 var
441   WorkItem: TFpDbgDebggerThreadWorkerLinkedItem;
442 begin
443   WorkItem := FNextWorker;
444   while (WorkItem <> nil) do begin
445     WorkItem.RequestStop;
446     WorkItem := WorkItem.FNextWorker;
447   end;
448 end;
449 
450 procedure TFpDbgDebggerThreadWorkerLinkedList.WaitForWorkers(AStop: Boolean);
451 var
452   WorkItem, w: TFpDbgDebggerThreadWorkerLinkedItem;
453 begin
454   assert(system.ThreadID = classes.MainThreadID, 'TFpDbgDebggerThreadWorkerLinkedList.WaitForWorkers: system.ThreadID = classes.MainThreadID');
455   assert(not FLocked, 'TFpDbgDebggerThreadWorkerLinkedList.WaitForWorkers: not FLocked');
456   if AStop then
457     RequestStopForWorkers;
458 
459   FLocked := True;
460   WorkItem := FNextWorker;
461   FNextWorker := nil;
462   while (WorkItem <> nil) do begin
463     w := WorkItem;
464     WorkItem := w.FNextWorker;
465     if w.IsCancelled then
466       w.FDebugger.FWorkQueue.RemoveItem(w)
467     else
468       w.FDebugger.FWorkQueue.WaitForItem(w);
469     w.DoRemovedFromLinkedList;
470     w.DecRef;
471   end;
472   FLocked := False;
473 end;
474 
475 { TFpThreadWorkerControllerRun }
476 
477 procedure TFpThreadWorkerControllerRun.DoExecute;
478 begin
479   FStartSuccessfull := FDebugger.FDbgController.Run;
480   FWorkerThreadId := ThreadID;
481 end;
482 
483 constructor TFpThreadWorkerControllerRun.Create(ADebugger: TFpDebugDebuggerBase);
484 begin
485   inherited Create(ADebugger, twpContinue);
486 end;
487 
488 { TFpThreadWorkerRunLoop }
489 
490 procedure TFpThreadWorkerRunLoop.DoExecute;
491 begin
492   FDebugger.FDbgController.ProcessLoop;
493   Queue(@LoopFinished_DecRef);
494 end;
495 
496 constructor TFpThreadWorkerRunLoop.Create(ADebugger: TFpDebugDebuggerBase);
497 begin
498   inherited Create(ADebugger, twpContinue);
499 end;
500 
501 { TFpThreadWorkerRunLoopAfterIdle }
502 
503 procedure TFpThreadWorkerRunLoopAfterIdle.DoExecute;
504 begin
505   Queue(@CheckIdleOrRun_DecRef);
506 end;
507 
508 constructor TFpThreadWorkerRunLoopAfterIdle.Create(ADebugger: TFpDebugDebuggerBase);
509 begin
510   inherited Create(ADebugger, twpContinue);
511 end;
512 
513 { TFpThreadWorkerAsyncMeth }
514 
515 procedure TFpThreadWorkerAsyncMeth.DoExecute;
516 begin
517   FAsyncMethod();
518 end;
519 
520 constructor TFpThreadWorkerAsyncMeth.Create(ADebugger: TFpDebugDebuggerBase;
521   AnAsyncMethod: TFpDbgAsyncMethod);
522 begin
523   inherited Create(ADebugger, twpUser);
524   FAsyncMethod := AnAsyncMethod;
525 end;
526 
527 { TFpThreadWorkerPrepareCallStackEntryList }
528 
529 procedure TFpThreadWorkerPrepareCallStackEntryList.PrepareCallStackEntryList(
530   AFrameRequired: Integer; AThread: TDbgThread);
531 var
532   ThreadCallStack: TDbgCallstackEntryList;
533   CurCnt, ReqCnt: Integer;
534 begin
535   ThreadCallStack := AThread.CallStackEntryList;
536 
537   if ThreadCallStack = nil then begin
538     AThread.PrepareCallStackEntryList(-2); // Only create the list
539     ThreadCallStack := AThread.CallStackEntryList;
540     if ThreadCallStack = nil then
541       exit;
542   end;
543 
544   FDebugger.FLockList.GetLockFor(ThreadCallStack);
545   try
546     CurCnt := ThreadCallStack.Count;
547     while (not StopRequested) and (FRequiredMinCount > CurCnt) and
548           (not ThreadCallStack.HasReadAllAvailableFrames)
549     do begin
550       ReqCnt := Min(CurCnt + 5, FRequiredMinCount);
551       AThread.PrepareCallStackEntryList(ReqCnt);
552       CurCnt := ThreadCallStack.Count;
553       if CurCnt < ReqCnt then
554         exit;
555     end;
556   finally
557     FDebugger.FLockList.FreeLockFor(ThreadCallStack);
558   end;
559 end;
560 
561 procedure TFpThreadWorkerPrepareCallStackEntryList.DoExecute;
562 var
563   t: TDbgThread;
564 begin
565   if FRequiredMinCount < 0 then
566     exit;
567   if FThread = nil then begin
568     for t in FDebugger.FDbgController.CurrentProcess.ThreadMap do begin
569       PrepareCallStackEntryList(FRequiredMinCount, t);
570       if StopRequested then
571         break;
572     end;
573   end
574   else
575     PrepareCallStackEntryList(FRequiredMinCount, FThread);
576 end;
577 
578 constructor TFpThreadWorkerPrepareCallStackEntryList.Create(
579   ADebugger: TFpDebugDebuggerBase; ARequiredMinCount: Integer;
580   APriority: TFpThreadWorkerPriority);
581 begin
582   inherited Create(ADebugger, APriority);
583   FRequiredMinCount := ARequiredMinCount;
584   FThread := nil;
585 end;
586 
587 constructor TFpThreadWorkerPrepareCallStackEntryList.Create(
588   ADebugger: TFpDebugDebuggerBase; ARequiredMinCount: Integer; AThread: TDbgThread);
589 begin
590   Create(ADebugger, ARequiredMinCount);
591   FThread := AThread;
592 end;
593 
594 { TFpThreadWorkerCallStackCount }
595 
596 procedure TFpThreadWorkerCallStackCount.DoExecute;
597 begin
598   inherited DoExecute;
599   Queue(@UpdateCallstack_DecRef);
600 end;
601 
602 { TFpThreadWorkerCallEntry }
603 
604 procedure TFpThreadWorkerCallEntry.DoExecute;
605 var
606   PrettyPrinter: TFpPascalPrettyPrinter;
607   Prop: TFpDebugDebuggerProperties;
608 begin
609   inherited DoExecute;
610 
611   FDbgCallStack := FThread.CallStackEntryList[FCallstackIndex];
612   if (FDbgCallStack <> nil) and (not StopRequested) then begin
613     Prop := TFpDebugDebuggerProperties(FDebugger.GetProperties);
614     PrettyPrinter := TFpPascalPrettyPrinter.Create(DBGPTRSIZE[FDebugger.FDbgController.CurrentProcess.Mode]);
615     PrettyPrinter.Context := FDebugger.FDbgController.DefaultContext;
616 
617     FDebugger.FMemManager.MemLimits.MaxArrayLen            := Prop.MemLimits.MaxStackArrayLen;
618     FDebugger.FMemManager.MemLimits.MaxStringLen           := Prop.MemLimits.MaxStackStringLen;
619     FDebugger.FMemManager.MemLimits.MaxNullStringSearchLen := Prop.MemLimits.MaxStackNullStringSearchLen;
620 
621     FParamAsString := FDbgCallStack.GetParamsAsString(PrettyPrinter);
622     PrettyPrinter.Free;
623 
624     FDebugger.FMemManager.MemLimits.MaxArrayLen            := Prop.MemLimits.MaxArrayLen;
625     FDebugger.FMemManager.MemLimits.MaxStringLen           := Prop.MemLimits.MaxStringLen;
626     FDebugger.FMemManager.MemLimits.MaxNullStringSearchLen := Prop.MemLimits.MaxNullStringSearchLen;
627   end;
628 
629   Queue(@UpdateCallstackEntry_DecRef);
630 end;
631 
632 { TFpThreadWorkerThreads }
633 
634 procedure TFpThreadWorkerThreads.DoExecute;
635 begin
636   inherited DoExecute;
637   Queue(@UpdateThreads_DecRef);
638 end;
639 
640 constructor TFpThreadWorkerThreads.Create(ADebugger: TFpDebugDebuggerBase);
641 begin
642   inherited Create(ADebugger, 1, twpThread);
643 end;
644 
645 { TFpThreadWorkerLocals.TResultEntry }
646 
647 class operator TFpThreadWorkerLocals.TResultEntry. = (a, b: TResultEntry
648   ): Boolean;
649 begin
650   Result := False;
651   assert(False, 'TFpThreadWorkerLocals.TResultEntry.=: False');
652 end;
653 
654 { TFpThreadWorkerLocals }
655 
656 procedure TFpThreadWorkerLocals.DoExecute;
657 var
658   LocalScope: TFpDbgSymbolScope;
659   ProcVal, m: TFpValue;
660   PrettyPrinter: TFpPascalPrettyPrinter;
661   i: Integer;
662   r: TResultEntry;
663 begin
664   LocalScope := FDebugger.FDbgController.CurrentProcess.FindSymbolScope(FThreadId, FStackFrame);
665   if (LocalScope = nil) or (LocalScope.SymbolAtAddress = nil) then begin
666     LocalScope.ReleaseReference;
667     exit;
668   end;
669 
670   ProcVal := LocalScope.ProcedureAtAddress;
671   if (ProcVal = nil) then begin
672     LocalScope.ReleaseReference;
673     exit;
674   end;
675 
676   PrettyPrinter := TFpPascalPrettyPrinter.Create(LocalScope.SizeOfAddress);
677   PrettyPrinter.Context := LocalScope.LocationContext;
678 //  PrettyPrinter.MemManager.DefaultContext := LocalScope.LocationContext;
679 
680   FResults := TResultList.Create;
681   for i := 0 to ProcVal.MemberCount - 1 do begin
682     m := ProcVal.Member[i];
683     if m <> nil then begin
684       if m.DbgSymbol <> nil then
685         r.Name := m.DbgSymbol.Name
686       else
687         r.Name := '';
688       //if not StopRequested then // finish getting all names?
689       PrettyPrinter.PrintValue(r.Value, m);
690       m.ReleaseReference;
691       FResults.Add(r);
692     end;
693     if StopRequested then
694       Break;
695   end;
696   PrettyPrinter.Free;
697   ProcVal.ReleaseReference;
698   LocalScope.ReleaseReference;
699 
700   Queue(@UpdateLocals_DecRef);
701 end;
702 
703 destructor TFpThreadWorkerLocals.Destroy;
704 begin
705   FResults.Free;
706   inherited Destroy;
707 end;
708 
709 { TFpThreadWorkerModify }
710 
711 procedure TFpThreadWorkerModify.DoExecute;
712 var
713   APasExpr: TFpPascalExpression;
714   ResValue: TFpValue;
715   ExpressionScope: TFpDbgSymbolScope;
716   i64: int64;
717   c64: QWord;
718 begin
719   FSuccess := False;
720   ExpressionScope := FDebugger.FDbgController.CurrentProcess.FindSymbolScope(FThreadId, FStackFrame);
721   if ExpressionScope = nil then
722     exit;
723 
724   APasExpr := TFpPascalExpression.Create(FExpression, ExpressionScope);
725   try
726     APasExpr.ResultValue; // trigger full validation
727     if not APasExpr.Valid then
728       exit;
729 
730     ResValue := APasExpr.ResultValue;
731     if ResValue = nil then
732       exit;
733 
734     case ResValue.Kind of
735       skInteger:   if TryStrToInt64(FNewVal, i64) then ResValue.AsInteger := i64;
736       skCardinal:  if TryStrToQWord(FNewVal, c64) then ResValue.AsCardinal := c64;
737       skBoolean:   case LowerCase(trim(FNewVal)) of
738           'true':  ResValue.AsBool := True;
739           'false': ResValue.AsBool := False;
740         end;
741       skChar:      ResValue.AsString := FNewVal;
742       skEnum:      ResValue.AsString := FNewVal;
743       skSet:       ResValue.AsString := FNewVal;
744       skPointer:   if TryStrToQWord(FNewVal, c64) then ResValue.AsCardinal := c64;
745       skFloat: ;
746       skCurrency: ;
747       skVariant: ;
748     end;
749 
750 
751   finally
752     APasExpr.Free;
753     ExpressionScope.ReleaseReference;
754     Queue(@DoCallback_DecRef);
755   end;
756 end;
757 
758 constructor TFpThreadWorkerModify.Create(ADebugger: TFpDebugDebuggerBase;
759   const AnExpression, ANewValue: String; AStackFrame, AThreadId: Integer);
760 begin
761   inherited Create(ADebugger, twpModify);
762   FExpression := AnExpression;
763   FNewVal := ANewValue;
764   FStackFrame := AStackFrame;
765   FThreadId := AThreadId;
766 end;
767 
DebugTextnull768 function TFpThreadWorkerModify.DebugText: String;
769 begin
770   Result := inherited DebugText;
771 end;
772 
773 { TFpThreadWorkerEvaluate }
774 
DoWatchFunctionCallnull775 function TFpThreadWorkerEvaluate.DoWatchFunctionCall(
776   AnExpressionPart: TFpPascalExpressionPart; AFunctionValue,
777   ASelfValue: TFpValue; AParams: TFpPascalExpressionPartList; out
778   AResult: TFpValue; var AnError: TFpError): boolean;
779 var
780   FunctionSymbolData, FunctionSymbolType, FunctionResultSymbolType,
781   TempSymbol: TFpSymbol;
782   ParamSymbol, ExprParamVal: TFpValue;
783   ProcAddress: TFpDbgMemLocation;
784   FunctionResultDataSize: TFpDbgValueSize;
785   ParameterSymbolArr: array of TFpSymbol;
786   CallContext: TFpDbgInfoCallContext;
787   PCnt, i, FoundIdx, ItemsOffs: Integer;
788   rk: TDbgSymbolKind;
789 begin
790   Result := False;
791   if FExpressionScope = nil then
792     exit;
793 (*
794    AFunctionValue =>  TFpValueDwarfSubroutine  // gotten from <== TFpSymbolDwarfDataProc.GetValueObject;
795                    .DataSympol = TFpSymbolDwarfDataProc  from which we were created
796                    .TypeSymbol = TFpSymbolDwarfTypeProc.TypeInfo : TFpSymbolDwarfType
797 
798    AFunctionFpSymbol => TFpSymbolDwarfTypeProc;
799    val
800 *)
801 
802   FunctionSymbolData := AFunctionValue.DbgSymbol;  // AFunctionValue . FDataSymbol
803   FunctionSymbolType := FunctionSymbolData.TypeInfo;
804   FunctionResultSymbolType := FunctionSymbolType.TypeInfo;
805 
806   if not (FunctionResultSymbolType.Kind in [skInteger, skCurrency, skPointer, skEnum,
807       skCardinal, skBoolean, skChar, skClass])
808   then begin
809     DebugLn(['Error result kind  ', dbgs(FunctionSymbolType.Kind)]);
810     AnError := CreateError(fpErrAnyError, ['Result type of function not supported']);
811     exit;
812   end;
813 
814   // TODO: pass a value object
815   if (not FunctionResultSymbolType.ReadSize(nil, FunctionResultDataSize)) or
816      (FunctionResultDataSize >  FDebugger.FMemManager.RegisterSize(0))
817   then begin
818     DebugLn(['Error result size', dbgs(FunctionResultDataSize)]);
819     //ReturnMessage := 'Unable to call function. The size of the function-result exceeds the content-size of a register.';
820     AnError := CreateError(fpErrAnyError, ['Result type of function not supported']);
821     exit;
822   end;
823 
824   // check params
825 
826   ProcAddress := AFunctionValue.DataAddress;
827   if not IsReadableLoc(ProcAddress) then begin
828     DebugLn(['Error proc addr']);
829     AnError := CreateError(fpErrAnyError, ['Unable to calculate function address']);
830     exit;
831   end;
832 
833   PCnt := AParams.Count;
834   ItemsOffs := 0;
835   if ASelfValue <> nil then begin
836     inc(PCnt);
837     ItemsOffs := -1; // In the loop "i = 0" is the self object. So "i = 1" should be AParams[0]
838   end;
839 
840   SetLength(ParameterSymbolArr, PCnt);
841     for i := 0 to High(ParameterSymbolArr) do
842       ParameterSymbolArr[i] := nil;
843   FoundIdx := 0;
844   try
845     for i := 0 to FunctionSymbolType.NestedSymbolCount - 1 do begin
846       TempSymbol := FunctionSymbolType.NestedSymbol[i];
847       if sfParameter in TempSymbol.Flags then begin
848         if FoundIdx >= PCnt then begin
849           DebugLn(['Error param count']);
850           AnError := CreateError(fpErrAnyError, ['wrong amount of parameters']);
851           exit;
852           //ReturnMessage := Format('Unable to call function%s. Not enough parameters supplied.', [OutputFunctionName]);
853         end;
854         // Type Compatibility
855         // TODO: more checks for type compatibility
856         if (ASelfValue <> nil) and (FoundIdx = 0) then begin
857           // TODO: check self param
858         end
859         else begin
860           ExprParamVal := AParams.Items[FoundIdx + ItemsOffs].ResultValue;
861           if (ExprParamVal = nil) then begin
862             DebugLn('Internal error for arg %d ', [FoundIdx]);
863             AnError := AnExpressionPart.Expression.Error;
864             if not IsError(AnError) then
865               AnError := CreateError(fpErrAnyError, ['internal error, computing parameter']);
866             exit;
867           end;
868           rk := ExprParamVal.Kind;
869           if not (rk in [skInteger, {skCurrency,} skPointer, skEnum, skCardinal, skBoolean, skChar, skClass]) then begin
870             DebugLn('Error not supported kind arg %d : %s ', [FoundIdx, dbgs(rk)]);
871             AnError := CreateError(fpErrAnyError, ['parameter type not supported']);
872             exit;
873           end;
874           if (TempSymbol.Kind <> rk) and
875              ( (TempSymbol.Kind in [skInteger, skCardinal]) <> (rk in [skInteger, skCardinal]) )
876           then begin
877             DebugLn('Error kind mismatch for arg %d : %s <> %s', [FoundIdx, dbgs(TempSymbol.Kind), dbgs(rk)]);
878             AnError := CreateError(fpErrAnyError, ['wrong type for parameter']);
879             exit;
880           end;
881         end;
882         if not IsTargetOrRegNotNil(FDebugger.FDbgController.CurrentProcess.CallParamDefaultLocation(FoundIdx)) then begin
883           DebugLn('error to many args / not supported / arg > %d ', [FoundIdx]);
884           AnError := CreateError(fpErrAnyError, ['too many parameter / not supported']);
885           exit;
886         end;
887         TempSymbol.AddReference;
888         ParameterSymbolArr[FoundIdx] := TempSymbol;
889         inc(FoundIdx)
890       end;
891     end;
892     if FoundIdx <> PCnt then begin
893       DebugLn(['Error param count']);
894       AnError := CreateError(fpErrAnyError, ['wrong amount of parameters']);
895       exit;
896     end;
897 
898 
899     CallContext := FDebugger.FDbgController.Call(ProcAddress, FExpressionScope.LocationContext,
900       FDebugger.FMemReader, FDebugger.FMemConverter);
901 
902     try
903       for i := 0 to High(ParameterSymbolArr) do begin
904         ParamSymbol := CallContext.CreateParamSymbol(i, ParameterSymbolArr[i], FDebugger.FDbgController.CurrentProcess);
905         try
906           if (ASelfValue <> nil) and (i = 0) then
907             ParamSymbol.AsCardinal := ASelfValue.AsCardinal
908           else
909             ParamSymbol.AsCardinal := AParams.Items[i + ItemsOffs].ResultValue.AsCardinal;
910           if IsError(ParamSymbol.LastError) then begin
911             DebugLn('Internal error for arg %d ', [i]);
912             AnError := ParamSymbol.LastError;
913             exit;
914           end;
915         finally
916           ParamSymbol.ReleaseReference;
917         end;
918       end;
919 
920       FDebugger.FDbgController.ProcessLoop;
921 
922       if not CallContext.IsValid then begin
923         DebugLn(['Error in call ',CallContext.Message]);
924         //ReturnMessage := CallContext.Message;
925         exit;
926       end;
927 
928       AResult := CallContext.CreateParamSymbol(-1, FunctionSymbolType,
929         FDebugger.FDbgController.CurrentProcess, FunctionSymbolData.Name);
930       Result := AResult <> nil;
931     finally
932       CallContext.ReleaseReference;
933     end;
934   finally
935     for i := 0 to High(ParameterSymbolArr) do
936       if ParameterSymbolArr[i] <> nil then
937         ParameterSymbolArr[i].ReleaseReference;
938   end;
939 
940 
941 end;
942 
TFpThreadWorkerEvaluate.EvaluateExpressionnull943 function TFpThreadWorkerEvaluate.EvaluateExpression(const AnExpression: String;
944   AStackFrame, AThreadId: Integer; ADispFormat: TWatchDisplayFormat;
945   ARepeatCnt: Integer; AnEvalFlags: TDBGEvaluateFlags; out AResText: String;
946   out ATypeInfo: TDBGType): Boolean;
947 var
948   APasExpr, PasExpr2: TFpPascalExpression;
949   PrettyPrinter: TFpPascalPrettyPrinter;
950   ResValue: TFpValue;
951   CastName, ResText2: String;
952 begin
953   Result := False;
954   AResText := '';
955   ATypeInfo := nil;
956 
957   FExpressionScope := FDebugger.FDbgController.CurrentProcess.FindSymbolScope(AThreadId, AStackFrame);
958   if FExpressionScope = nil then
959     exit;
960 
961   PrettyPrinter := nil;
962   APasExpr := TFpPascalExpression.Create(AnExpression, FExpressionScope);
963   try
964     if FAllowFunctions and (dfEvalFunctionCalls in FDebugger.EnabledFeatures) then
965       APasExpr.OnFunctionCall  := @DoWatchFunctionCall;
966     APasExpr.ResultValue; // trigger full validation
967     if not APasExpr.Valid then begin
968       AResText := ErrorHandler.ErrorAsString(APasExpr.Error);
969       exit;
970     end;
971 
972     ResValue := APasExpr.ResultValue;
973     if ResValue = nil then begin
974       AResText := 'Error';
975       exit;
976     end;
977 
978     if StopRequested then
979       exit;
980     if (ResValue.Kind = skClass) and (ResValue.AsCardinal <> 0) and
981        (not IsError(ResValue.LastError)) and (defClassAutoCast in AnEvalFlags)
982     then begin
983       if ResValue.GetInstanceClassName(CastName) then begin
984         PasExpr2 := TFpPascalExpression.Create(CastName+'('+AnExpression+')', FExpressionScope);
985         PasExpr2.ResultValue;
986         if PasExpr2.Valid then begin
987           APasExpr.Free;
988           APasExpr := PasExpr2;
989           ResValue := APasExpr.ResultValue;
990         end
991         else
992           PasExpr2.Free;
993       end
994       else begin
995         ResValue.ResetError; // in case GetInstanceClassName did set an error
996         // TODO: indicate that typecasting to instance failed
997       end;
998     end;
999 
1000     if StopRequested then
1001       exit;
1002 
1003     PrettyPrinter := TFpPascalPrettyPrinter.Create(FExpressionScope.SizeOfAddress);
1004     PrettyPrinter.Context := FExpressionScope.LocationContext;
1005 
1006     if defNoTypeInfo in AnEvalFlags then
1007       Result := PrettyPrinter.PrintValue(AResText, ResValue, ADispFormat, ARepeatCnt)
1008     else
1009       Result := PrettyPrinter.PrintValue(AResText, ATypeInfo, ResValue, ADispFormat, ARepeatCnt);
1010 
1011     // PCHAR/String
1012     if Result and APasExpr.HasPCharIndexAccess and not IsError(ResValue.LastError) then begin
1013     // TODO: Only dwarf 2
1014       APasExpr.FixPCharIndexAccess := True;
1015       APasExpr.ResetEvaluation;
1016       ResValue := APasExpr.ResultValue;
1017       if (ResValue=nil) or (not PrettyPrinter.PrintValue(ResText2, ResValue, ADispFormat, ARepeatCnt)) then
1018         ResText2 := 'Failed';
1019       AResText := 'PChar: '+AResText+ LineEnding + 'String: '+ResText2;
1020     end;
1021 
1022     if Result then
1023       Result := not IsError(ResValue.LastError) // AResText should be set from Prettyprinter
1024     else
1025       AResText := 'Error';
1026 
1027     if not Result then
1028       FreeAndNil(ATypeInfo);
1029   finally
1030     PrettyPrinter.Free;
1031     APasExpr.Free;
1032     FExpressionScope.ReleaseReference;
1033   end;
1034 end;
1035 
1036 { TFpThreadWorkerEvaluateExpr }
1037 
1038 procedure TFpThreadWorkerEvaluateExpr.DoExecute;
1039 begin
1040   FRes := EvaluateExpression(FExpression, FStackFrame, FThreadId,
1041     FDispFormat, FRepeatCnt, FEvalFlags, FResText, FResDbgType);
1042 end;
1043 
1044 constructor TFpThreadWorkerEvaluateExpr.Create(ADebugger: TFpDebugDebuggerBase;
1045   APriority: TFpThreadWorkerPriority; const AnExpression: String; AStackFrame,
1046   AThreadId: Integer; ADispFormat: TWatchDisplayFormat; ARepeatCnt: Integer;
1047   AnEvalFlags: TDBGEvaluateFlags);
1048 begin
1049   inherited Create(ADebugger, APriority);
1050   FExpression := AnExpression;
1051   FStackFrame := AStackFrame;
1052   FThreadId := AThreadId;
1053   FDispFormat := ADispFormat;
1054   FRepeatCnt := ARepeatCnt;
1055   FEvalFlags := AnEvalFlags;
1056   if (defAllowFunctionCall in AnEvalFlags) then
1057     FAllowFunctions := True;
1058   FRes := False;
1059 end;
1060 
DebugTextnull1061 function TFpThreadWorkerEvaluateExpr.DebugText: String;
1062 begin
1063   Result := inherited DebugText;
1064   if self = nil then exit;
1065   Result := Format('%s Expr: "%s" T: %s S: %s', [Result, FExpression, dbgs(FThreadId), dbgs(FStackFrame)]);
1066 end;
1067 
1068 { TFpThreadWorkerCmdEval }
1069 
1070 procedure TFpThreadWorkerCmdEval.DoCallback_DecRef(Data: PtrInt);
1071 var
1072   CB: TDBGEvaluateResultCallback;
1073   Dbg: TFpDebugDebuggerBase;
1074   Res: Boolean;
1075   ResText: String;
1076   ResDbgType: TDBGType;
1077 begin
1078   assert(system.ThreadID = classes.MainThreadID, 'TFpThreadWorkerCmdEval.DoCallback_DecRef: system.ThreadID = classes.MainThreadID');
1079   CB := nil;
1080   try
1081     if FEvalFlags * [defNoTypeInfo, defSimpleTypeInfo, defFullTypeInfo] = [defNoTypeInfo] then
1082       FreeAndNil(FResText);
1083 
1084     if (FCallback <> nil) then begin
1085       // All to local vars, because SELF may be destroyed before/while the callback happens
1086       CB := FCallback;
1087       Dbg := FDebugger;
1088       Res := FRes;
1089       ResText := FResText;
1090       ResDbgType := FResDbgType;
1091       FResDbgType := nil; // prevent from being freed => will be freed in callback
1092       FCallback := nil; // Ensure callback is never called a 2nd time (e.g. if Self.Abort is called, while in Callback)
1093       (* We cannot call Callback here, because ABORT can be called, and prematurely call UnQueue_DecRef,
1094          removing the last ref to this object *)
1095     end;
1096   except
1097   end;
1098 
1099   UnQueue_DecRef;
1100 
1101   // Self may now be invalid, unless FDebugger.FEvalWorkItem still has a reference.
1102   // Abort may be called (during CB), removing this refence.
1103   // Abort would be called, if a new Evaluate Request is made. FEvalWorkItem<>nil
1104   if CB <> nil then
1105     CB(Dbg, Res, ResText, ResDbgType);
1106 end;
1107 
1108 procedure TFpThreadWorkerCmdEval.DoExecute;
1109 begin
1110   inherited DoExecute;
1111   Queue(@DoCallback_DecRef);
1112 end;
1113 
1114 constructor TFpThreadWorkerCmdEval.Create(ADebugger: TFpDebugDebuggerBase;
1115   APriority: TFpThreadWorkerPriority; const AnExpression: String; AStackFrame,
1116   AThreadId: Integer; AnEvalFlags: TDBGEvaluateFlags;
1117   ACallback: TDBGEvaluateResultCallback);
1118 begin
1119   inherited Create(ADebugger, APriority, AnExpression, AStackFrame, AThreadId, wdfDefault, 0,
1120     AnEvalFlags);
1121   FCallback := ACallback;
1122 end;
1123 
1124 destructor TFpThreadWorkerCmdEval.Destroy;
1125 begin
1126   inherited Destroy;
1127   FreeAndNil(FResDbgType);
1128 end;
1129 
1130 procedure TFpThreadWorkerCmdEval.Abort;
1131 begin
1132   RequestStop;
1133   FDebugger.FWorkQueue.RemoveItem(Self);
1134   DoCallback_DecRef;
1135 end;
1136 
1137 { TFpThreadWorkerWatchValueEval }
1138 
1139 procedure TFpThreadWorkerWatchValueEval.DoExecute;
1140 begin
1141   inherited DoExecute;
1142   Queue(@UpdateWatch_DecRef);
1143 end;
1144 
1145 { TFpThreadWorkerBreakPoint }
1146 
1147 procedure TFpThreadWorkerBreakPoint.RemoveBreakPoint_DecRef;
1148 begin
1149   //
1150 end;
1151 
1152 procedure TFpThreadWorkerBreakPoint.AbortSetBreak;
1153 begin
1154   //
1155 end;
1156 
1157 { TFpThreadWorkerBreakPointSet }
1158 
1159 procedure TFpThreadWorkerBreakPointSet.DoExecute;
1160 var
1161   CurContext: TFpDbgSymbolScope;
1162   WatchPasExpr: TFpPascalExpression;
1163   R: TFpValue;
1164   s: TFpDbgValueSize;
1165 begin
1166   case FKind of
1167     bpkAddress:
1168       FInternalBreakpoint := FDebugger.FDbgController.CurrentProcess.AddBreak(FAddress, True);
1169     bpkSource:
1170       FInternalBreakpoint := FDebugger.FDbgController.CurrentProcess.AddBreak(FSource, FLine, True);
1171     bpkData: begin
1172       CurContext := FDebugger.FDbgController.CurrentProcess.FindSymbolScope(FThreadId, FStackFrame);
1173       if CurContext <> nil then begin
1174         WatchPasExpr := TFpPascalExpression.Create(FWatchData, CurContext);
1175         R := WatchPasExpr.ResultValue; // Address and Size
1176         // TODO: Cache current value
1177         if WatchPasExpr.Valid and IsTargetNotNil(R.Address) and R.GetSize(s) then begin
1178           // pass context
1179           FInternalBreakpoint := FDebugger.FDbgController.CurrentProcess.AddWatch(R.Address.Address, SizeToFullBytes(s), FWatchKind, FWatchScope);
1180         end;
1181         WatchPasExpr.Free;
1182         CurContext.ReleaseReference;
1183       end;
1184     end;
1185   end;
1186   if FResetBreakPoint then begin
1187     FDebugger.FDbgController.CurrentProcess.RemoveBreak(FInternalBreakpoint);
1188     FreeAndNil(FInternalBreakpoint);
1189   end;
1190   Queue(@UpdateBrkPoint_DecRef);
1191 end;
1192 
1193 constructor TFpThreadWorkerBreakPointSet.Create(ADebugger: TFpDebugDebuggerBase; AnAddress: TDBGPtr);
1194 begin
1195   FKind := bpkAddress;
1196   FAddress := AnAddress;
1197   inherited Create(ADebugger, twpUser);
1198 end;
1199 
1200 constructor TFpThreadWorkerBreakPointSet.Create(
1201   ADebugger: TFpDebugDebuggerBase; ASource: String; ALine: Integer);
1202 begin
1203   FKind := bpkSource;
1204   FSource := ASource;
1205   FLine   := ALine;
1206   inherited Create(ADebugger, twpUser);
1207 end;
1208 
1209 constructor TFpThreadWorkerBreakPointSet.Create(
1210   ADebugger: TFpDebugDebuggerBase; AWatchData: String;
1211   AWatchScope: TDBGWatchPointScope; AWatchKind: TDBGWatchPointKind;
1212   AStackFrame, AThreadId: Integer);
1213 begin
1214   FKind := bpkData;
1215   FWatchData  := AWatchData;
1216   FWatchScope := AWatchScope;
1217   FWatchKind  := AWatchKind;
1218   FStackFrame := AStackFrame;
1219   FThreadId   := AThreadId;
1220   inherited Create(ADebugger, twpUser);
1221 end;
1222 
1223 { TFpThreadWorkerBreakPointRemove }
1224 
1225 procedure TFpThreadWorkerBreakPointRemove.DoExecute;
1226 begin
1227   if (FDebugger.FDbgController <> nil) and (FDebugger.FDbgController.CurrentProcess <> nil) then
1228     FDebugger.FDbgController.CurrentProcess.RemoveBreak(FInternalBreakpoint);
1229   FreeAndNil(FInternalBreakpoint);
1230 end;
1231 
1232 constructor TFpThreadWorkerBreakPointRemove.Create(
1233   ADebugger: TFpDebugDebuggerBase;
1234   AnInternalBreakpoint: FpDbgClasses.TFpDbgBreakpoint);
1235 begin
1236   FInternalBreakpoint := AnInternalBreakpoint;
1237   inherited Create(ADebugger, twpUser);
1238 end;
1239 
1240 end.
1241 
1242