public BacktraceWrapper (MD.StackFrame[] frames, TargetObject exception): base (Server.Instance.MdbObjectValueAdaptor)
		{
			this.frames = frames;
			this.exception = exception;
			Connect ();
		}
		public virtual void InitializeBreakpoint (MDB.SourceBreakpoint bp)
		{
		}
		public override void AbortThread (MDB.Thread thread, MDB.RuntimeInvokeResult result)
		{
			result.Abort ();
		}
		public MdbDissassemblyBuffer (MD.Thread thread, MD.TargetAddress addr): base (addr.Address)
		{
			this.thread = thread;
			this.baseAddr = addr;
		}
		ThreadInfo CreateThreadInfo (MD.Thread t)
		{
			string loc;
			if (t.CurrentFrame != null && t.CurrentFrame.SourceLocation != null) {
				loc = t.CurrentFrame.ToString ();
			} else
				loc = "<Unknown>";
			
			return new ThreadInfo (t.Process.ID, t.ID, t.Name, loc);
		}
		public override void InitializeBreakpoint (MDB.SourceBreakpoint bp)
		{
		//	bp.IsUserModule = true;
		}
		private void OnProcessExecdEvent (MD.Debugger debugger, MD.Process process)
		{
			WriteDebuggerOutput (string.Format ("Process {0} execd.\n", process.ID));
		}
		public virtual void ActivateEvent (MDB.Event ev)
		{
		}
		private void OnTargetEvent (MD.Thread thread, MD.TargetEventArgs args)
		{
			try {
				if (!running) {
					LogEvent (args);
					return;
				}
				
				bool notifyToClient = args.IsStopped || args.Type == MD.TargetEventType.UnhandledException || args.Type == MD.TargetEventType.Exception || args.Type == MD.TargetEventType.TargetInterrupted;
				
				LogEvent (args);
				
				bool isStop = args.Type != MD.TargetEventType.FrameChanged &&
					args.Type != MD.TargetEventType.TargetExited &&
					args.Type != MD.TargetEventType.TargetRunning;
				
				if (isStop) {
					
					lock (debugger) {
						
						if (stoppedWorkQueue.Count > 0) {
							// Execute queued work in another thread with a small delay
							// since it is not safe to execute it here
							System.Threading.ThreadPool.QueueUserWorkItem (delegate {
								System.Threading.Thread.Sleep (50);
								bool resume = false;
								lock (debugger) {
									foreach (ST.WaitCallback cb in stoppedWorkQueue) {
										cb (null);
									}
									stoppedWorkQueue.Clear ();
								}
								if (resume)
									guiManager.Continue (process.MainThread);
								else if (notifyToClient)
									NotifyTargetEvent (thread, args);
							});
							return;
						}
					}
				}
				
				if (notifyToClient)
					NotifyTargetEvent (thread, args);

			} catch (Exception e) {
				Console.WriteLine ("*** DS.OnTargetEvent1, exception : {0}", e.ToString ());
			}
		}
		void NotifyTargetEvent (MD.Thread thread, MD.TargetEventArgs args)
		{
			if (args.Frame != null)
				activeThread = args.Frame.Thread;
	
			try {
				if (args.Type == MD.TargetEventType.TargetStopped && ((int)args.Data) != 0) {
					DispatchEvent (delegate {
						controller.OnDebuggerOutput (false, string.Format ("Thread {0:x} received signal {1}.\n", args.Frame.Thread.ID, args.Data));
					});
				}

				DL.TargetEventType type;
				
				switch (args.Type) {
					case MD.TargetEventType.Exception: type = DL.TargetEventType.ExceptionThrown; break;
					case MD.TargetEventType.TargetHitBreakpoint: type = DL.TargetEventType.TargetHitBreakpoint; break;
					case MD.TargetEventType.TargetInterrupted: type = DL.TargetEventType.TargetInterrupted; break;
					case MD.TargetEventType.TargetSignaled: type = DL.TargetEventType.TargetSignaled; break;
					case MD.TargetEventType.TargetStopped: type = DL.TargetEventType.TargetStopped; break;
					case MD.TargetEventType.UnhandledException: type = DL.TargetEventType.UnhandledException; break;
					default:
						return;
				}

				OnCleanFrameData ();
				
				DL.TargetEventArgs targetArgs = new DL.TargetEventArgs (type);

				if (args.Type != MD.TargetEventType.TargetExited) {
					ML.TargetObject exception = null;
					if (args.Type == MD.TargetEventType.UnhandledException || args.Type == MD.TargetEventType.Exception)
						exception = args.Frame.ExceptionObject;
					targetArgs.Backtrace = CreateBacktrace (thread, exception);
					targetArgs.Thread = CreateThreadInfo (activeThread);
				}


				running = false;

				DispatchEvent (delegate {
					controller.OnTargetEvent (targetArgs);
				});
			} catch (Exception e) {
				Console.WriteLine ("*** DS.OnTargetEvent2, exception : {0}", e.ToString ());
			}
		}
		void LogEvent (MD.TargetEventArgs args)
		{
			Console.WriteLine ("Server OnTargetEvent: {0} stopped:{1} data:{2} queue:{3} thread:{4} running:{5}", args.Type, args.IsStopped, args.Data, stoppedWorkQueue.Count, args.Frame != null ? args.Frame.Thread : null, running);
		}
		private void OnInitialized (MD.Debugger debugger, Process process)
		{
			Console.WriteLine (">> OnInitialized");
			
			this.process = process;
			this.debugger = debugger;
			
			mdbAdaptor.Process = process;
			
			guiManager = process.StartGUIManager ();

			//FIXME: conditionally add event handlers
			process.TargetOutputEvent += OnTargetOutput;
			
			debugger.ProcessCreatedEvent += OnProcessCreatedEvent;
			debugger.ProcessExecdEvent += OnProcessExecdEvent;
			debugger.ProcessExitedEvent += OnProcessExitedEvent;
			
			debugger.ThreadCreatedEvent += OnThreadCreatedEvent;
			debugger.ThreadExitedEvent += OnThreadExitedEvent;
			
			debugger.TargetExitedEvent += OnTargetExitedEvent;
			guiManager.TargetEvent += OnTargetEvent;

			// Not supported
			//guiManager.BreakpointHitHandler = BreakEventCheck;
			
			activeThread = process.MainThread;
			running = true;
			
			Console.WriteLine ("<< OnInitialized");
		}
		DL.Backtrace CreateBacktrace (MD.Thread thread, ML.TargetObject exception)
		{
			List<MD.StackFrame> frames = new List<MD.StackFrame> ();
			DateTime t = DateTime.Now;
			if (!thread.CurrentFrame.Language.IsManaged) {
				MD.Backtrace bt = thread.GetBacktrace (MD.Backtrace.Mode.Native, max_frames);
				if (bt != null) {
					Console.WriteLine ("GetBacktrace native time: {0} ms n:{1}", (DateTime.Now - t).TotalMilliseconds, bt.Count);
					frames.AddRange (bt.Frames);
				}
			} else {
				t = DateTime.Now;
				MD.Backtrace backtrace = thread.GetBacktrace (MD.Backtrace.Mode.Managed, max_frames);
				if (backtrace != null) {
					Console.WriteLine ("GetBacktrace managed time: {0} ms n:{1}", (DateTime.Now - t).TotalMilliseconds, backtrace.Count);
					frames.AddRange (backtrace.Frames);
				}
			}
			if (frames.Count > 0) {
				BacktraceWrapper wrapper = new BacktraceWrapper (frames.ToArray (), exception);
				return new DL.Backtrace (wrapper);
			} else if (thread.CurrentBacktrace != null) {
				BacktraceWrapper wrapper = new BacktraceWrapper (thread.CurrentBacktrace.Frames, exception);
				return new DL.Backtrace (wrapper);
			}
			return null;
		}
		public abstract void AbortThread (MDB.Thread thread, MDB.RuntimeInvokeResult result);
		private void OnThreadExitedEvent (MD.Debugger debugger, MD.Thread thread)
		{
			WriteDebuggerOutput (string.Format ("Thread {0} exited.\n", thread.ID));
		}
		public virtual void EnableEvent (MDB.Event ev, bool enable)
		{
		}
		private void OnTargetExitedEvent (MD.Debugger debugger)
		{
			exited = true;
			DispatchEvent (delegate {
				controller.OnDebuggerOutput (false, "Target exited.\n");
				DL.TargetEventArgs args = new DL.TargetEventArgs (DL.TargetEventType.TargetExited);
				controller.OnTargetEvent (args);
			});
		}
		public virtual void RemoveEvent (MDB.Event ev)
		{
		}
		bool BreakEventCheck (MD.TargetEventArgs args)
		{
			MD.StackFrame frame = args.Frame;
			if (!(args.Data is int))
				return true;
			
			int eventHandle = (int) args.Data;
			
			DL.BreakEvent be;
			if (!events.TryGetValue (eventHandle, out be))
				return true;
			
			// Check hit count
			if (be.HitCount > 0) {
				be.HitCount--;
				DispatchEvent (delegate {
					NotifyBreakEventUpdate (eventHandle, be.HitCount, null);
				});
				return false;
			}

			MdbEvaluationContext ctx = new MdbEvaluationContext (frame.Thread, frame, null, SessionOptions.EvaluationOptions);
			DL.Breakpoint bp = be as DL.Breakpoint;
			if (bp != null && !string.IsNullOrEmpty (bp.ConditionExpression)) {
				ML.TargetObject val = EvaluateExp (ctx, bp.ConditionExpression);
				if (val == null)
					return false;
				if (bp.BreakIfConditionChanges) {
					string current = evaluator.TargetObjectToExpression (ctx, val).Value;
					string last;
					bool found = lastConditionValue.TryGetValue (eventHandle, out last);
					lastConditionValue [eventHandle] = current;
					if (!found || last == current)
						return false;
				} else {
					ML.TargetFundamentalObject fob = val as ML.TargetFundamentalObject;
					if (fob == null)
						return false;
					object ob = fob.GetObject (frame.Thread);
					if (!(ob is bool) || !(bool)ob)
						return false;
				}
			}

			switch (be.HitAction) {
				case HitAction.Break:
					return true;
				case HitAction.CustomAction:
					return controller.OnCustomBreakpointAction (be.CustomActionId, eventHandle);
				case HitAction.PrintExpression:
					if (string.IsNullOrEmpty (be.TraceExpression) || frame == null)
						return false;
					ML.TargetObject val = EvaluateExp (ctx, be.TraceExpression);
					if (val != null) {
						string str = evaluator.TargetObjectToString (ctx, val);
						DispatchEvent (delegate {
							NotifyBreakEventUpdate (eventHandle, -1, str);
						});
					}
					return false;
			}
			return false;
		}