Nihilus118 / perl-debug-adapter

MIT License
11 stars 2 forks source link

Debugger fails to start when "watch" window contains "not available" expression #3

Closed didster closed 1 year ago

didster commented 1 year ago

VSCode: 1.81.0 Module: 1.0.3

Suppose you have a small bit of code like this:

my $brand = PrestaDB::Manufacturer->findOrCreate("Foo");

PrestaDB::Manufacturer is a module, findOrCreate returns a blessed reference.

Now debug past the $brand = line, all is well.

Then add a watch in VSCode for some property on the blessed reference. In the above case I'm adding $brand->name but of course it depends on the module used. Again is all fine - the value of $brand->name is displayed in the watch.

Then stop the debug session. VSCode then changes the watch value for $brand to "not available".

Then try and restart a debug session. It starts, very briefly stops at the breakpoint, then the debug session terminates. This can be solved by deleting the watch.

Full trace is below

This is different than if you add a watch to a simple lexical variable which defaults to undef and works just fine.

I'll try and add a simple example module and program if I get a sec. It does this even with system modules.

CWD: ....
Script: ....
Perl executable: perl
Command: f ....
Starting debugging
Command: package DB; $DB::preview = 0;
Could not change file context: No file matching '....' is loaded.
Command: f import_cembre.pl
Command: notrace
Successfully changed file context to ....: Choosing .... matching '....':
Command: b 139 undefined
Command: o PrintRet=0
Command: b 264 undefined
Command: package DB; *DB::postponed = sub { return sub { if ( 'GLOB' eq ref(\$_[0]) && $_[0] =~ /<(.*)\s*$/s) { if ($DB::single == 0) { print STDERR "continue "; } $DB::single = 1; print STDERR "loaded source $1\n"; } } ; }->(\\&DB::postponed)
Command: b 295 undefined
Command: use PadWalker qw/peek_our peek_my/; use Data::Dumper;
Command: b 364 undefined
StopOnEntry: false
Command: c
Command: c
Command: c
Command: print STDERR threads->self()->tid()
Command: c
Command: c
Command: c
Command: c
Command: c
Command: c
Command: c
Command: c
Command: c
Command: c
Command: print STDERR threads->self()->tid()
Command: T
Command: print STDERR Data::Dumper->new([$n], [])->Deepcopy(1)->Sortkeys(1)->Indent(1)->Terse(0)->Trailingcomma(1)->Useqq(1)->Dump()
Command: print STDERR Data::Dumper->new([{%all_acc}], [])->Deepcopy(1)->Sortkeys(1)->Indent(1)->Terse(0)->Trailingcomma(1)->Useqq(1)->Dump()
Command: print STDERR Data::Dumper->new([$brand->name], [])->Deepcopy(1)->Sortkeys(1)->Indent(1)->Terse(0)->Trailingcomma(1)->Useqq(1)->Dump()
Command: print STDERR Data::Dumper->new([$k], [])->Deepcopy(1)->Sortkeys(1)->Indent(1)->Terse(0)->Trailingcomma(1)->Useqq(1)->Dump()
Reached breakpoint while dumping variable
Command: c
Command: print STDERR Data::Dumper->new([$v], [])->Deepcopy(1)->Sortkeys(1)->Indent(1)->Terse(0)->Trailingcomma(1)->Useqq(1)->Dump()
2023/08/07 22:17:52|INFO|main|369|Loading YAML ...
Reached breakpoint while dumping variable
Command: c
Command: print STDERR Data::Dumper->new([{%prod_info}], [])->Deepcopy(1)->Sortkeys(1)->Indent(1)->Terse(0)->Trailingcomma(1)->Useqq(1)->Dump()
Reached breakpoint while dumping variable
Command: c
Command: print STDERR Data::Dumper->new([$k], [])->Deepcopy(1)->Sortkeys(1)->Indent(1)->Terse(0)->Trailingcomma(1)->Useqq(1)->Dump()
Reached breakpoint while dumping variable
Command: c
Command: print STDERR Data::Dumper->new([$k2], [])->Deepcopy(1)->Sortkeys(1)->Indent(1)->Terse(0)->Trailingcomma(1)->Useqq(1)->Dump()
2023/08/07 22:17:52|INFO|main|371|Completed loading YAML
2023/08/07 22:17:52|INFO|main|406|Process B1300-CE [product - /product/details/42700] ...
Reached breakpoint while dumping variable
Command: c
Command: print STDERR Data::Dumper->new([$v2], [])->Deepcopy(1)->Sortkeys(1)->Indent(1)->Terse(0)->Trailingcomma(1)->Useqq(1)->Dump()

Reached breakpoint while dumping variable
Command: sleep(.5)
Command: print STDERR Data::Dumper->new([$v], [])->Deepcopy(1)->Sortkeys(1)->Indent(1)->Terse(0)->Trailingcomma(1)->Useqq(1)->Dump()
didster commented 1 year ago

Here is a simple module which demonstrates:

TestModule.pm

use strict;
use warnings;

package TestModule;

sub new {
    my $class = shift;
    return bless {
        name => "Hello"
    }, $class;
}

sub name {
    my $class = shift;
    return $class->{name};
}

1;

Then test program:

#!/usr/bin/perl
use strict;
use warnings;
use FindBin;
use lib "$FindBin::Bin";
use TestModule;

my $test = TestModule->new();

my $dummy = 0;

Adding a watch for $test->name and a break point on the my $test line causes the above behavour.

Nihilus118 commented 1 year ago

Hello @didster,

thank you for the great example! I was able to reproduce the error quickly. Trying to print the value of $test->name before the variable $test was initialized caused an error of the pattern ^Can't call method ".*" on an undefined value at .* line \d+\.$ This text could not be parsed by the variable parser and resulted in an error. I just built a fix that matches the previous pattern against the debugger output before the variable parser inside the debug adapter takes over. If the pattern matches the variable value will be returned as undef. I bundled the fix together with some improvements to the logging and released it as version 1.0.4.

I hope this fixes the problem. I am pretty sure you are not the only one wanting to watch nested variable values before the parent variable is initialized.

didster commented 1 year ago

Great! Glad it was easy to reproduce. I can confirm its all working now as expected for me. It's certainly not uncommon (for me anyway) to have 100s of things in watch usually watches from other bits of code or in other modules as I'm stepping through, so I totally agree its a needed fix. Cheers