Read the following articles related to this topic:
Help yourself in Debugging by using Call Stack and Immediate Window
Help Yourself in Debugging (Part-2) using Breakpoint/Tracepoint
This article is about the two features in C#, the Caller Information Attribute and the StackTrace class that are helpful to get information at runtime from where the call came from, in other words both provide information about the caller. The purpose of both is the same but there is a significant difference between them. Information provided by both can be used by a developer application to provide trace information.
Before starting with StackTrace and Caller Information, the following image shows the structure of the code.
So the project is divided into the three layers, UI, Business and Data.
Front Layer code
class Program
{
//Program p = new Program();
public int i = 10;
public static void Main(string[] args)
{
try
{
var str = (new BusinessEmployee()).GetEmployeeList();
Console.WriteLine(str);
Console.ReadLine();
}
catch (Exception ex)
{
global::System.Windows.Forms.MessageBox.Show(ex.Message);
}
}
}
As you see in the code above, the Font layer is calling the “GetEmployeeList” method to get a list of all employees.
BusinessLayer code
public class BusinessEmployee
{
private readonly DataEmployee dataEmployee;
public BusinessEmployee()
{
dataEmployee = new DataEmployee();
}
public void GetEmployeeList()
{
dataEmployee.GetEmployeeList();
}
}
In this layer the Business class is calling the DataLayer to get the employee list from the database.
DataLayer code
public class DataEmployee
{
//Data layer class for employee
public void GetEmployeeList()
{
//code goes here for retiving from DATABASE
}
}
This layer returns the employee list, but for the example its returning an exception.
StackTrace
A class in C# that is part of the System.Diagnostics namespace that provides information, the same as the Call Stack window (read more about the Call Stack window ) at runtime.
To understand it better let's consider changing the code above in the DataLayer as in the following:
//Data layer class for employee
public string GetEmployeeList()
{
//// get call stack
StackTrace stackTrace = new StackTrace(true);
StringBuilder sb = new StringBuilder();
foreach (StackFrame frame in stackTrace.GetFrames())
{
sb.AppendLine(" Method Name: " + frame.GetMethod().Name + " File Name:" + frame.GetMethod().Module.Name + " Line No: " +
frame.GetFileLineNumber());
}
return sb.ToString();
}
As you can see in the code, it creates a StackTrace class and when creating a new object of the class, "true" is passed as an argument to the constructor of the StackTrace class for capturing the file name, line number, and column number.
After the object is created the GetFrames method is usd to get information about each frame (each method call is represented by one frame) and finally each frame detail is appeded using StringBuilder, then it is displayed by the front end.
The following is the output received when the code is run.
So the output prints each call made from one layer to another. If you see in code above, the call is coming from the front layer to the database layer.
Advantages
- StackTrace provides detailed information about from where a call came from, from the beginning to the end. This is one resason a developer can use StackTrace when there is a need to get trace detail-level information.
- StackTrace frame also provides control of the caller method, provides the assembly from where the call is coming.
Disadvantages
- Inline methods are not listed when the code is compiled in Release mode, that is it is listed when the code is compiled in Debug mode.
To understand it consider the following code changes in the front layer code.
Now if you compile code in Debug mode with the following configuration:
It will generate the following output for you , where you can see four lines, in other words one extra line of calling to the method "GetEmployeeList1".
So debug lists all the method calls but now if you compile the code in Release mode with the following configuration:
Then it will generate the following output for you where you can see three lines. The line calling the method "GetEmployeeList1" is missing.
So StackTrace doesn't list methods converted to inline by the compiler. If you want to list them then you can mark a method as [MethodImpl(MethodImplOptions.NoInlining)].
If the devloper marks a method as [MethodImpl(MethodImplOptions.AggressiveInlining)] then the method is also not listed in debug.
So StackTrace information can be spoofed by making a method inline.
Since StackTrace provides every minute detail, StackTrace will show the full trace right into the core Microsoft sourced assemblies and will reveal details about what technologies you're using, and possible versions as well. This gives intruders valuable info about possible weaknesses that could be exploited. So always be careful before displaying this information and put security on this.
Advantages over Caller Information attribute
It provides minute details that are not possible with the Caller Information attribute.
Line number, Filename and Method Name information is given by StackFrame that cannot be changed by the developer but in the case of the Caller Information attribute the developer can spoof by passing the wrong value in the Caller Information parameter.
Caller Information attribute
This is a new feature introduced in C# 5.0 and the attributes are part of the System.Runtime.CompilerServices namespace. These attributes must be added as an optional parameter in the method for which there is a need to get the Caller Information.
Example
Accessiblity ReturnType MethodName (Type parameterName,....,
[CallerMemberName] string memberName = "",
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber] int sourceLineNumber = 0
Name | Type | Description |
CallerMemberName | string | Provide caller method Name |
CallerFilePath | string | Provide caller method File Name |
CallerLineNumber | int | Provide caller Line number in File from where method get called |
Note
The caller of the method can aslo pass this value since this attribute is added as a parmeter to the method.
Example when calling a method decorated by the Caller Information attribute:
To understand this let us make a change in the DataLayer method like this:
//Data layer class for employee
public string GetEmployeeList([CallerMemberName] string memberName = "",
[CallerFilePath] string fileName = "",
[CallerLineNumber] int lineNumber = 0)
{
return " Method Name: " + memberName + " File Name:" + fileName + " Line No: " + lineNumber;
}
So running the code above provides the following output.
Advantage
- Information of the caller's attribute cannot be spoofed until the developer passes the information in the method parameter.
- There is no runtime cost for this, in other words it doesn't affect performance of the code since the attribute is applied at compile time.
- Very helpful to determine when a call is coming from some unknown location, like in case of "PropertyChange".
Disadvantage
Information about a caller can be spoofed by a developer if the developer passes the wrong information in the parameter of the Caller Information.
There is only one StackFrame, in other words it will provide information about who called a method (immediate caller of the method) but doesn't provide detailed information like StackTrace.
It's part of C# 5.0 so it does not work with older versions of the framework.
Advantage over StackTrace
Since it is applied at compile time there is no runtime cost like for StackTrace (we need to create an object of StackTrace at runtime to get the information).
Information provided by the attribute cannot be spoofed by method attributes.
Do comment if the information is missing or oncorrect.
No comments:
Post a Comment