Samsung / netcoredbg

NetCoreDbg is a managed code debugger with MI interface for CoreCLR.
MIT License
780 stars 101 forks source link

"Unverified Breakpoint" #40

Open kaphula opened 4 years ago

kaphula commented 4 years ago

Hello,

I am having this weird issue with vscodium where I get breakpoints to break because they are listed as "unverified". The debugger stops itself in a line where intentional exception is thrown. I can start debugging the application from this point on step by step, and as I move to new files, I can set valid breakpoints as long as the debugger is in the same file as the breakpoints. I wonder what is going on here?

viewizard commented 4 years ago

netcoredbg sources was updated. Could you please check it? Also, could you please provide debug console output, so, we could analyze it.

richterr commented 1 year ago

Same issue here when trying to setup the debugger. It is run with --interpreter=vscode and starts without problems. Even hits the exceptions breakpoint when I make the application throw one. But all other breakpoints (conditional or normal) are rejected. Any help is much appreciated :-) Below is the exported log and system info:

OS: Windows 10 dotnet: 7.0.200 netcoredbg: NET Core debugger 2.2.0-21 (fa4b62f, Release) Application: Net6.0 Console

-> (C) {"command":"initialize","type":"request","seq":0,"arguments":{"linesStartAt1":true,"columnsStartAt1":true,"pathFormat":"path","clientname":"neovim","locale":"en_US.UTF-8","adapterID":"nvim-dap","clientId":"neovim","supportsStartDebuggingRequest":true,"supportsProgressReporting":true,"supportsVariableType":true,"supportsRunInTerminalRequest":true}}
<- (E) {"body":{"capabilities":{"exceptionBreakpointFilters":[{"filter":"all","label":"all"},{"filter":"user-unhandled","label":"user-unhandled"}],"supportTerminateDebuggee":true,"supportsCancelRequest":true,"supportsConditionalBreakpoints":true,"supportsConfigurationDoneRequest":true,"supportsExceptionFilterOptions":true,"supportsExceptionInfoRequest":true,"supportsExceptionOptions":false,"supportsFunctionBreakpoints":true,"supportsSetExpression":true,"supportsSetVariable":true,"supportsTerminateRequest":true}},"event":"capabilities","seq":"1","type":"event"}
<- (E) {"body":{},"event":"initialized","seq":"2","type":"event"}
<- (R) {"body":{"exceptionBreakpointFilters":[{"filter":"all","label":"all"},{"filter":"user-unhandled","label":"user-unhandled"}],"supportTerminateDebuggee":true,"supportsCancelRequest":true,"supportsConditionalBreakpoints":true,"supportsConfigurationDoneRequest":true,"supportsExceptionFilterOptions":true,"supportsExceptionInfoRequest":true,"supportsExceptionOptions":false,"supportsFunctionBreakpoints":true,"supportsSetExpression":true,"supportsSetVariable":true,"supportsTerminateRequest":true},"command":"initialize","request_seq":0,"seq":"3","success":true,"type":"response"}
-> (C) {"command":"setBreakpoints","type":"request","seq":1,"arguments":{"lines":[10,17],"source":{"name":"Program.cs","path":"C:\/REPLACED_PATH\/Program.cs"},"breakpoints":[{"line":10},{"condition":"l.Count() == 2","line":17}],"sourceModified":false}}
<- (R) {"body":{"breakpoints":[{"id":1,"line":10,"message":"The breakpoint is pending and will be resolved when debugging starts.","verified":false},{"id":2,"line":17,"message":"The breakpoint is pending and will be resolved when debugging starts.","verified":false}]},"command":"setBreakpoints","request_seq":1,"seq":"4","success":true,"type":"response"}
-> (C) {"command":"launch","type":"request","seq":2,"arguments":{"program":"C:\\REPLACED_PATH\\bin\\Debug\\net6.0\\Test.App.Console.Runner.dll","type":"coreclr","name":"launch - netcoredbg","request":"launch"}}
<- (R) {"body":{},"command":"launch","request_seq":2,"seq":"5","success":true,"type":"response"}
-> (C) {"command":"setExceptionBreakpoints","type":"request","seq":3,"arguments":{"filters":[]}}
<- (R) {"body":{},"command":"setExceptionBreakpoints","request_seq":3,"seq":"6","success":true,"type":"response"}
-> (C) {"command":"configurationDone","type":"request","seq":4}
<- (E) {"body":{"isLocalProcess":true,"name":"dotnet","startMethod":"launch","systemProcessId":1740},"event":"process","seq":"7","type":"event"}
<- (R) {"body":{},"command":"configurationDone","request_seq":4,"seq":"8","success":true,"type":"response"}
<- (E) {"body":{"module":{"id":"c13f4999-0bf3-4d18-b209-c4f451026a97","name":"System.Private.CoreLib.dll","path":"C:\\Program Files\\dotnet\\shared\\Microsoft.NETCore.App\\6.0.14\\System.Private.CoreLib.dll","symbolStatus":"Symbols not found."},"reason":"new"},"event":"module","seq":"9","type":"event"}
<- (E) {"body":{"reason":"started","threadId":12352},"event":"thread","seq":"10","type":"event"}
<- (E) {"body":{"module":{"id":"de637fb0-eb60-4814-9579-d9ab3bce8afe","name":"Test.App.Console.Runner.dll","path":"C:\\REPLACED_PATH\\bin\\Debug\\net6.0\\Test.App.Console.Runner.dll","symbolStatus":"Symbols loaded."},"reason":"new"},"event":"module","seq":"11","type":"event"}
<- (E) {"body":{"module":{"id":"4057b45c-1c6d-4e44-b980-343a42bc93b5","name":"System.Runtime.dll","path":"C:\\Program Files\\dotnet\\shared\\Microsoft.NETCore.App\\6.0.14\\System.Runtime.dll","symbolStatus":"Symbols not found."},"reason":"new"},"event":"module","seq":"12","type":"event"}
<- (E) {"body":{"module":{"id":"c0ecec9c-21c7-4447-b83a-6f3ac0ee9ec0","name":"System.Collections.dll","path":"C:\\Program Files\\dotnet\\shared\\Microsoft.NETCore.App\\6.0.14\\System.Collections.dll","symbolStatus":"Symbols not found."},"reason":"new"},"event":"module","seq":"13","type":"event"}
<- (E) {"body":{"module":{"id":"dd66016e-7081-496c-b3ec-45b8f9b8d769","name":"System.Console.dll","path":"C:\\Program Files\\dotnet\\shared\\Microsoft.NETCore.App\\6.0.14\\System.Console.dll","symbolStatus":"Symbols not found."},"reason":"new"},"event":"module","seq":"14","type":"event"}
<- (E) {"body":{"module":{"id":"21c66bc3-96f6-4297-ab10-980937d21159","name":"System.Linq.dll","path":"C:\\Program Files\\dotnet\\shared\\Microsoft.NETCore.App\\6.0.14\\System.Linq.dll","symbolStatus":"Symbols not found."},"reason":"new"},"event":"module","seq":"15","type":"event"}
<- (E) {"body":{"module":{"id":"b15d67ca-fec7-4563-9f22-c8d3b154d9c5","name":"System.Threading.dll","path":"C:\\Program Files\\dotnet\\shared\\Microsoft.NETCore.App\\6.0.14\\System.Threading.dll","symbolStatus":"Symbols not found."},"reason":"new"},"event":"module","seq":"16","type":"event"}
<- (E) {"body":{"module":{"id":"0f922e66-3e05-4df1-817d-7566b6cb7007","name":"System.Text.Encoding.Extensions.dll","path":"C:\\Program Files\\dotnet\\shared\\Microsoft.NETCore.App\\6.0.14\\System.Text.Encoding.Extensions.dll","symbolStatus":"Symbols not found."},"reason":"new"},"event":"module","seq":"17","type":"event"}
<- (E) {"body":{"reason":"started","threadId":9240},"event":"thread","seq":"18","type":"event"}
<- (E) {"body":{"reason":"started","threadId":9948},"event":"thread","seq":"19","type":"event"}
<- (E) {"body":{"reason":"started","threadId":11816},"event":"thread","seq":"20","type":"event"}
<- (E) {"body":{"reason":"started","threadId":2588},"event":"thread","seq":"21","type":"event"}
<- (E) {"body":{"reason":"started","threadId":10668},"event":"thread","seq":"22","type":"event"}
<- (E) {"body":{"exitCode":0},"event":"exited","seq":"23","type":"event"}
<- (E) {"body":{},"event":"terminated","seq":"24","type":"event"}
viewizard commented 1 year ago
-> (C) {"command":"setBreakpoints","type":"request","seq":1,"arguments":{"lines":[10,17],"source":{"name":"Program.cs","path":"C:\/REPLACED_PATH\/Program.cs"},"breakpoints":[{"line":10},{"condition":"l.Count() == 2","line":17}],"sourceModified":false}}

I believe, debugger will never find source file with path C:\/REPLACED_PATH\/Program.cs during breakpoint resolve. You should use same path, that was included into PDB during project build.

richterr commented 1 year ago

@viewizard thanks for the response. The REPLACED_PATH part of the string is just a replacement from me for the actual path I made in the log when creating the post. The actual path is the correct one. Or are you referring to the escape sequence \/?

viewizard commented 1 year ago

As I mentioned before, you should use same path as stored in PDB with same delimiters. Debugger will not analyze path, detect delimiters changes or anything else during breakpoint resolving. If path you provide not equal to stored in PDB - breakpoint resolving will be fail all the time.

axelhj commented 10 months ago

I seem to have a related/the same issue. Basically, setting a breakpoint with a full path does not trigger. I found a workaround which is to use only the filename rather than a full path in the setBreakpoint DAP request. I am not sure if there is an issue with netcoredbg or something is going wrong (configured wrong in my build?) since the PDB file references does not include full filepaths. More info here.

viewizard commented 10 months ago

Please, provide PDB you are using and vscode interuction log (looks like log from https://github.com/Samsung/netcoredbg/issues/40#issuecomment-1443612149) during breakpoint setup fail with full path.

Note, on Windows debugger use case sensitive path checks, that mean in case you setup breakpoint with full path to souces, you should use same path as stored in PDB with same delimiters and characters.

axelhj commented 10 months ago

Hi, will do. So I made a quite minimal repro-project on dotnet 6. These are my versions:

The project I suppose is more for curiosity, I put the pdb in the repro arhive along with the logs and the debugged dll. proj1.zip repro.zip

I would like to take the chance to say thanks for a great project, it does allow me to use Neovim exclusively for my day to day coding!

PS. In regard to path case it seems to have no effect when I lowercase the whole path. It is one of the things I tried leading up to nvim-dap workaround. #16 claims to have changed this behaviour for Windows. DS.

viewizard commented 10 months ago

Please note, we don't use Neovim, I only could try to "reproduce" vscode commands inteructions in order to reproduce this issue in our test suite.

In log you provided, I see breakpoint request with path = "C:/Users/WindowsUser/code/repro/proj1/dir/Code.cs" probably you should try proper path with "windows" delimeters, like all other paths in your log path = "C:\\Users\\WindowsUser\\code\\repro\\proj1\\dir\\Code.cs"?

I also see, that PDB you provided have path C:\Users\WindowsUser11111111\code\repro\proj1\dir\Code.cs inside, but not C:\Users\WindowsUser\code\repro\proj1\dir\Code.cs. You could even see this in hex editor: Screenshot 2023-10-28 143411 In case I build project you provided, no issues, PDB hex looks proper too: Screenshot 2023-10-28 143953

BTW I am also not sure that "mix" delimeters in one path is good idea - program = "C:\\Users\\WindowsUser\\code\\repro\\proj1/bin/Debug/net6.0/proj1.dll",.

axelhj commented 10 months ago

Thanks for having a look.

So I switched to use native (windows backslash) path separators in the debug adapter and that actually does set & trigger the breakpoints even while including the full path. Unfortunately nvim-dap does not allow this path format to be configured as far as I can tell. I had to to modify the plugin source for nvim-dap in order to change what is passed here. But still, this clarifies the issue a lot! I did try to use . like the PDB file does before which of course did not trigger the breakpoint but then backslash did the trick!

I had a look at the DAP spec, it mentions as expected that the source path value is implementation definedis not specifically required to be in a specific way. The initialize request has a key for pathFormat though. I suspect that a value of 'uri' is used with remote symbols and not for local files?

About the WindowsUser in the path, it is actually not the real string since I masked it for public github. I suppose this file is only valid on the system where the debug build was created since paths are either absolute or filename-only. Something that is curious to me is that in the pdb file, the Program.cs source is only referenced by filename and no path. Maybe the full path is recreated by the debugger. The program field path separators seem to not make a difference, the debug session starts & runs fine even with mixed slashes. Supposedly Linux allows backslashes as part of filenames :-) I would say that rejecting mixed path separators from netcoredbg would probably be safer but then again maybe those are deliberately accepted & normalized.

Anyway, here is the log of a successful debug run with native path references used: dap - success.log

Finally, if I could include a setting in the launch request to make netcoredbg accept & convert forward slashes in the setBreakpoints-request, that would be really great for me personally. Not sure if that would make sense though. Those are implementation defined either way and should probably be configurable in the dap-adapter. Will take this back to the nvim-dap issue :-)

viewizard commented 10 months ago

The initialize request has a key for pathFormat though. I suspect that a value of 'uri' is used with remote symbols and not for local files?

I am sure, pathFormat option in initialize request don't related to stored in PDB paths to sources, since DLL/PDB could be created with different environments on different PCs and even on different OSes. You can't use 1 option to all this DLL/PDB for sure.

Plus, we don't have it implemented: https://github.com/Samsung/netcoredbg/blob/aafa6f3b84c2e844d832db40478c50d174340a4e/src/protocols/vscodeprotocol.cpp#L469-L474

Note, debugger don't modify sources full path (don't change delimiters or characters) related to breakpoint setup or PDB and internal logic use hashes of this full paths for fast search inside internal structures. Debugger expect, that "machine" oriented VSCode and MI protocols requests from IDE/plugins will provide proper data, same as DLL/PDB have storred, that they just created and started to debug.