• Exception的ToString()方法究竟返回的是什么


    最近项目上线后遇到exception没有堆栈信息。所以跟踪一下 源码,其中主要的code如下:

            // Returns the stack trace as a string.  If no stack trace is
            // available, null is returned.
            public virtual String StackTrace
            {
    #if FEATURE_CORECLR
                [System.Security.SecuritySafeCritical] 
    #endif
                get 
                {
                    // By default attempt to include file and line number info
                    return GetStackTrace(true);
                }
            }
    
            // Computes and returns the stack trace as a string
            // Attempts to get source file and line number information if needFileInfo
            // is true.  Note that this requires FileIOPermission(PathDiscovery), and so
            // will usually fail in CoreCLR.  To avoid the demand and resulting
            // SecurityException we can explicitly not even try to get fileinfo.
            #if FEATURE_CORECLR
            [System.Security.SecurityCritical] // auto-generated
            #endif
            private string GetStackTrace(bool needFileInfo)
            {
                string stackTraceString = _stackTraceString;
                string remoteStackTraceString = _remoteStackTraceString;
    
    #if !FEATURE_CORECLR
                if (!needFileInfo)
                {
                    // Filter out file names/paths and line numbers from _stackTraceString and _remoteStackTraceString.
                    // This is used only when generating stack trace for Watson where the strings must be PII-free.
                    stackTraceString = StripFileInfo(stackTraceString, false);
                    remoteStackTraceString = StripFileInfo(remoteStackTraceString, true);
                }
    #endif // !FEATURE_CORECLR
    
                // if no stack trace, try to get one
                if (stackTraceString != null)
                {
                    return remoteStackTraceString + stackTraceString;
                }
                if (_stackTrace == null)
                {
                    return remoteStackTraceString;
                }
    
                // Obtain the stack trace string. Note that since Environment.GetStackTrace
                // will add the path to the source file if the PDB is present and a demand
                // for FileIOPermission(PathDiscovery) succeeds, we need to make sure we 
                // don't store the stack trace string in the _stackTraceString member variable.
                String tempStackTraceString = Environment.GetStackTrace(this, needFileInfo);
                return remoteStackTraceString + tempStackTraceString;
             }
        
    #if FEATURE_CORECLR
            [System.Security.SecuritySafeCritical] 
    #endif
            public override String ToString()
            {
                return ToString(true, true);
            }
    
            #if FEATURE_CORECLR
            [System.Security.SecurityCritical] // auto-generated
            #endif
            private String ToString(bool needFileLineInfo, bool needMessage) {
                String message = (needMessage ? Message : null);
                String s;
    
                if (message == null || message.Length <= 0) {
                    s = GetClassName();
                }
                else {
                    s = GetClassName() + ": " + message;
                }
    
                if (_innerException!=null) {
                    s = s + " ---> " + _innerException.ToString(needFileLineInfo, needMessage) + Environment.NewLine + 
                    "   " + Environment.GetResourceString("Exception_EndOfInnerExceptionStack");
    
                }
    
                string stackTrace = GetStackTrace(needFileLineInfo);
                if (stackTrace != null)
                {
                    s += Environment.NewLine + stackTrace;
                }
    
                return s;
            }
        
            

    Exception的StackTrace属性只返回当前对象的站信息,toString方法首先需要获取当前的Message,然后获取内部exception的tostring方法,最后获取 GetStackTrace方法的返回值,该方法主要内容来源于 Environment.GetStackTrace方法,其实现code如下:

            internal static String GetStackTrace(Exception e, bool needFileInfo)
            {
                // Note: Setting needFileInfo to true will start up COM and set our
                // apartment state.  Try to not call this when passing "true" 
                // before the EE's ExecuteMainMethod has had a chance to set up the
                // apartment state.  -- 
                StackTrace st;
                if (e == null)
                    st = new StackTrace(needFileInfo);
                else
                    st = new StackTrace(e, needFileInfo);
    
                // Do no include a trailing newline for backwards compatibility
                return st.ToString( System.Diagnostics.StackTrace.TraceFormat.Normal );
            }

    调用的是StackTrace的tostring方法,其实现如下:

      internal String ToString(TraceFormat traceFormat)
            {
                bool displayFilenames = true;   // we'll try, but demand may fail
                String word_At = "at";
                String inFileLineNum = "in {0}:line {1}";
    
                if(traceFormat != TraceFormat.NoResourceLookup)
                {
                    word_At = Environment.GetResourceString("Word_At");
                    inFileLineNum = Environment.GetResourceString("StackTrace_InFileLineNumber");
                }
                
                bool fFirstFrame = true;
                StringBuilder sb = new StringBuilder(255);
                for (int iFrameIndex = 0; iFrameIndex < m_iNumOfFrames; iFrameIndex++)
                {
                    StackFrame sf = GetFrame(iFrameIndex);
                    MethodBase mb = sf.GetMethod();
                    if (mb != null)
                    {
                        // We want a newline at the end of every line except for the last
                        if (fFirstFrame)
                            fFirstFrame = false;
                        else
                            sb.Append(Environment.NewLine);
                        
                        sb.AppendFormat(CultureInfo.InvariantCulture, "   {0} ", word_At);
    
                        Type t = mb.DeclaringType;
                         // if there is a type (non global method) print it
                        if (t != null)
                        {                                   
                            sb.Append(t.FullName.Replace('+', '.'));
                            sb.Append(".");
                        }
                        sb.Append(mb.Name);
    
                        // deal with the generic portion of the method
                        if (mb is MethodInfo && ((MethodInfo)mb).IsGenericMethod)
                        {
                            Type[] typars = ((MethodInfo)mb).GetGenericArguments();
                            sb.Append("[");
                            int k=0;
                            bool fFirstTyParam = true;
                            while (k < typars.Length)
                            {
                                if (fFirstTyParam == false)
                                    sb.Append(",");
                                else
                                    fFirstTyParam = false;
    
                                sb.Append(typars[k].Name);             
                                k++;
                            }   
                            sb.Append("]");    
                        }
    
                        // arguments printing
                        sb.Append("(");
                        ParameterInfo[] pi = mb.GetParameters();
                        bool fFirstParam = true;
                        for (int j = 0; j < pi.Length; j++)
                        {
                            if (fFirstParam == false)
                                sb.Append(", ");
                            else
                                fFirstParam = false;
    
                            String typeName = "<UnknownType>";
                            if (pi[j].ParameterType != null)
                                typeName = pi[j].ParameterType.Name;
                            sb.Append(typeName + " " + pi[j].Name);             
                        }   
                        sb.Append(")");
    
                        // source location printing
                        if (displayFilenames && (sf.GetILOffset() != -1))
                        {
                            // If we don't have a PDB or PDB-reading is disabled for the module,
                            // then the file name will be null.
                            String fileName = null;
                            
                            // Getting the filename from a StackFrame is a privileged operation - we won't want
                            // to disclose full path names to arbitrarily untrusted code.  Rather than just omit
                            // this we could probably trim to just the filename so it's still mostly usefull.
                            try
                            {
                                fileName = sf.GetFileName();
                            }
    #if FEATURE_CAS_POLICY
                            catch (NotSupportedException)
                            {
                                // Having a deprecated stack modifier on the callstack (such as Deny) will cause
                                // a NotSupportedException to be thrown.  Since we don't know if the app can
                                // access the file names, we'll conservatively hide them.
                                displayFilenames = false;
                            }
    #endif // FEATURE_CAS_POLICY
                            catch (SecurityException)
                            {
                                // If the demand for displaying filenames fails, then it won't
                                // succeed later in the loop.  Avoid repeated exceptions by not trying again.
                                displayFilenames = false;
                            }
    
                            if (fileName != null) 
                            {
                                // tack on " in c:	mpMyFile.cs:line 5"
                                sb.Append(' ');
                                sb.AppendFormat(CultureInfo.InvariantCulture, inFileLineNum, fileName, sf.GetFileLineNumber());
                            }
                        }
    
    #if FEATURE_EXCEPTIONDISPATCHINFO
                        if (sf.GetIsLastFrameFromForeignExceptionStackTrace())
                        {
                            sb.Append(Environment.NewLine);
                            sb.Append(Environment.GetResourceString("Exception_EndStackTraceFromPreviousThrow"));
                        }
    #endif // FEATURE_EXCEPTIONDISPATCHINFO
                    }
                }
    
                if(traceFormat == TraceFormat.TrailingNewLine)
                    sb.Append(Environment.NewLine);
                
                return sb.ToString(); 
            }

    这个方法最主要的还是   StackFrame sf = GetFrame(iFrameIndex);   它返回跟踪栈的信息。跟踪栈的信息主要来源于StackTrace的构造函数

      public StackTrace(Exception e, bool fNeedFileInfo)
            {
                if (e == null)
                    throw new ArgumentNullException("e");
                Contract.EndContractBlock();
    
                m_iNumOfFrames = 0;
                m_iMethodsToSkip = 0;
                CaptureStackTrace(METHODS_TO_SKIP, fNeedFileInfo, null, e);
            }

    核心实现CaptureStackTrace code如下:

      private void CaptureStackTrace(int iSkip, bool fNeedFileInfo, Thread targetThread,
                                           Exception e)
            {
                m_iMethodsToSkip += iSkip;
        
                StackFrameHelper StackF = new StackFrameHelper(fNeedFileInfo, targetThread);
        
                GetStackFramesInternal(StackF, 0, e);
        
                m_iNumOfFrames = StackF.GetNumberOfFrames();
    
                if (m_iMethodsToSkip > m_iNumOfFrames)
                    m_iMethodsToSkip = m_iNumOfFrames;
    
                if (m_iNumOfFrames != 0)
                {
                    frames = new StackFrame[m_iNumOfFrames];
    
                    for (int i = 0; i < m_iNumOfFrames; i++)
                    {
                        bool fDummy1 = true;
                        bool fDummy2 = true;
                        StackFrame sfTemp = new StackFrame(fDummy1, fDummy2);
    
                        sfTemp.SetMethodBase(StackF.GetMethodBase(i));
                        sfTemp.SetOffset(StackF.GetOffset(i));
                        sfTemp.SetILOffset(StackF.GetILOffset(i));
    
    #if FEATURE_EXCEPTIONDISPATCHINFO
                        sfTemp.SetIsLastFrameFromForeignExceptionStackTrace(StackF.IsLastFrameFromForeignExceptionStackTrace(i));
    #endif // FEATURE_EXCEPTIONDISPATCHINFO
    
                        if (fNeedFileInfo)
                        {
                            sfTemp.SetFileName(StackF.GetFilename (i));
                            sfTemp.SetLineNumber(StackF.GetLineNumber(i));
                            sfTemp.SetColumnNumber(StackF.GetColumnNumber(i));
                        } 
    
                        frames[i] = sfTemp;
                    }
    
                    // CalculateFramesToSkip skips all frames in the System.Diagnostics namespace,
                    // but this is not desired if building a stack trace from an exception.
                    if (e == null)
                        m_iMethodsToSkip += CalculateFramesToSkip(StackF, m_iNumOfFrames);
    
                    m_iNumOfFrames -= m_iMethodsToSkip;
                    if (m_iNumOfFrames < 0)
                       {
                           m_iNumOfFrames = 0;
                       }
                }
    
                // In case this is the same object being re-used, set frames to null
                else
                    frames = null;
            }
        

    顺便提一下,VS在release模式下是可以调试的,也可以获取堆栈信息。

  • 相关阅读:
    linux下vim命令详解
    Linux 指令篇:档案目录管理chown
    分级渲染百度地图路段红黄绿功能
    矢量数据的属性值的添加、编辑、删除
    Extjs+C# 文件上传
    IBATIS 的动态SQL语句拼接
    一个DIV位于另一个div的右下角
    ArcGIS JavaScript API 添加点
    将Excel数据导入至SqlServer中
    Extjs 图表控件,可修改X轴与Y轴坐标
  • 原文地址:https://www.cnblogs.com/majiang/p/6597377.html
Copyright © 2020-2023  润新知