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:\tmp\MyFile.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模式下是可以调试的,也可以获取堆栈信息。

 

posted on 2017-03-21 23:42  dz45693  阅读(3727)  评论(0编辑  收藏  举报

导航