Bug 605 - MD debugger does not see local variable
Summary: MD debugger does not see local variable
Alias: None
Product: Compilers
Classification: Mono
Component: C# ()
Version: unspecified
Hardware: PC Linux
: High major
Target Milestone: ---
Assignee: Marek Safar
Depends on:
Reported: 2011-09-02 08:44 UTC by Marek Safar
Modified: 2012-02-17 11:48 UTC (History)
4 users (show)

Is this bug a regression?: ---
Last known good build:

Screenshot (61.35 KB, image/png)
2011-12-08 18:33 UTC, Marek Safar
Screenshot of it working for me (109.86 KB, image/png)
2011-12-08 19:16 UTC, Jeffrey Stedfast
local pads (134.39 KB, image/png)
2011-12-09 03:59 UTC, Marek Safar
full project (3.60 MB, application/x-zip-compressed)
2011-12-12 14:48 UTC, Marek Safar

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.

Please create a new report on GitHub or Developer Community with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:

Description Marek Safar 2011-09-02 08:44:34 UTC
using System;
using System.IO;
using NUnit.Framework;

namespace test3
	class MainClass
		public static void Main (string[] args)
			MemoryStream testStream;
			byte [] testStreamData;
			testStreamData = new byte [100];
			for (int i = 0; i < 100; i++)
				testStreamData[i] = (byte) (100 - i);
			testStream = new MemoryStream (testStreamData);
			byte [] readBytes = new byte [5];
			string async_state = null;

			var res = testStream.BeginRead (readBytes, 0, 5, l => {
				async_state = l.AsyncState as string;
			}, "state");

			Assert.IsTrue (res.AsyncWaitHandle.WaitOne (10000), "#1");
			Assert.AreEqual ("state", async_state, "#2"); // BREAK HERE and watch async_state variable
			Assert.IsTrue (res.IsCompleted, "#3");
			Assert.AreEqual (5, testStream.EndRead (res), "#4");

MD shows Unknown_identifier: async_state
Comment 1 Mikayla Hutchinson [MSFT] 2011-09-02 09:55:39 UTC
And, this is why we should use the pdb debug format or fix the mdb format, so the compiler is responsible for creating maps of closure locals, instead of MD having to reverse-engineer the compiler-generated names.
Comment 2 Jeffrey Stedfast 2011-12-08 15:44:18 UTC
This seems to work for me
Comment 3 Marek Safar 2011-12-08 18:33:32 UTC
Created attachment 1018 [details]
Comment 4 Marek Safar 2011-12-08 18:33:58 UTC
It does not work, see the screenshot
Comment 5 Mikayla Hutchinson [MSFT] 2011-12-08 19:14:31 UTC
I did some digging in the pdb format and it appears that I was wrong - it doesn't contain the mappings for closure variables. Therefore, the current approach where MD is aware of the compiler-generated closure variables is the "correct" approach. Would you be able to document these patterns and ensure they do not change and are compatible with csc?
Comment 6 Jeffrey Stedfast 2011-12-08 19:16:52 UTC
Created attachment 1019 [details]
Screenshot of it working for me

This is what it shows for me.

I wonder if this is a bug in Mono master? I'm using Mono 2.10.7
Comment 7 Mikayla Hutchinson [MSFT] 2011-12-08 19:23:03 UTC
Can you show us the state of your locals pad when you hit the breakpoint?
Comment 8 Marek Safar 2011-12-09 03:59:21 UTC
Created attachment 1021 [details]
local pads
Comment 9 Marek Safar 2011-12-09 03:59:49 UTC
That's with slightly simplified test and Mono master + MD master
Comment 10 Jeffrey Stedfast 2011-12-12 14:16:29 UTC
Marek, can you send me a compiled version of this project so I can inspect what is going on?
Comment 11 Marek Safar 2011-12-12 14:48:09 UTC
Created attachment 1033 [details]
full project
Comment 12 Jeffrey Stedfast 2011-12-12 16:10:30 UTC
Okay, so a bit of digging later...

First, it seems that the naming convention for capture variables has changed. They now get names like $locvar0 (for which I've now added support for).

However, there's more :-(

It also seems that the compiler is providing wrong IL offset ranges for the compiler-generated local variable:

Getting local variables at ILOffset: 92
new LocalVariable: System.String[] args in Void test3.MainClass:Main (String[]) range:-1:-1
new LocalVariable: test3.MainClass+<Main>c__AnonStorey0 $locvar0 in Void test3.MainClass:Main (String[]) range:0:6
new LocalVariable: System.IO.MemoryStream testStream in Void test3.MainClass:Main (String[]) range:0:101
new LocalVariable: System.Byte[] testStreamData in Void test3.MainClass:Main (String[]) range:0:101
new LocalVariable: System.Int32 i in Void test3.MainClass:Main (String[]) range:14:41
new LocalVariable: System.Byte[] readBytes in Void test3.MainClass:Main (String[]) range:0:101
new LocalVariable: System.IAsyncResult res in Void test3.MainClass:Main (String[]) range:0:101
new LocalVariable: System.String m in Void test3.MainClass:Main (String[]) range:0:101

Notice how $locvar0's offsets are 0->6, whereas all others are 0->101

Obviously 'i' is a subrange because it is just used in the for-loop, so that makes sense, but $locvar0's range seems wrong based on my (limited) understanding of what is going on.
Comment 13 Marek Safar 2011-12-13 05:39:07 UTC
No, naming convention has not changed for captured variables, what has changed is all compiled generated variables (foreach, using, lock, etc) use same naming convention.

How should the ranges work when the method body is rewritten (lifted) into another method ?
Comment 14 Jeffrey Stedfast 2011-12-13 11:42:56 UTC
Can you document the naming convention the compiler uses? Is the new naming convention the same as Microsoft's?

I can't claim to know the answer to your ranges question, but it seems to me that with a range 0:6, there's no way MonoDevelop could possibly work with the example in this bug report.

Is there any reason setting it to 0:101 like the rest of the toplevel local variables wouldn't work?
Comment 15 Marek Safar 2012-02-17 11:48:18 UTC
This seems to work with the latest MD