0

I am trying to understand how I can print the execution flow of a program. I've wrote some simple code to demonstrate my dilemma.

I'd like for this to print something like: In class A, Main. In class B, methodB. In class C, methodC.

StackTrace seems to be giving me a lot of hard to read information. Is there a simpler method, or a way to have an easier to read StackTrace? The actual code is much larger and I don't want to put print statements in every single method, so I figured something like this may make debugging easier. Also, I can't use Visual Studio's debugger in the actual code. So if you guys could please advise me on which function to use, if not StackTrace, that would print the necessary information for me, I would appreciate it.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace StackTrace
{
        public class A
        {
            public static void Main(string[] args)
            {
                B newB = new B();
                newB.methodB();
                Console.WriteLine("StackTrace: {0}", Environment.StackTrace); //I'd like to get the entire execution flow printed here
                Console.ReadKey(); //just for testing, don't worry about this.
            }
        }

        public class B
        {

            public void methodB()
            {
                C newC = new C();
                //Console.WriteLine("In class B, method B");
                newC.methodC();
            }
        }

        public class C
        {
            public void methodC()
            {
                //Console.WriteLine("In class C, method C");
            }
        }
}
Tim
  • 478
  • 4
  • 15
  • are you using visual studio? if so, press f9 on the first line in main() and then click f10 to step through the code. – dave May 18 '18 at 17:10
  • Yes, I am using Visual Studio. Just wanted to reclarify: I can't use the debugger in the actual code. – Tim May 18 '18 at 17:11
  • did you try uncommenting `//Console.WriteLine("In class B, method B");` and other commented line? What exact problem you are trying to solve here? Do you have any logging implemented in the "Actual Code" ? – Chetan May 18 '18 at 17:11
  • _"I can't use the debugger in the actual code."_ - why not? If you can add code to it, why can't you debug it? – stuartd May 18 '18 at 17:13
  • agh sorry for not reading the last line there, yeah writing the logging statements manually would be my next approach i guess.. – dave May 18 '18 at 17:13
  • Hi Chetan. I think I understand what you are getting at, but I would like to restate that this was just an example code to demonstrate my issue. The actual code is much larger, and I don't think it would be productive to put logging comments in every single method in each class. – Tim May 18 '18 at 17:14
  • Hi stuartd, this is actually for an embedded application, not a console application. I'm not sure how to get the debugger to work. So I figured it might be a better use of time to just print the execution flow instead of trying to get the debugger to work. – Tim May 18 '18 at 17:15
  • Do you have any interceptors used in the code, which would log the statements like BlockStart when method execution starts and BlockEnd when method execution is complete with method name. That would be a better approach. – Chetan May 18 '18 at 17:16
  • 1
    @tim Fair enough. You could do this with PostSharp, but the free version is limited. – stuartd May 18 '18 at 17:16
  • If you can run the application from VS, definitely play with the debugger and try getting it to work. – dave May 18 '18 at 17:16
  • @Chetan, No i don't. I wasn't even sure what those were. I will look into those functions/methods and try them out. Thanks for your help. – Tim May 18 '18 at 17:17
  • So to summarize everything, is there a built in method/function in c# (I guess similar to StackTrace) that can give me this info so I don't need to add multiple logging comments everywhere? – Tim May 18 '18 at 17:19
  • No, there's nothing built-in that can do it for you.. – stuartd May 18 '18 at 17:27
  • @stuartd thanks for your time. I'll look into PostSharp... – Tim May 18 '18 at 17:29
  • 1
    Honestly, your best bet is learning how to read the StackTrace, as all the information you seem to be looking for is in it. It's just very poorly formatted. – Daxtron2 May 18 '18 at 17:33
  • @TJ Wolschon Yea it really is... Is there anyway to format it, that you know of? – Tim May 18 '18 at 17:36
  • @Tim I usually just try and format it myself in an editor, I'd love something that visualized it better. If you find something, share! – Daxtron2 May 18 '18 at 17:40
  • 1
    You might consider using something like Postsharp. Add a cross-cutting concern to log the method being executed, and have Postsharp inject that logging code into every method. –  May 18 '18 at 19:30

2 Answers2

2

So for everyone who responded to my original thread, you may be interested in what I came up with so far. Maybe you guys, or somebody else, can add to this discussion and we can find a more permanent solution. But, here's the closest I got to automating this:

1) Create this function/method and add it to every class:

public void LogInfo(string className, string methodName)
{
   string info = ("In class: " + className + " In method: " + methodName);
   Console.WriteLine(info);
}

2) Paste this line into the relevant areas of the code base:

 StackTrace stackTrace = new StackTrace();
 LogInfo(MethodBase.GetCurrentMethod().DeclaringType.Name, stackTrace.GetFrame(0).GetMethod().Name);

3) Also please take a look at my modified code, and note that we need to add these two using cases:
using System.Diagnostics; using System.Reflection;

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;


using System.Diagnostics;
using System.Reflection;

namespace StackTraceTest
{
        public class A
        {
            public static void Main(string[] args)
            {
                StackTrace stackTrace = new StackTrace();
                LogInfo(MethodBase.GetCurrentMethod().DeclaringType.Name, stackTrace.GetFrame(0).GetMethod().Name);

                B newB = new B();
                newB.methodB();

                LogInfo(MethodBase.GetCurrentMethod().DeclaringType.Name, stackTrace.GetFrame(0).GetMethod().Name);

                /*for (int i = 0; i < stackTrace.FrameCount;i++)
                {
                    LogInfo(stackTrace.GetFrame(i).GetType().Name, stackTrace.GetFrame(i).GetMethod().Name);
                }*/

                Console.ReadLine();
            }

            public static void LogInfo(string className, string methodName)
            {
                string info = ("In class: " +className +" In method: " +methodName);
                Console.WriteLine(info);
            }
        }

        public class B
        {

            public void methodB()
            {
                StackTrace stackTrace = new StackTrace();
                LogInfo(MethodBase.GetCurrentMethod().DeclaringType.Name, stackTrace.GetFrame(0).GetMethod().Name);

                C newC = new C();
                newC.methodC();
            }

            public void LogInfo(string className, string methodName)
            {
                string info = ("In class: " + className + " In method: " + methodName);
                Console.WriteLine(info);
            }
        }

        public class C
        {
            public void methodC()
            {
                StackTrace stackTrace = new StackTrace();
                LogInfo(MethodBase.GetCurrentMethod().DeclaringType.Name, stackTrace.GetFrame(0).GetMethod().Name);
                //Console.WriteLine("StackTrace: {0}", Environment.StackTrace);
            }

            public void LogInfo(string className, string methodName)
            {
                string info = ("In class: " + className + " In method: " + methodName);
                Console.WriteLine(info);
            }
        }
}

4) The output now becomes:

In class: A In method: Main
In class: B In method: methodB
In class: C In method: methodC
In class: A In method: Main

5) A few things I'd like to point out to you guys: I thought the commented out for loop would be the magic bullet that would solve everything in one line, but it gives the output:

In class: StackFrame In method: Main
In class: StackFrame In method: _nExecuteAssembly
In class: StackFrame In method: ExecuteAssembly
In class: StackFrame In method: RunUsersAssembly
In class: StackFrame In method: ThreadStart_Context
In class: StackFrame In method: RunInternal
In class: StackFrame In method: Run
In class: StackFrame In method: Run
In class: StackFrame In method: ThreadStart

Also note the commented out line, in class C: If you uncomment it, it gives you the entire execution flow properly (won't post results because it includes personal info). However, this means I need to dig deep within the code, find the method that is called last, and add this line of code to it.

6) Sources: How performant is StackFrame?

How can I find the method that called the current method?

C# getting its own class name

Please let me know what you guys think. Thanks.

Tim
  • 478
  • 4
  • 15
2

Using the free version of postsharp you can do this very easily. First, create an attribute you can tag on to your methods:

[PSerializable]
public class LogInvocationAttribute : MethodInterceptionAspect
{
    public override void OnInvoke(MethodInterceptionArgs args)
    {
        // TODO: use your existing logging method here. simple example here for now.
        Console.WriteLine($"{args.Instance.GetType()}.{args.Method.Name}");             

        // pass the call through to the original receiver
        args.Proceed();
    }
}

After that, any method you want logged you can just apply this attribute to like this:

[LogInvocation]
public void SomeMethod(int someArg)
{
    // do stuff
}

With paid versions of postsharp you can actually apply things like this at a base class level and have it automatically apply to children as well (SO reference here) but for what you want to do applying an attribute to your methods is a simple way to get what you want.

S.C.
  • 1,152
  • 8
  • 13
  • Thanks for the example! Since everyone is advocating PostSharp, I will have a look at it. Thanks! – Tim May 18 '18 at 19:41