.NETGURU
System.Diagnostics.StackTrace and Exceptions
Messages   Related Types
This message was discovered on microsoft.public.dotnet.framework.sdk.
Responses highlighted in red are from those people who are likely to be able to contribute good, authoratitive information to this discussion. They include Microsoft employees, MVP's and others who IMHO contribute well to these kinds of discussions.
Post a new message to this list...

Michael McGuire
I am creating a custom exception that derives from ApplicationException to
automatically capture additional information that is not included in the base
Exception class. One of the pieces of information I am wanting to capture is
a more complete call stack than the one provided by the StackTrace property
in the base Exception class. By "more complete", I mean two things:

1) Include the entire call stack when the exception was generated. The base
Exception class only captures the call stack from where it is thrown, to the
method where you catch the exception and call the property. For intsance, if
I had the following:

Method1 calls Method 2
Method 2 calls Method 3
Method 3 calls Method 4
Method 4 raises an exception
The exception is caught in Method 2

In the StackTrace, the lowest call on the stack is Method 4, and the highest
on the stack is Method 2.

What I want to capture is the entire call stack for a more complete picture
of what is occurring. This information will be automatically logged to a
central repository.

2) An easier way to programatically examine the stack. The StackTrace
property on the base Exception class is a simple string that is formatted for
you. I would like to expose a property called "CallStack" that is an array
of what I call StackRecords that have the method, type, line number and file
information in a convenient to use format.

To accomplish this, I've created a function that is called in the
constructor of my custom exception (I'll call it MyException from now on).
Here is the code that gets the information:

private void CreateCallStack()
{
    StackTrace st = new StackTrace(true);

    Type baseType = typeof(MyException);
    int i = 0;

    // Skip over any of the stack frames for the MyException class or any
derived class.
    // We don't want to include those in the call stack we create.
    for (; i < st.FrameCount; i++)
    {
        // This should always work, right?
        Type methodType = st.GetFrame(i).GetMethod().DeclaringType;

        if (!(methodType.Equals(baseType) || methodType.IsSubclassOf(baseType)))
            break;

    }

    // TODO: Review: Should the callStack variable be null if we don't have any
StackTrace due to
    //                 code optimization or should it be a zero length array?
    if (i < st.FrameCount)
    {

        callStack = new StackRecord[st.FrameCount - i];
        int start = i;

        for (; i < st.FrameCount; i++)
        {
            StackFrame sf = st.GetFrame(i);
            MethodBase method = sf.GetMethod();
            Type methodType = method.DeclaringType;

            callStack[i - start] = new StackRecord(method.Name, methodType.FullName,
sf.GetFileName(), sf.GetFileLineNumber().ToString());
        }

    }

}

(My advance apologies if this code isn't formatted well, I'm using the
web-based newsgroup reader from Microsoft, so I'm not sure how to control
formatting)

I'm using the System.Diagnostics.StackTrace class to get access to the full
StackTrace of the call. Note that the first for loop is present to skip over
any activation records for the current method call (CreateCallStack) or the
constructors for this base MyException class or any derived exception class.
I only want to capture the call stack up to the method that created my
exception.

Everything seems to work perfectly, except for one thing. For some reason
the line numbers don't seem to be properly listed. I know that using
StackTrace, especially the line numbers and file names, is not always going
to give me the proper stack trace due to code optimizations, etc. However,
when I examine the StackTrace property of this exception when testing, it
contains the proper line numbers. Here is some example output:

StackTrace for my exception (uses base Exception StackTrace):
at MyNamespace.MainClass.Level5() in [removed file path
stuff]\mainclass.cs:line 80
at MyNamespace.MainClass.Level4() in [removed file path
stuff]\mainclass.cs:line 69
at MyNamespace.MainClass.Level3() in [removed file path
stuff]\mainclass.cs:line 50

CallStack for my exception (uses the CallStack property I've created for
MyException)
MyNamespace.MainClass.Level5 in [removed file path stuff]\mainclass.cs at
line 80
MyNamespace.MainClass.Level4 in [removed file path stuff]\mainclass.cs at
line 71
MyNamespace.MainClass.Level3 in [removed file path stuff]\mainclass.cs at
line 50
MyNamespace.MainClass.Level2 in [removed file path stuff]\mainclass.cs at
line 44
MyNamespace.MainClass.Level1 in [removed file path stuff]\mainclass.cs
at line 39
MyNamespace.MainClass.Main in [removed file path stuff]\mainclass.cs at
line 17

My test code has just a few simple routines that call each other to fill up
the call stack. The exception is raised in Level5() and these two messages
are printed to the console in Level3(). Here is the test code:

/// <summary>
/// Summary description for MainClass.
/// </summary>
public class MainClass
{

    public static void Main(string[] args)
    {
        Level1();

        // Blah!
    }                // <--- This is line 17

    public static void ReflectProperties(Object o)
    {
        Type t = o.GetType();

        Console.WriteLine("Reflecting properties for type: {0}", t.Name);

        PropertyInfo[] properties = t.GetProperties(BindingFlags.Instance |
BindingFlags.Public);

        for (int i = 0; i < properties.Length; i++)
            Console.WriteLine("\t{0}: {1}", properties[i].Name,
properties[i].GetValue(o, null));

    }

    public static void Level1()
    {
        // dude

        Level2();

        // Messing up lines
    }                // <--- This is line 39

    public static void Level2()
    {
        Level3();
    }                // <--- This is line 44

    public static void Level3()
    {
        try
        {
            Level4();    // <--- This is line 50
        }
        catch (MyException e)
        {
            Console.WriteLine("Caught MyException or derived.");
            ReflectProperties(e);

            Console.WriteLine("Writing out Call Stack:");

            for (int i = 0; i < e.CallStack.Length; i++)
            {
                StackRecord sr = e.CallStack[i];
                Console.WriteLine("{0}{1}", new String(' ', i), sr.ToString());
            }
        }
    }

    public static void Level4()
    {
        Level5();        // <--- This is line 69
        // blah
    }                // <--- This is line 71

    public static void Level5()
    {
        /*
        TestException e = new TestException("Blah");
        e.CreateCallStack();
        throw e;
        */
        throw new TestException("Blah");
    }

}

I marked the line numbers that appear in both stack traces. The StackTrace
for the Exception is "more" correct in that the line numbers it give point to
where they should. It looks as if the stack information I collect has the
line numbers pointing to the end of the method, rather than where the
execution currently is located within the method.

I looked at the code for the base Exception object in Rotor for help, and it
looks like it pretty much uses the StackTrace also to retrieve information.
The StackTrace implementation is mostly internal to CLR and way above my
head, so that was a dead end.

My question is why would the base Exception class be reporting different
(and correct) line numbers from my code? I would like to have my code return
the proper line numbers for accuracy.

Thanks for any help, and for reading through this huge post. :-)

Michael McGuire

Reply to this message...
 
    
Yan-Hong Huang[MSFT] (VIP)
Hi Mike,

We did exactly reproduce the problem and reported it to the product team.
Currently it was assigned to a design engineer and we are performing
research on it. It may need some more time. Please rest assured that we
will post the update here as soon as possible.

If you have any more concerns on it, please feel free to post here. Thanks
very much for your understanding.

Best regards,
Yanhong Huang
Microsoft Community Support

Get Secure! ¨C www.microsoft.com/security
Register to Access MSDN Managed Newsgroups!
-http://support.microsoft.com/default.aspx?scid=/servicedesks/msdn/nospam.as
p&SD=msdn

This posting is provided "AS IS" with no warranties, and confers no rights.

Reply to this message...
 
    
Michael McGuire
Ok, I will keep an eye on this thread for updates.

Thanks for your help.

"Yan-Hong Huang[MSFT]" wrote:

[Original message clipped]

Reply to this message...
 
    
Yan-Hong Huang[MSFT] (VIP)
Hi Mike,

Our product group has confirmed it is a bug already. However, the problem
seems complicated. I have pushed them but there is no further update yet.
The issue may need some more time. But please rest assured that we are do
our best on it.

Thanks very much for your understanding.

Best regards,
Yanhong Huang
Microsoft Community Support

Get Secure! ¨C www.microsoft.com/security
Register to Access MSDN Managed Newsgroups!
-http://support.microsoft.com/default.aspx?scid=/servicedesks/msdn/nospam.as
p&SD=msdn

This posting is provided "AS IS" with no warranties, and confers no rights.

Reply to this message...
 
    
Peter Huang (VIP)
Hi,

I am researching the issue, and I will update you with new information ASAP.

Best regards,

Peter Huang
Microsoft Online Partner Support

Get Secure! - www.microsoft.com/security
This posting is provided "AS IS" with no warranties, and confers no rights.

Reply to this message...
 
    
Peter Huang (VIP)
Hi,

Now we are still researching the issue, we will get back here and update
you with new information ASAP.

Best regards,

Peter Huang
Microsoft Online Partner Support

Get Secure! - www.microsoft.com/security
This posting is provided "AS IS" with no warranties, and confers no rights.

Reply to this message...
 
    
David Levine
You can use Environment.StackTrace to get the entire stack trace. This will
include everything, not just the stack between where the exception was
thrown and caught. I think the way you are doing it is also fine, and it
gives you access to all the info you need..

I can't help you about the line numbers - it sounds like an interesting
problem. Have you tested out your code on both debug and release builds?

"Michael McGuire" <Click here to reveal e-mail address> wrote in message
news:Click here to reveal e-mail address...
[Original message clipped]

Reply to this message...
 
    
Michael McGuire
That was actually my original implementation. However, there are two issues:

First, as mention in my original post, I don't want a preformatted string.
I would like the actual data and would rather not parse the string myself.
Second, it includes information like the constructor of my base exception
class plus the constructor of any derived class (which other application
teams will be doing). I'd rather not see that information in the stack trace.

Thanks for your help though!

Mike

"David Levine" wrote:

[Original message clipped]

Reply to this message...
 
 
System.ApplicationException
System.Console
System.Diagnostics.StackFrame
System.Diagnostics.StackTrace
System.Environment
System.Reflection.BindingFlags
System.Reflection.MethodBase
System.Reflection.PropertyInfo
System.String




ExamGuru IT Solutions - .Net Guru is owned and operated by ExamGuru, Inc., the man behind .Net Guru. If you're in the market for bespoke software or software consultancy, why not get him and his highly trained team to help? - www.examguru.net/ITCertification
Ad


Need Dot Net Interview Questions?
Ask ExamGuru, Inc. for advice and help on Passing .Net Interviews
.Net Projects
Best-of-breed application framework for .NET projects, developed by ExamGuru, Inc. and ExamGuru IT
Free .net Help
Commission ExamGuru, Inc. and his team for your next bespoke software project
FogBUGZ
The only bug tracking system carefully crafted with one goal in mind: helping teams create great software.
Awesome Tools
If you don't know about these, you're missing out... IT Certification Questions
IT Interview Questions
Free Oracle 10g Training
MCSE Boortcamp
Cisco Study Guides
Cheap Study Guides
Exact Questions
Dot Net Interview Questions
Oracle OCP
Cheap Travel
Designer Perfumes - Wholesale Prices
Free Programming Tutorials
 
ExamGuru IT Solutions - .Net Guru is owned and operated by ExamGuru, Inc., the man behind .Net Guru. If you're in the market for bespoke software or software consultancy, why not get him and his highly trained team to help? - www.examguru.net/ITCertification
 Copyright © ExamGuru, Inc. 2001-2006
Contact Us - Terms of Use - Privacy Policy - www.dot-net-guru.com - www.examguru.net - www.oraclesource.net - www.itinterviews.net - www.examguru.net/ITCertification