using System; using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.VisualStudio; using Microsoft.VisualStudio.Debugger.Interop; using System.Diagnostics; using Cosmos.Debug.Common; using System.Windows.Forms; namespace Cosmos.Debug.VSDebugEngine { // Represents a logical stack frame on the thread stack. // Also implements the IDebugExpressionContext interface, which allows expression evaluation and watch windows. class AD7StackFrame : IDebugStackFrame2, IDebugExpressionContext2 { readonly AD7Engine m_engine; readonly AD7Thread m_thread; //readonly X86ThreadContext m_threadContext; private string m_documentName; private string m_functionName; private uint m_lineNum; private bool m_hasSource; private int m_numParameters; private int m_numLocals; internal DebugInfo.Local_Argument_Info[] mLocalInfos; internal DebugInfo.Local_Argument_Info[] mArgumentInfos; // An array of this frame's parameters private DebugLocalInfo[] m_parameters; // An array of this frame's locals private DebugLocalInfo[] m_locals; private AD7Process mProcess; public AD7StackFrame(AD7Engine engine, AD7Thread thread, AD7Process process)//, X86ThreadContext threadContext) { System.Diagnostics.Debug.WriteLine("In AD7StackFrame..ctor"); m_engine = engine; m_thread = thread; mProcess = process; var xProcess = m_engine.mProcess; m_hasSource = xProcess.mCurrentAddress.HasValue && xProcess.mSourceMappings.ContainsKey(xProcess.mCurrentAddress.Value); if (m_hasSource) { var xSourceMapping = xProcess.mSourceMappings[xProcess.mCurrentAddress.Value]; m_documentName = xSourceMapping.SourceFile; m_functionName = xSourceMapping.MethodName; m_lineNum = (uint)xSourceMapping.Line; m_numLocals = 0; m_numParameters = 0; string xCurrentInstructionLabelName = String.Empty; if (xProcess.mAddressLabelMappings.TryGetValue(xProcess.mCurrentAddress.Value, out xCurrentInstructionLabelName)) { var xSymbolInfo = xProcess.mDebugInfoDb.ReadSymbolByLabelName(xCurrentInstructionLabelName); if (xSymbolInfo != null) { var xAllInfos = xProcess.mDebugInfoDb.ReadAllLocalsArgumentsInfosByMethodLabelName(xSymbolInfo.MethodName); mLocalInfos = (from item in xAllInfos where !item.IsArgument select item).ToArray(); mArgumentInfos = (from item in xAllInfos where item.IsArgument select item).ToArray(); m_numLocals = mLocalInfos.Length; m_numParameters = mArgumentInfos.Length; if (m_numParameters > 0) { m_parameters = new DebugLocalInfo[m_numParameters]; for (int i = 0; i < m_numParameters; i++) { m_parameters[i] = new DebugLocalInfo { Name = mArgumentInfos[i].Name, Index = i, IsLocal = false }; } } if (m_numLocals > 0) { m_locals = new DebugLocalInfo[m_numLocals]; for (int i = 0; i < m_numLocals; i++) { m_locals[i] = new DebugLocalInfo { Name = mLocalInfos[i].Name, Index = i, IsLocal = true }; } //m_locals = new VariableInformation[m_numLocals]; //m_engine.DebuggedProcess.GetFunctionLocalsByIP(m_threadContext.eip, m_threadContext.ebp, m_locals); } } } else { MessageBox.Show("No Symbol found for address 0x" + xProcess.mCurrentAddress.Value.ToString("X8").ToUpper()); } xProcess.DebugMsg(String.Format("StackFrame: Returning: {0}#{1}[{2}]", m_documentName, m_functionName, m_lineNum)); } else { xProcess.DebugMsg("StackFrame: No Source available"); } // If source information is available, create the collections of locals and parameters and populate them with // values from the debuggee. if (m_hasSource) { if (m_numParameters > 0) { //m_parameters = new VariableInformation[m_numParameters]; //m_engine.DebuggedProcess.GetFunctionArgumentsByIP(m_threadContext.eip, m_threadContext.ebp, m_parameters); } if (m_numLocals > 0) { //m_locals = new VariableInformation[m_numLocals]; //m_engine.DebuggedProcess.GetFunctionLocalsByIP(m_threadContext.eip, m_threadContext.ebp, m_locals); } } System.Diagnostics.Debug.WriteLine("\tHasSource = " + m_hasSource); System.Diagnostics.Debug.WriteLine("\tm_numParameters = " + m_numParameters); System.Diagnostics.Debug.WriteLine("\tm_numLocals = " + m_numLocals); } #region Non-interface methods // Construct a FRAMEINFO for this stack frame with the requested information. public void SetFrameInfo(enum_FRAMEINFO_FLAGS dwFieldSpec, out FRAMEINFO frameInfo) { System.Diagnostics.Debug.WriteLine("In AD7StackFrame.SetFrameInfo"); System.Diagnostics.Debug.WriteLine("\tdwFieldSpec = " + dwFieldSpec.ToString()); frameInfo = new FRAMEINFO(); //uint ip = m_threadContext.eip; //DebuggedModule module = null;// m_engine.DebuggedProcess.ResolveAddress(ip); // The debugger is asking for the formatted name of the function which is displayed in the callstack window. // There are several optional parts to this name including the module, argument types and values, and line numbers. // The optional information is requested by setting flags in the dwFieldSpec parameter. if (dwFieldSpec.HasFlag(enum_FRAMEINFO_FLAGS.FIF_FUNCNAME)) { // If there is source information, construct a string that contains the module name, function name, and optionally argument names and values. if (m_hasSource) { frameInfo.m_bstrFuncName = ""; if (dwFieldSpec.HasFlag(enum_FRAMEINFO_FLAGS.FIF_FUNCNAME_MODULE)) { // m_ //frameInfo.m_bstrFuncName = System.IO.Path.GetFileName(module.Name) + "!"; frameInfo.m_bstrFuncName = "module!"; } frameInfo.m_bstrFuncName += m_functionName; if (dwFieldSpec.HasFlag(enum_FRAMEINFO_FLAGS.FIF_FUNCNAME_ARGS) && m_numParameters > 0) { frameInfo.m_bstrFuncName += "("; for (int i = 0; i < m_parameters.Length; i++) { if ((dwFieldSpec & enum_FRAMEINFO_FLAGS.FIF_FUNCNAME_ARGS_TYPES) != 0) { //frameInfo.m_bstrFuncName += m_parameters[i]. + " "; frameInfo.m_bstrFuncName += "ParamType "; } if ((dwFieldSpec & enum_FRAMEINFO_FLAGS.FIF_FUNCNAME_ARGS_NAMES) != 0) { frameInfo.m_bstrFuncName += m_parameters[i].Name; } // if ((dwFieldSpec & (uint)enum_FRAMEINFO_FLAGS.FIF_FUNCNAME_ARGS_VALUES) != 0) // { // frameInfo.m_bstrFuncName += "=" + m_parameters[i].m_value; // } if (i < m_parameters.Length - 1) { frameInfo.m_bstrFuncName += ", "; } } frameInfo.m_bstrFuncName += ")"; } if (dwFieldSpec.HasFlag(enum_FRAMEINFO_FLAGS.FIF_FUNCNAME_LINES)) { frameInfo.m_bstrFuncName += " Line:" + m_lineNum.ToString(); } } else { // No source information, so only return the module name and the instruction pointer. if (dwFieldSpec.HasFlag(enum_FRAMEINFO_FLAGS.FIF_FUNCNAME_MODULE)) { //frameInfo.m_bstrFuncName = EngineUtils.GetAddressDescription(module, ip); } else { //frameInfo.m_bstrFuncName = EngineUtils.GetAddressDescription(null, ip); } } frameInfo.m_dwValidFields |= enum_FRAMEINFO_FLAGS.FIF_FUNCNAME; } // The debugger is requesting the name of the module for this stack frame. if (dwFieldSpec.HasFlag(enum_FRAMEINFO_FLAGS.FIF_MODULE)) { frameInfo.m_bstrModule = "module"; frameInfo.m_dwValidFields |= enum_FRAMEINFO_FLAGS.FIF_MODULE; } // The debugger is requesting the range of memory addresses for this frame. // For the sample engine, this is the contents of the frame pointer. if (dwFieldSpec.HasFlag(enum_FRAMEINFO_FLAGS.FIF_STACKRANGE)) { //frameInfo.m_addrMin = m_threadContext.ebp; //frameInfo.m_addrMax = m_threadContext.ebp; frameInfo.m_dwValidFields |= enum_FRAMEINFO_FLAGS.FIF_STACKRANGE; } // The debugger is requesting the IDebugStackFrame2 value for this frame info. if (dwFieldSpec.HasFlag(enum_FRAMEINFO_FLAGS.FIF_FRAME)) { frameInfo.m_pFrame = this; frameInfo.m_dwValidFields |= enum_FRAMEINFO_FLAGS.FIF_FRAME; } // Does this stack frame of symbols loaded? if (dwFieldSpec.HasFlag(enum_FRAMEINFO_FLAGS.FIF_DEBUGINFO)) { frameInfo.m_fHasDebugInfo = m_hasSource ? 1 : 0; frameInfo.m_dwValidFields |= enum_FRAMEINFO_FLAGS.FIF_DEBUGINFO; } // Is this frame stale? if (dwFieldSpec.HasFlag(enum_FRAMEINFO_FLAGS.FIF_STALECODE)) { frameInfo.m_fStaleCode = 0; frameInfo.m_dwValidFields |= enum_FRAMEINFO_FLAGS.FIF_STALECODE; } // The debugger would like a pointer to the IDebugModule2 that contains this stack frame. if (dwFieldSpec.HasFlag(enum_FRAMEINFO_FLAGS.FIF_DEBUG_MODULEP)) { if (m_engine.mModule != null) { frameInfo.m_pModule = m_engine.mModule; frameInfo.m_dwValidFields |= enum_FRAMEINFO_FLAGS.FIF_DEBUG_MODULEP; } } } // Construct an instance of IEnumDebugPropertyInfo2 for the combined locals and parameters. private void CreateLocalsPlusArgsProperties(out uint elementsReturned, out IEnumDebugPropertyInfo2 enumObject) { elementsReturned = 0; int localsLength = 0; if (m_locals != null) { localsLength = m_locals.Length; elementsReturned += (uint)localsLength; } if (m_parameters != null) { elementsReturned += (uint)m_parameters.Length; } DEBUG_PROPERTY_INFO[] propInfo = new DEBUG_PROPERTY_INFO[elementsReturned]; if (m_locals != null) { for (int i = 0; i < m_locals.Length; i++) { AD7Property property = new AD7Property(m_locals[i], this.mProcess, this); propInfo[i] = property.ConstructDebugPropertyInfo(enum_DEBUGPROP_INFO_FLAGS.DEBUGPROP_INFO_STANDARD); } } if (m_parameters != null) { for (int i = 0; i < m_parameters.Length; i++) { AD7Property property = new AD7Property(m_parameters[i], this.mProcess, this); propInfo[localsLength + i] = property.ConstructDebugPropertyInfo(enum_DEBUGPROP_INFO_FLAGS.DEBUGPROP_INFO_STANDARD); } } enumObject = new AD7PropertyInfoEnum(propInfo); } // Construct an instance of IEnumDebugPropertyInfo2 for the locals collection only. private void CreateLocalProperties(out uint elementsReturned, out IEnumDebugPropertyInfo2 enumObject) { elementsReturned = (uint)m_locals.Length; DEBUG_PROPERTY_INFO[] propInfo = new DEBUG_PROPERTY_INFO[m_locals.Length]; for (int i = 0; i < propInfo.Length; i++) { AD7Property property = new AD7Property(m_locals[i], mProcess, this); propInfo[i] = property.ConstructDebugPropertyInfo(enum_DEBUGPROP_INFO_FLAGS.DEBUGPROP_INFO_STANDARD); } enumObject = new AD7PropertyInfoEnum(propInfo); } // Construct an instance of IEnumDebugPropertyInfo2 for the parameters collection only. private void CreateParameterProperties(out uint elementsReturned, out IEnumDebugPropertyInfo2 enumObject) { elementsReturned = (uint)m_parameters.Length; DEBUG_PROPERTY_INFO[] propInfo = new DEBUG_PROPERTY_INFO[m_parameters.Length]; for (int i = 0; i < propInfo.Length; i++) { AD7Property property = new AD7Property(m_parameters[i], mProcess, this); propInfo[i] = property.ConstructDebugPropertyInfo(enum_DEBUGPROP_INFO_FLAGS.DEBUGPROP_INFO_STANDARD); } enumObject = new AD7PropertyInfoEnum(propInfo); } #endregion #region IDebugStackFrame2 Members // Creates an enumerator for properties associated with the stack frame, such as local variables. // The sample engine only supports returning locals and parameters. Other possible values include // class fields (this pointer), registers, exceptions... int IDebugStackFrame2.EnumProperties(enum_DEBUGPROP_INFO_FLAGS dwFields, uint nRadix, ref Guid guidFilter, uint dwTimeout, out uint elementsReturned, out IEnumDebugPropertyInfo2 enumObject) { int hr; elementsReturned = 0; enumObject = null; try { if (guidFilter == AD7Guids.guidFilterLocalsPlusArgs || guidFilter == AD7Guids.guidFilterAllLocalsPlusArgs) { CreateLocalsPlusArgsProperties(out elementsReturned, out enumObject); hr = VSConstants.S_OK; } else if (guidFilter == AD7Guids.guidFilterLocals) { CreateLocalProperties(out elementsReturned, out enumObject); hr = VSConstants.S_OK; } else if (guidFilter == AD7Guids.guidFilterArgs) { CreateParameterProperties(out elementsReturned, out enumObject); hr = VSConstants.S_OK; } else { hr = VSConstants.E_NOTIMPL; } } //catch (ComponentException e) //{ // return e.HResult; //} catch (Exception e) { return EngineUtils.UnexpectedException(e); } return hr; } // Gets the code context for this stack frame. The code context represents the current instruction pointer in this stack frame. int IDebugStackFrame2.GetCodeContext(out IDebugCodeContext2 memoryAddress) { memoryAddress = null; try { //memoryAddress = new AD7MemoryAddress(m_engine, m_threadContext.eip); return VSConstants.S_OK; } //catch (ComponentException e) //{ // return e.HResult; //} catch (Exception e) { return EngineUtils.UnexpectedException(e); } } // Gets a description of the properties of a stack frame. // Calling the IDebugProperty2::EnumChildren method with appropriate filters can retrieve the local variables, method parameters, registers, and "this" // pointer associated with the stack frame. The debugger calls EnumProperties to obtain these values in the sample. int IDebugStackFrame2.GetDebugProperty(out IDebugProperty2 property) { throw new NotImplementedException(); } // Gets the document context for this stack frame. The debugger will call this when the current stack frame is changed // and will use it to open the correct source document for this stack frame. int IDebugStackFrame2.GetDocumentContext(out IDebugDocumentContext2 docContext) { docContext = null; try { if (m_hasSource) { // Assume all lines begin and end at the beginning of the line. TEXT_POSITION begTp = new TEXT_POSITION(); begTp.dwColumn = 0; begTp.dwLine = m_lineNum - 1; TEXT_POSITION endTp = new TEXT_POSITION(); endTp.dwColumn = 0; endTp.dwLine = m_lineNum - 1; docContext = new AD7DocumentContext(m_documentName, begTp, endTp, null); return VSConstants.S_OK; } } //catch (ComponentException e) //{ // return e.HResult; //} catch (Exception e) { return EngineUtils.UnexpectedException(e); } return VSConstants.S_FALSE; } // Gets an evaluation context for expression evaluation within the current context of a stack frame and thread. // Generally, an expression evaluation context can be thought of as a scope for performing expression evaluation. // Call the IDebugExpressionContext2::ParseText method to parse an expression and then call the resulting IDebugExpression2::EvaluateSync // or IDebugExpression2::EvaluateAsync methods to evaluate the parsed expression. int IDebugStackFrame2.GetExpressionContext(out IDebugExpressionContext2 ppExprCxt) { ppExprCxt = (IDebugExpressionContext2)this; return VSConstants.S_OK; } // Gets a description of the stack frame. int IDebugStackFrame2.GetInfo(enum_FRAMEINFO_FLAGS dwFieldSpec, uint nRadix, FRAMEINFO[] pFrameInfo) { try { SetFrameInfo((enum_FRAMEINFO_FLAGS)dwFieldSpec, out pFrameInfo[0]); return VSConstants.S_OK; } //catch (ComponentException e) //{ // return e.HResult; //} catch (Exception e) { return EngineUtils.UnexpectedException(e); } } // Gets the language associated with this stack frame. // In this sample, all the supported stack frames are C++ int IDebugStackFrame2.GetLanguageInfo(ref string pbstrLanguage, ref Guid pguidLanguage) { pbstrLanguage = "CSharp"; pguidLanguage = AD7Guids.guidLanguageCSharp; return VSConstants.S_OK; } // Gets the name of the stack frame. // The name of a stack frame is typically the name of the method being executed. int IDebugStackFrame2.GetName(out string name) { name = null; try { //name = EngineUtils.GetAddressDescription(null, m_threadContext.eip); return VSConstants.S_OK; } //catch (ComponentException e) //{ // return e.HResult; //} catch (Exception e) { return EngineUtils.UnexpectedException(e); } } // Gets a machine-dependent representation of the range of physical addresses associated with a stack frame. int IDebugStackFrame2.GetPhysicalStackRange(out ulong addrMin, out ulong addrMax) { addrMin = 0;// m_threadContext.ebp; addrMax = 0;// m_threadContext.ebp; return VSConstants.S_OK; } // Gets the thread associated with a stack frame. int IDebugStackFrame2.GetThread(out IDebugThread2 thread) { thread = m_thread; return VSConstants.S_OK; } #endregion #region IDebugExpressionContext2 Members // Retrieves the name of the evaluation context. // The name is the description of this evaluation context. It is typically something that can be parsed by an expression evaluator // that refers to this exact evaluation context. For example, in C++ the name is as follows: // "{ function-name, source-file-name, module-file-name }" int IDebugExpressionContext2.GetName(out string pbstrName) { throw new NotImplementedException(); } // Parses a text-based expression for evaluation. // The engine sample only supports locals and parameters so the only task here is to check the names in those collections. int IDebugExpressionContext2.ParseText(string pszCode, enum_PARSEFLAGS dwFlags, uint nRadix, out IDebugExpression2 ppExpr, out string pbstrError, out uint pichError) { //System.Windows.Forms.MessageBox.Show("pszCode: " + pszCode); pbstrError = ""; pichError = 0; ppExpr = null; try { //if (m_parameters != null) { //foreach (VariableInformation currVariable in m_parameters) { //if (String.CompareOrdinal(currVariable.m_name, pszCode) == 0) { //ppExpr = new AD7Expression(currVariable); //return VSConstants.S_OK; } } } //if (m_locals != null) { //foreach (VariableInformation currVariable in m_locals) { //if (String.CompareOrdinal(currVariable.m_name, pszCode) == 0) { //ppExpr = new AD7Expression(currVariable); //return VSConstants.S_OK; } } } pbstrError = "Invalid Expression"; pichError = (uint)pbstrError.Length; return VSConstants.S_FALSE; } //catch (ComponentException e) //{ // return e.HResult; //} catch (Exception e) { return EngineUtils.UnexpectedException(e); } } #endregion } }