// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using Debugger;
using Debugger.AddIn.TreeModel;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop.Services;
namespace ICSharpCode.SharpDevelop.Gui.Pads
{
///
/// Interaction logic for CallStackPadContent.xaml
///
public partial class CallStackPadContent : UserControl
{
Process debuggedProcess;
public CallStackPadContent()
{
InitializeComponent();
view.ContextMenu = CreateMenu();
}
ContextMenu CreateMenu()
{
MenuItem extMethodsItem = new MenuItem();
extMethodsItem.Header = ResourceService.GetString("MainWindow.Windows.Debug.CallStack.ShowExternalMethods");
extMethodsItem.IsChecked = DebuggingOptions.Instance.ShowExternalMethods;
extMethodsItem.Click += delegate {
extMethodsItem.IsChecked = DebuggingOptions.Instance.ShowExternalMethods = !DebuggingOptions.Instance.ShowExternalMethods;
RefreshPad();
};
MenuItem moduleItem = new MenuItem();
moduleItem.Header = ResourceService.GetString("MainWindow.Windows.Debug.CallStack.ShowModuleNames");
moduleItem.IsChecked = DebuggingOptions.Instance.ShowModuleNames;
moduleItem.Click += delegate {
moduleItem.IsChecked = DebuggingOptions.Instance.ShowModuleNames = !DebuggingOptions.Instance.ShowModuleNames;
RefreshPad();
};
MenuItem argNamesItem = new MenuItem();
argNamesItem.Header = ResourceService.GetString("MainWindow.Windows.Debug.CallStack.ShowArgumentNames");
argNamesItem.IsChecked = DebuggingOptions.Instance.ShowArgumentNames;
argNamesItem.Click += delegate {
argNamesItem.IsChecked = DebuggingOptions.Instance.ShowArgumentNames = !DebuggingOptions.Instance.ShowArgumentNames;
RefreshPad();
};
MenuItem argValuesItem = new MenuItem();
argValuesItem.Header = ResourceService.GetString("MainWindow.Windows.Debug.CallStack.ShowArgumentValues");
argValuesItem.IsChecked = DebuggingOptions.Instance.ShowArgumentValues;
argValuesItem.Click += delegate {
argValuesItem.IsChecked = DebuggingOptions.Instance.ShowArgumentValues = !DebuggingOptions.Instance.ShowArgumentValues;
RefreshPad();
};
MenuItem lineItem = new MenuItem();
lineItem.Header = ResourceService.GetString("MainWindow.Windows.Debug.CallStack.ShowLineNumber");
lineItem.IsChecked = DebuggingOptions.Instance.ShowLineNumbers;
lineItem.Click += delegate {
lineItem.IsChecked = DebuggingOptions.Instance.ShowLineNumbers = !DebuggingOptions.Instance.ShowLineNumbers;
RefreshPad();
};
return new ContextMenu() {
Items = {
extMethodsItem,
new Separator(),
moduleItem,
argNamesItem,
argValuesItem,
lineItem
}
};
}
public void SelectProcess(Process process)
{
if (debuggedProcess != null) {
debuggedProcess.Paused -= debuggedProcess_Paused;
}
debuggedProcess = process;
if (debuggedProcess != null) {
debuggedProcess.Paused += debuggedProcess_Paused;
}
RefreshPad();
}
void debuggedProcess_Paused(object sender, ProcessEventArgs e)
{
RefreshPad();
}
void View_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (debuggedProcess == null)
return;
if (debuggedProcess.IsPaused) {
CallStackItem item = view.SelectedItem as CallStackItem;
if (item == null)
return;
if (item.Frame.HasSymbols) {
if (debuggedProcess.SelectedThread != null) {
debuggedProcess.SelectedThread.SelectedStackFrame = item.Frame;
debuggedProcess.OnPaused(); // Force refresh of pads
}
} else {
MessageService.ShowMessage("${res:MainWindow.Windows.Debug.CallStack.CannotSwitchWithoutSymbols}", "${res:MainWindow.Windows.Debug.CallStack.FunctionSwitch}");
}
} else {
MessageService.ShowMessage("${res:MainWindow.Windows.Debug.CallStack.CannotSwitchWhileRunning}", "${res:MainWindow.Windows.Debug.CallStack.FunctionSwitch}");
}
}
void View_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter) {
View_MouseLeftButtonUp(sender, null);
e.Handled = true;
}
}
public void RefreshPad()
{
if (debuggedProcess == null || debuggedProcess.IsRunning || debuggedProcess.SelectedThread == null) {
view.ItemsSource = null;
return;
}
List items = null;
StackFrame activeFrame = null;
using(new PrintTimes("Callstack refresh")) {
try {
Utils.DoEvents(debuggedProcess);
items = CreateItems().ToList();
activeFrame = debuggedProcess.SelectedThread.SelectedStackFrame;
} catch(AbortedBecauseDebuggeeResumedException) {
} catch(System.Exception) {
if (debuggedProcess == null || debuggedProcess.HasExited) {
// Process unexpectedly exited
} else {
throw;
}
}
}
view.ItemsSource = items;
view.SelectedItem = items != null ? items.FirstOrDefault(item => object.Equals(activeFrame, item.Frame)) : null;
}
IEnumerable CreateItems()
{
bool showExternalMethods = DebuggingOptions.Instance.ShowExternalMethods;
bool lastItemIsExternalMethod = false;
foreach (StackFrame frame in debuggedProcess.SelectedThread.GetCallstack(100)) {
CallStackItem item;
if (frame.HasSymbols || showExternalMethods) {
// Show the method in the list
item = new CallStackItem() { Name = GetFullName(frame), Language = "" };
lastItemIsExternalMethod = false;
} else {
// Show [External methods] in the list
if (lastItemIsExternalMethod) continue;
item = new CallStackItem() { Name = ResourceService.GetString("MainWindow.Windows.Debug.CallStack.ExternalMethods"), Language = "" };
lastItemIsExternalMethod = true;
}
item.Frame = frame;
yield return item;
Utils.DoEvents(debuggedProcess);
}
}
internal static string GetFullName(StackFrame frame)
{
bool showArgumentNames = DebuggingOptions.Instance.ShowArgumentNames;
bool showArgumentValues = DebuggingOptions.Instance.ShowArgumentValues;
bool showLineNumber = DebuggingOptions.Instance.ShowLineNumbers;
bool showModuleNames = DebuggingOptions.Instance.ShowModuleNames;
StringBuilder name = new StringBuilder();
// show modules names
if (showModuleNames) {
name.Append(frame.MethodInfo.DebugModule.ToString());
name.Append("!");
}
name.Append(frame.MethodInfo.DeclaringType.Name);
name.Append('.');
name.Append(frame.MethodInfo.Name);
if (showArgumentNames || showArgumentValues) {
name.Append("(");
for (int i = 0; i < frame.ArgumentCount; i++) {
string parameterName = null;
string argValue = null;
if (showArgumentNames) {
try {
parameterName = frame.MethodInfo.GetParameters()[i].Name;
} catch { }
if (parameterName == "") parameterName = null;
}
if (showArgumentValues) {
try {
argValue = frame.GetArgumentValue(i).AsString;
} catch { }
}
if (parameterName != null && argValue != null) {
name.Append(parameterName);
name.Append("=");
name.Append(argValue);
}
if (parameterName != null && argValue == null) {
name.Append(parameterName);
}
if (parameterName == null && argValue != null) {
name.Append(argValue);
}
if (parameterName == null && argValue == null) {
name.Append(ResourceService.GetString("Global.NA"));
}
if (i < frame.ArgumentCount - 1) {
name.Append(", ");
}
}
name.Append(")");
}
// line number
if (showLineNumber) {
var segmentCode = frame.GetSegmentForOffet(0);
if (segmentCode != null) {
name.Append(ResourceService.GetString("MainWindow.Windows.Debug.CallStack.LineString"));
name.Append(segmentCode.StartLine.ToString());
name.Append("->");
name.Append(frame.NextStatement.StartLine.ToString());
}
}
return name.ToString();
}
}
public class CallStackItem
{
public string Name { get; set; }
public string Language { get; set; }
public StackFrame Frame { get; set; }
public Brush FontColor {
get { return Frame == null || Frame.HasSymbols ? Brushes.Black : Brushes.Gray; }
}
}
public class CallStackPad : DebuggerPad
{
CallStackPadContent callStackList;
public override object Control {
get {
return callStackList;
}
}
protected override void InitializeComponents()
{
callStackList = new CallStackPadContent();
}
protected override void SelectProcess(Process process)
{
callStackList.SelectProcess(process);
}
public override void RefreshPad()
{
callStackList.RefreshPad();
}
}
}