Free cookie consent management tool by TermsFeed Policy Generator

source: branches/HeuristicLab.DebugEngine/DebugEngine.cs @ 4949

Last change on this file since 4949 was 4947, checked in by epitzer, 14 years ago

Simpler breakpoint handling, fix cloning, prevent stepping while running (#47)

File size: 11.2 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2010 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Collections.Generic;
24using System.Linq;
25using System.Threading;
26using HeuristicLab.Common;
27using HeuristicLab.Core;
28using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
29
30namespace HeuristicLab.DebugEngine {
31
32  [StorableClass]
33  [Item("Debug Engine", "Engine for debugging algorithms.")]
34  public class DebugEngine : Executable, IEngine {
35
36    #region Construction and Cloning
37
38    [StorableConstructor]
39    protected DebugEngine(bool deserializing)
40      : base(deserializing) {
41      pausePending = stopPending = false;
42      InitializeTimer();
43    }
44
45    protected DebugEngine(DebugEngine original, Cloner cloner)
46      : base(original, cloner) {
47      if (original.ExecutionState == ExecutionState.Started) throw new InvalidOperationException(string.Format("Clone not allowed in execution state \"{0}\".", ExecutionState));
48      Log = cloner.Clone(original.Log);
49      ExecutionStack = cloner.Clone(original.ExecutionStack);
50      OperatorTrace = cloner.Clone(original.OperatorTrace);
51      operatorParents = original.operatorParents.ToDictionary(kvp => cloner.Clone(kvp.Key), kvp => cloner.Clone(kvp.Value));
52      pausePending = original.pausePending;
53      stopPending = original.stopPending;
54      InitializeTimer();
55      currentOperation = cloner.Clone(original.currentOperation);
56      currentOperator = cloner.Clone(original.currentOperator);
57    }
58    public DebugEngine()
59      : base() {
60      Log = new Log();
61      ExecutionStack = new ExecutionStack();
62      OperatorTrace = new ItemList<IOperator>();
63      operatorParents = new Dictionary<IAtomicOperation, IAtomicOperation>();
64      pausePending = stopPending = false;
65      InitializeTimer();
66    }
67
68    public override IDeepCloneable Clone(Cloner cloner) {
69      return new DebugEngine(this, cloner);
70    }
71
72    private void InitializeTimer() {
73      timer = new System.Timers.Timer(100);
74      timer.AutoReset = true;
75      timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
76    }
77
78    #endregion
79
80    #region Fields and Properties
81
82    [Storable]
83    public ILog Log { get; protected set; }
84
85    [Storable]
86    public ExecutionStack ExecutionStack { get; protected set; }
87
88    private bool pausePending, stopPending;
89    private DateTime lastUpdateTime;
90    private System.Timers.Timer timer;
91
92    [Storable]
93    private IOperator currentOperator;
94
95    [Storable]
96    private IOperation currentOperation;
97    public virtual IOperation CurrentOperation {
98      get { return currentOperation; }
99      private set {
100        if (value != currentOperation) {
101          currentOperation = value;
102          OnOperationChanged(value);
103        }
104      }
105    }
106
107    public virtual IAtomicOperation CurrentAtomicOperation {
108      get { return CurrentOperation as IAtomicOperation; }
109    }
110
111    public virtual IExecutionContext CurrentExecutionContext {
112      get { return CurrentOperation as IExecutionContext; }
113    }
114
115    [Storable]
116    public ItemList<IOperator> OperatorTrace { get; private set; }
117
118    [Storable]
119    private Dictionary<IAtomicOperation, IAtomicOperation> operatorParents;
120
121    public virtual bool CanContinue {
122      get { return CurrentOperation != null || ExecutionStack.Count > 0; }
123    }
124
125    public virtual bool IsAtBreakpoint {
126      get { return CurrentAtomicOperation != null && CurrentAtomicOperation.Operator != null && CurrentAtomicOperation.Operator.Breakpoint; }
127    }
128
129    #endregion
130
131    #region Events
132
133    public event EventHandler<OperationChangedEventArgs> CurrentOperationChanged;
134    protected virtual void OnOperationChanged(IOperation newOperation) {
135      EventHandler<OperationChangedEventArgs> handler = CurrentOperationChanged;
136      if (handler != null) {
137        handler(this, new OperationChangedEventArgs(newOperation));
138      }
139    }
140
141    #endregion
142
143    #region Std Methods
144    public sealed override void Prepare() {
145      base.Prepare();
146      ExecutionStack.Clear();
147      CurrentOperation = null;
148      OperatorTrace.Clear();
149      operatorParents.Clear();
150      OnPrepared();
151    }
152    public void Prepare(IOperation initialOperation) {
153      base.Prepare();
154      ExecutionStack.Clear();
155      if (initialOperation != null)
156        ExecutionStack.Add(initialOperation);
157      CurrentOperation = null;
158      OperatorTrace.Clear();
159      operatorParents.Clear();
160      OnPrepared();
161    }
162    protected override void OnPrepared() {
163      Log.LogMessage("Engine prepared");
164      base.OnPrepared();
165    }
166
167    public virtual void Step(bool skipStackOperations) {
168      OnStarted();
169      lastUpdateTime = DateTime.Now;
170      timer.Start();
171      ProcessNextOperation();
172      while (skipStackOperations && !(CurrentOperation is IAtomicOperation) && CanContinue)
173        ProcessNextOperation();
174      timer.Stop();
175      ExecutionTime += DateTime.Now - lastUpdateTime;
176      OnPaused();
177    }
178
179    public override void Start() {
180      base.Start();
181      ThreadPool.QueueUserWorkItem(new WaitCallback(Run), null);
182    }
183
184    protected override void OnStarted() {
185      Log.LogMessage("Engine started");
186      base.OnStarted();
187    }
188
189    public override void Pause() {
190      base.Pause();
191      pausePending = true;
192      if (currentOperator != null) currentOperator.Abort();
193    }
194
195    protected override void OnPaused() {
196      Log.LogMessage("Engine paused");
197      base.OnPaused();
198    }
199
200    public override void Stop() {
201      CurrentOperation = null;
202      base.Stop();
203      stopPending = true;
204      if (currentOperator != null) currentOperator.Abort();
205      if (ExecutionState == ExecutionState.Paused) OnStopped();
206    }
207
208    protected override void OnStopped() {
209      Log.LogMessage("Engine stopped");
210      base.OnStopped();
211    }
212
213    protected override void OnExceptionOccurred(Exception exception) {
214      Log.LogException(exception);
215      base.OnExceptionOccurred(exception);
216    }
217
218    private void Run(object state) {
219      OnStarted();
220      pausePending = stopPending = false;
221
222      lastUpdateTime = DateTime.Now;
223      timer.Start();
224      if (!pausePending && !stopPending && CanContinue)
225        ProcessNextOperation();
226      while (!pausePending && !stopPending && CanContinue && !IsAtBreakpoint)
227        ProcessNextOperation();
228      timer.Stop();
229      ExecutionTime += DateTime.Now - lastUpdateTime;
230
231      if (IsAtBreakpoint)
232        Log.LogMessage(string.Format("Breaking before: {0}", CurrentAtomicOperation.Operator.Name));
233      if (pausePending || IsAtBreakpoint)
234        OnPaused();
235      else
236        OnStopped();
237    }
238
239    private void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) {
240      DateTime now = DateTime.Now;
241      ExecutionTime += now - lastUpdateTime;
242      lastUpdateTime = now;
243    }
244    #endregion
245
246    #region Methods
247
248
249
250    /// <summary>
251    /// Deals with the next operation, if it is an <see cref="AtomicOperation"/> it is executed,
252    /// if it is a <see cref="CompositeOperation"/> its single operations are pushed on the execution stack.
253    /// </summary>
254    /// <remarks>If an error occurs during the execution the operation is aborted and the operation
255    /// is pushed on the stack again.<br/>
256    /// If the execution was successful <see cref="EngineBase.OnOperationExecuted"/> is called.</remarks>
257    protected virtual void ProcessNextOperation() {
258      try {
259        IAtomicOperation atomicOperation = CurrentOperation as IAtomicOperation;
260        OperationCollection operations = CurrentOperation as OperationCollection;
261        if (atomicOperation != null && operations != null)
262          throw new InvalidOperationException("Current operation is both atomic and an operation collection");
263
264        if (atomicOperation != null) {
265          Log.LogMessage(string.Format("Performing atomic operation {0}", Name(atomicOperation)));
266          PerformAtomicOperation(atomicOperation);
267        } else if (operations != null) {
268          Log.LogMessage("Expanding operation collection");
269          ExecutionStack.AddRange(operations.Reverse());
270          CurrentOperation = null;
271        } else if (ExecutionStack.Count > 0) {
272          Log.LogMessage("Popping execution stack");
273          CurrentOperation = ExecutionStack.Last();
274          ExecutionStack.RemoveAt(ExecutionStack.Count - 1);
275        } else {
276          Log.LogMessage("Nothing to do");
277        }
278        GenerateOperationTrace();
279      } catch (Exception x) {
280        OnExceptionOccurred(x);
281      }
282    }
283
284    private void GenerateOperationTrace() {
285      var operation = CurrentOperation as IAtomicOperation;
286      if (operation != null) {
287        List<IOperator> trace = new List<IOperator>();
288        while (operation != null) {
289          trace.Add(operation.Operator);
290          IAtomicOperation parent = null;
291          operatorParents.TryGetValue(operation, out parent);
292          operation = parent;
293        }
294        trace.Reverse();
295        OperatorTrace.Clear();
296        OperatorTrace.AddRange(trace);
297      }
298    }
299
300    protected virtual void PerformAtomicOperation(IAtomicOperation operation) {
301      if (operation != null) {
302        try {
303          currentOperator = operation.Operator;
304          IOperation successor = operation.Operator.Execute((IExecutionContext)operation);
305          if (successor != null) {
306            AssignParents(operation, successor);
307            ExecutionStack.Add(successor);
308          }
309          currentOperator = null;
310          CurrentOperation = null;
311        } catch (Exception ex) {
312          OnExceptionOccurred(new OperatorExecutionException(operation.Operator, ex));
313          Pause();
314        }
315      }
316    }
317
318    private void AssignParents(IAtomicOperation parent, IOperation successor) {
319      OperationCollection operations = successor as OperationCollection;
320      if (operations != null)
321        foreach (var op in operations)
322          AssignParents(parent, op);
323      IAtomicOperation atomicOperation = successor as IAtomicOperation;
324      if (atomicOperation != null && atomicOperation.Operator != null && !operatorParents.ContainsKey(atomicOperation))
325        operatorParents[atomicOperation] = parent;
326    }
327
328    protected virtual string Name(IAtomicOperation operation) {
329      return string.IsNullOrEmpty(operation.Operator.Name) ? operation.Operator.ItemName : operation.Operator.Name;
330    }
331
332    #endregion
333  }
334}
Note: See TracBrowser for help on using the repository browser.