How does Exception.StackTrace know lines from my source code? - c#

This confuses me a lot.
My understanding is that when I compile an application it becomes optimized code that my operating system reads. Things from my source code such as variable names, line numbers, etc., no longer have meaning.
So then how am I able to build-and-run code like
try
{
// ...
}
catch ( Exception E )
{
Console.WriteLine("Exception occured: {0}", E.StackTrace);
}
and get all this detailed information about what part of my source code is respomsible for an exception.
Now, I understand that in .NET my C# code doesn't intially become low-level "operating system code" but rather Microsoft Intermediary Language. What I'm guessing it that MIL, in order to generate this exception info, perserves some type of mapping between my source code and the compiled code. This seems like a huge waste, isn't it? So "compiling" in .NET isn't really translating source code to machine code; it is creating machine code in addition to source code. That means that all the applications on my Windows machine have metadata pertraining to their source code.
Or am I completely wrong about all this?

When you compile an assembly the compiler also generates a .pdb file, which is essentially a database file for running your assembly in debug mode. It also contains mappings between the optimized code and the original code, which allows the debugger to know line numbers of methods calls.
Details on .pdb files here: https://msdn.microsoft.com/en-us/library/yd4f8bd1(vs.71).aspx

Stack trace basically contains several parts of information:
Information about what call chain lead to the current execution point. It's not surprise that you can access such information, since when you return from one function, execution should get back to whatever function called it, and so on, up to your program entry point. All function names are also freely available (remember, you can yourself get information about any .NET methods via reflection for example). So this part should not surprise you.
Information about source code file where exception occured, including path to that file (on machine where code was compiled) and line number. This information is stored in separate (.pdb) file, and is optional. If you do not deploy your .pdb files on target machine - you won't see this info in stack traces. You may or may not generate .pdb for your production release, and you may or may not deploy them to target machine.

Related

Debug C++ dll from C#

I will briefly tell you the situation.
I got a C# project which uses some DLL created in C++.
Now, separately, I also have a C++ project, which was used to create that DLL some time ago.
Now, I wanted to debug the C++ DLL during running the C# project.
I enabled "Enable Unmanaged Code Debugging" in my C# project.
I started debugging C# project and stepping into some functions alongside.
All seemed to be ok. When I reached a function which belonged to C++ DLL,
it asked for the source of the C++ file, I had to browse to my C++ project.
(Before I think it complained about some .pdb files).
Now, I managed to step into the C++ function also, but as I step over and over, some of the data structures in that function don't seem to be populated with the data, e.g., please see screenshot below
You can see the blob data structure is empty, and same happened with DataParser (it was showing it had 0 items inside, whereas in code above you can see there are multiple items being added to it).
I would really appreciate some help, what is going wrong here? And where I could have done mistake. How can I debug this C++ DLL so that I also see what values are assigned to its variables currently?
Maybe my way of debugging this C++ DLL is wrong? The fact that the C# project is using an already created DLL, and I have this C++ project which was used to create this DLL some time ago - the fact that they are separate, maybe that has to do something with it also?
PS Before I had to make changes like this to C++ project and lower toolset because I use VS2012 (strange if project was created using VS2013 though because I think it is old project). Also the project uses lot of manually written other C++ classes. Maybe that is the problem also and somehow the compiler can't retrieve their values and definitions?
What are the steps in general to debug a C++ DLL file in a setup like I have?
EDIT: PPS. Also some other interesting facts I have seen. If I click F11(Step into) on the DataParser.Add function for example, not necessarily I am taken to the body of that function, it shows me body of other function (which might be somehow related to it).
Also if I press F10 say after first time Request.Add is called, it jumps over multiple Request.Add lines, and moves to the fifth one for example.
EDIT2: Also before I step into C++ code it is showing me warning that "the source is different version than the one that was used to create a DLL". Is this a problem?
Module and PDB
There is a link between a module (.dll/.exe) and the debug database (.pdb). This link is established via a timestamp and a checksum that is present in both files. Visual Studio checks the correctness of those, otherwise it will complain and not stop at breakpoints at all.
While other debuggers such as WinDbg have commands to turn that feature off, Visual Studio doesn't have such a feature and requires active manipulation (such as Chkmatch) to turn off the checmsum verification. As long as you didn't use such a tool, your debugging symbols are fine.
PDB and source
There is also a link between the debug database (.pdb) and the source. This link is established by file name and line numbers. As you can guess, your source code will not modified during compilation, so the source code does not contain any checksum or timestamp that could be verified.
Therefore, the source may have changed and the line numbers may not even match roughly any more. There are several reasons for line numbers to get broken. I have answered a similar question before and listed the following reasons for line number changes although the code itself did not change:
code reformat, which e.g. sorts the methods by visibility, so complete methods are moved
code reformat, which e.g. breaks long lines at 80 characters, usually this moves things down
optimize usings (R#) which removes 30 lines of unneeded imports, so things move up
insertion of comments or newlines
How to debug
Restore the exact source code of that version, if you can.
Debug completely without source, just by PDB information. This way you can keep the binary components, if that's important (e.g. if a bug can only be reproduced with that version)
Rebuild all modules to make the code match the modules again. That way you lose the binary and the problem may not reproduce any more.

"URI formats are not supported." exception just started showing up in really old, unchanged code

Ok, I'm experiencing a truly random bug and I cannot find any reason why this would happen. I have an application that I update that was first developed MANY years ago. I work on a sizable dev team whose sole responsibility is to manage this application and we've come to accept that the project is a bit of a "franken-code" project. We are but humble developers in a line of many generations of developers who've inherited this project. (This will be important to know later.)
There is a portion of our application that deep within the initialization process calls the following code:
string strPath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().CodeBase);
string strFile = strPath.Substring(6) + "\\" + FILE_NAME;
Here's the deal. My fellow team members, and myself, have been able to modify and build higher-level, UI and DB related sections of our solution for an eternity. I, nor anyone else, has modified the above code, or any code in the same code file (or project within the solution, for that matter.)
However, today while working in a completely different section of my application I began to get some really odd "Out of Memory" exception errors. I'm not sure if that relates to my problem but I felt it was worth mentioning that after rebooting my machine and reloading the VS solution, I'm now consistently getting the following exception when I attempt to run a debugger test, when the initialization process attempts to execute the above mentioned snippet of code:
Exception: A first chance exception of type 'System.ArgumentException' occurred in mscorlib.dll
Message: URI formats are not supported.
I've googled this error message and it looks like the original dev was simply doing this wrong. This seems to be a common error, but what baffles me is that this has never been a problem until, randomly, today.
I know this is an odd question, but is there a way to fix this without modifying this code. As I mentioned, this is a really complex application that often feels a bit cobbled together. Our team is attempting to clean up, or replace, much of the applications functionality but there are portions we simply do not touch because we have no solid clue how the application will work once it is deployed to our production environment. This is a highly-critical application and it cannot be broken.
Might anyone have any clue what may cause this to "magically" start happening? Especially since I have been working in UI-related code, and no where near the low-level, configuration resolution section of code where this came from.
ADDITIONAL NOTES
We use source control. If I download, build an run an older revision of the application, it works.
We use AnkhSVN and when I inspect my changed files, again, there is nothing that has been changed that relates to the code that is now failing.
No one else in my team has ever seen this.
To my knowledge, I've not tweaked any setting associated with my project. I've taken a look at my project properties and everything looks normal. I guess there is a chance that I've hit some odd key-combo and enabled/disabled something through shortcut-keys, but I have no clue what that might be.
Any help is appreciated. Sorry for the novel. I'm just stumped and I'd rather not use a different method for acquiring this path string if there is ANY chance that altering this process could behave differently in different user environments.
I can only assume some working file within the Visual Studio that is associated with the project/solution had become corrupt. I searched through the text of my project files, and all of my code, and I didn't see anything out of place.
As I mentioned, we use source control. To attempt a fix, I pulled down the same source revision that I initially pulled for my current task. I compiled and ran the application. Everything worked properly in its "vanilla" state.
Next, I copied in all of the files I knew I had modified. I hadn't added any new project references or resources, so I just copied over the modified .cs files. I built and ran the application and I've had no trouble since the pull from my branch.
This does not answer the question of why this occurred, but this method can provide a solution to the problem.
I can confirm this change in Path.GetDirectoryName occured to me after installing VS 2015 and rebuilding our project in it so it seams to be .NET 4.6 feature.
Rebuilding the project again in VS 2013 returns the previous behaviour where Assembly.CodeBase with "file:" prefix is acceptable by Path.GetDirectoryName without any exception.
But rereading the MSDN documentation, there is a statement that "file:" paths are not supported, but this is not mentioned in ArgumentException thrown in VS 2015 code.
First of all, find out how many versions ago this started occuring: start with the current version, and work back changeset by changeset until it no longer fails.
It sounds like, for whatever reason, System.Reflection.Assembly.GetExecutingAssembly().CodeBase now returns a string that GetDirectoryName doesn't like. So, check the project files, the .sln, the repo config, anything that could cause a file to be in a different location.
If you can't find anything there, check the other files from that same commit, even if they appear that they shouldn't be related.
First Chance Exceptions generally happen when you've got multiple threads happening, so check for new threads that weren't in the previous version. I've also had situations where First Chance exceptions would only get caught under certain situations, and are silently ignored otherwise, so look changes in Debug settings: it's possible that this problem has always existed, you just haven't had the right settings to catch it until now.
Remember that under a source control, other people can change things that are "yours", even if only by accident.

ASP.NET MVC, outputting source code in error

So I've wound up in a very odd place. Due to circumstances beyond my control, a machine burned up before I was able to commit certain changes to a backup/repository. This is only one file that didn't get backed up, but it was an important one to me nonetheless.
However the binary that was generated still lives on an internal test webpage. So my first thought was to try a decompiler, which has given some results, but it isn't very accurate.
I noticed that when I do not have much configured for ASP.NET MVC, errors show the source code of the file that threw an exception if there is no kind of handling.
I was wondering if I might be able to use this to get my code back, but the output length seems limited to 9 lines.
So.
(A) is this possible?
(B) is there any way to get more than 9 lines?
(C) are there any good tools for this kind of thing?
I am not trying to hack. I own the site, and the code. It is just an unfortunate situation.
I think you are talking about PDB files. There's a question about it: Obtaining information about executable code from exe/pdb
You can inspect with http://www.codeproject.com/Articles/37456/How-To-Inspect-the-Content-of-a-Program-Database-P but it only shows little information.
And as you can read in http://www.wintellect.com/CS/blogs/jrobbins/archive/2009/05/11/pdb-files-what-every-developer-must-know.aspx
The actual file format of a PDB file is a closely guarded secret but Microsoft provides APIs to return the data for debuggers.

CS, __LINE__ unavailability, can we write an alternative?

I'm a Visual C++.net programmer that converted to C# about a year ago, and I really miss __FILE__, and __LINE__. To replace __FILE__, I wrote a nice function to return class.method, which is awesome, but there is no suitable alternative for __LINE__ (correct me if I'm wrong!).
I understand the language difference and technical limitations, and the reasoning, and all that stuff.
What I'm asking is this:
Is it practical, or even possible, to write some visual studio extension (that all of our developers would have installed), that would allow for us to define some type of token (~~LINE~~ or something), that we could have text replacement or something switch that symbol to the actual VS line number, when compiled into an executable?
My knowledge of extensions programming is minimal, I do not know the ext. systems limitations.
Edit for clarification:
In C++, __FILE__, once compiled, will return the current file you wrote your code in, and __LINE__, your current line. This is important because all of our logging systems (10+ years old), all require a char* for the file, and an int for the line, we're logging from.
C# cannot produce these two 'tokens' like C++ can.
As an example:
LoggingService.LogException(e, "file.cs", 1234);
is what I want to get compiled into my executable, and
LoggingService.LogException(e, ~~MYFILE~~, ~~MYLINE~~);
is what I want my code to look like, and saved on disk. I have a suitable alternative to get the file, but I don't have one to get the line.
Edit 2:
These our release builds, without debugging symbols. Our product installs would have to be hundreds of megabytes larger in order to accomidate this fix, which is out of the question.
Caller Information is coming in .NET 4.5. This will be compiled, a big improvment over having to examine the stacktrace manually:
http://msdn.microsoft.com/en-us/library/hh534540(v=vs.110).aspx
public void LogException(Exception ex,
[CallerFilePath] string filePath = "",
[CallerLineNumber] int lineNumber = 0)
{
// Log it!
}
Thanks to the compiler magic, this would be called as:
LoggingService.LogException(e);
You can simply run all C# code through C preprocessor first and than compile results. You will need to change build rules for CS files to do that.
Note: If you are processing exceptions the stack frame is in the exception, so this won't be needed. I used this once to store line and file information with a logging system and that is the only use case I can think of -- but it is "not so fast".
WARNING: If you don't have symbols in your release build some of this information will not be available.
using System;
using System.Diagnostics ;
StackTrace st=new StackTrace (0,true);
StackFrame sf=new StackFrame ();
sf=st.GetFrame (0);
Console.WriteLine ("FileName: {0}",sf.GetFileName ());
Console.WriteLine ("Line Number: {0}",sf.GetFileLineNumber ());
Console.WriteLine ("Function Name: {0}",sf.GetMethod ());
There is also column and type and some other stuff in there... look here for more.
http://msdn.microsoft.com/en-us/library/system.diagnostics.stackframe.aspx
The line is in the exception's stack trace. but rather than extracting it, I think you should log the entire stack trace as it has other useful information for debugging (plus the line number is sometimes a few lines off, due to preprocessor statements i believe)
I would highly recommend ELMAH if you're doing ASP.Net web applications either in Web Forms or MVC. It takes care of all of that logging

debugging a program's incorrect references

Trying to find a way to prove that my program is not running correctly because the version numbers of the dll's my interops are pointing to are different i.e. different GUIDs.
Works on my machine, not on "theirs" with the different dll's.
Can anyone recommend some debugging tools that let me watch the program as it starts up and see things like "looking for dll, not found, quitting"?
Is there logging tool available that would report these things to me?
If so I'm not aware of/using it.
You get an exception when a DLL isn't found. Or more commonly in your case, a COMException as soon as you try to use the interop library in your code. One drastic mistake you could make is catching such an exception. That's a very common mistake. But don't, undiagnosable failure is the result. There is rarely any point in letting your program continue running when an important chunk of code is just missing. Logging it isn't hard when you use AppDomain.UnhandledException.
This should at the very least provide you with decent diagnostics that help you to fix your code. You cannot get this started until you get good exception info. Pre-emptively fixing rather than waiting for the customer to get back to you with an exception trace usually requires you to recreate possible client configurations and testing your code. Highly advisable btw with 4 versions of IE in common use. You'll need a virtual machine so you can install the different OS and IE versions and test your code. Making the OS and IE version a system requirement is not unreasonable, ymmv.
You can try to do it yourself quick and dirty by enumerating all the assemblies loaded by your program via AppDomain.Current.GetAssemblies(). Also, check other questions about listing loaded assemblies, like this one
Read up on Assembly class in MSDN to see what information you can get about your assemblies.

Categories

Resources