In this blog post I am going to walk you through fixing a bug using IntelliTrace in Visual Studio 2015. If you haven’t done so already, check out the announcement of IntelliTrace in Visual Studio 2015 which gives you an overview of IntelliTrace and its UI. You can see this walkthrough as a video here (demo start at 1m30s).
The application we are going to be working with is a WinForms application from CodeProject called SocialClub. The application lets you maintain a database of members for a social club. The bug I want to fix is that search behaves erratically after a member is registered. To reproduce the bug, I start the application and register a new member (download the source code if you want to follow along):
Then I do a “Get All” search that is supposed to return all registered members. I expect to only get one result, but instead I get two:
The second search result is unexpected so let’s fix that. If you had to fix this bug, what would you do next? At this point my hypothesis is there is either something wrong with the “Get All” search functionality or there is something wrong with the registration of a new member. The application has another search mode that takes specific search criteria, so I will use it to search for the unexpected record returned by “Get All” (the one with the missing data and “Unknown” values). My thought process is:
- If I get no results back it probably means that the records don’t exist in the database and the issue is with the “Get All” search functionality.
- If I do get a result back matching a record with “Unknown” for Occupation and Marital status then the issue is probably with the registration functionality entering more records in the database than the one I ask it to.
So I perform a search with “Unknown” for Occupation and “Unknown” for Marital status, which returns exactly one result, the record I successfully registered as “Engineer” and “Married”:
Unfortunately this did not get me closer to the root cause of the bug and I don’t want to spend the time setting breakpoints, registering new members and searching for them over and over again. Let’s see how IntelliTrace can help me.
I can see the events that IntelliTrace has collected just by looking at the Diagnostic Tools window and the Events tab, even if the application is still running. However, since the application is running the timeline in the Diagnostic Tools window keeps progressing (i.e. scrolling to the right) to always show you the latest data collected. To stop the automatic progression of the timeline you can simply either drag the scrollbar where you want it and the window will respect that, or you can simply make the application enter break state by using the “Break All” button on the Visual Studio toolbar. Either way works, but let’s go with the “Break All” button this time.
At this point I have interacted with the application a lot since I started debugging (I logged in, registered a new member, searched using “Get All”, and finally searched using specific search criteria). However, I’m only interested in the events that occurred as a result of clicking the “Register” button, so to filter my view to just these events I first hover over the events in the timeline until I find the button click event, and then I drag and select the cluster of events immediately following it:
Below the timeline I can find the tabular detailed view in the Debugger tab. Selecting time in the timeline filters the list shown in tabular view and I can see the two most recent events listed (other than my hitting “Break All”) are two INSERT statements:
Clicking on an event in the list expands it to multiple lines to show you the entire executed SQL statement:
I can see that I have two INSERT statements happening and the second one is inserting a bad record with NULL values. Here are the two SQL statements copied & pasted (use CTRL + C to copy an event):
Execute Reader “insert [dbo].[ClubMembers]([Name], [DateOfBirth], [Occupation], [Salary], [MaritalStatus], [HealthStatus], [NumberOfChildren], [ExpirationDate])values (@0, @1, @2, @3, @4, @5, @6, @7) select [Id] from [dbo].[ClubMembers] where @@ROWCOUNT > 0 and [Id] = scope_identity()”
Execute Reader “insert [dbo].[ClubMembers]([Name], [DateOfBirth], [Occupation], [Salary], [MaritalStatus], [HealthStatus], [NumberOfChildren], [ExpirationDate])values (null, @0, @1, null, @2, @3, null, @4) select [Id] from [dbo].[ClubMembers] where @@ROWCOUNT > 0 and [Id] = scope_identity()”
Note: You can ignore the SELECT statement that follows the INSERT, that’s Entity Framework retrieving the ID of the record it just inserted.
The next question is: why am I getting two SQL statements executed for a single click of the “Register” button? IntelliTrace helps me answer the question very quickly by allowing me to activate Historical Debugging for each of the events and checking their respective historical call stacks:
The first insert’s historical call stack is:
The historical call stack of the second insert with the bad record is:
Clicking on each frame takes me to the corresponding line of code and after examining the two historical call stacks I have figured out that I have two different event handlers subscribed to the same button click: Register_Click(…) and btnRegister_MouseClick(…). Armed with that knowledge, I realize that because the form’s fields are reset after each new member registration, the first event handler inserts the records to the database correctly, but the second event handler inserts a record with blank and NULL fields. Now that I have identified the root cause so fast with IntelliTrace, I can transition to spend my time implementing a solution.
IntelliTrace saved us valuable time during debugging by allowing us to “travel in the past” and examine the state of the application at interesting moments, avoiding wasteful iterations of setting breakpoints and repeating testing steps.
We are always looking for feedback and comments for our features. You can leave general comments & questions at the end of this blog post or via Send-a-Smile, and submit feature requests to our IntelliTrace UserVoice. You can also send us a tweet or visit the MSDN Diagnostics forums.