Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things
 
PerlMonks  

comment on

( [id://3333]=superdoc: print w/replies, xml ) Need Help??
As always definition matters. perl -de0 is more than just a debugger. ... it's also Perl's main REPL, it's pretty good for ad hoc testing and interactive demos. I use it regularly for that.

I've seen your posts from (what are we gonna call it?) output in perl -de0 mode for years now, and they've mostly been over my head. Only recently have I discovered that I have a REPL too. I could have replicated your REPL.

Others even use it as a Perl Shell, because it's very customizable.

Can you elaborate?

But it is very valuable when analyzing monolithic foreign code (e.g. B::Deparse)

I would extend that to foreign code in general. I can't understand and integrate others' code into mine without it, whilst working alone. The number of keystrokes I save using the debugger is immeasurable. With successive use of save/source, it's like a trail in the forest and can even tell a story, and it's one you keep telling until you get out of the forest. I worked a little harder on a modifed script from bliako, and I'm able to point out places in code as perl -de0 sees it, and I can get into the nitty-gritty of execution. I'll repost the 2020 perl -de0 demonstration by Ricardo Signes, for reference. I place sources and debugger output in readmore tags:

This script has grown to the low end of medium size, both with source and output, where it is difficult to traverse and understand without help. Many will be trapped between the Scylla of knowing perl, and the Charybdis of dealing with celestial mechanics. Imagine if you were in charge of steering a ship through the Straits of Messina on $date_str = "2021-10-14"; and $time_str = "03:22:31";. Furthermore, you had to do it with the naked eye using the planets because of a solar flare. (Isn't that a lot of movies nowadays?) Would you make it with precise calculations or dashed to the rocks or depths like so many others?

I use the word source in two different contexts. Both seem to be scripts, with one operating on the other. The source that will execute is:

#!/usr/bin/perl use v5.030; # strictness implied use warnings; use feature qw[ signatures ]; no warnings qw[ experimental::signatures ]; use Astro::Coord::ECI::Utils 'deg2rad'; use Astro::Coords; use Astro::MoonPhase; use Astro::Telescope; use DateTime; use DateTime::Format::ISO8601; use DateTime::TimeZone; use Data::Dumper; use Log::Log4perl; use Try::Tiny; ###### # Calculate the moon phase of an event in time. # Required is the date (time defaults to 00:00:01) of the event # The moon-phase calculator does not need a location # since moon phase is the same across the planet (more-or-less) # with the usual antipodean side-effects # However, by specifying a timezone or even coordinates of the # event conversion of event's time to UTC is more accurate # (else UTC time is assumed) # author: bliako, modified by aldebaran # date: 2021-10-10 ###### my $debug = 1; my $time_str = "03:22:31"; my $alt = 0; my $date_str = "2021-10-14"; my @events = ( # { # # example event specification # "name" => "a name for reference, required", # "date" => "date of the event in YYYY-MM-DD", # "time" => "optional time as hh:mm:ss, default is 00:00:01", # #specify optional location using one of these for calculating # #epoch which uses UTC time, # #otherwise, UTC timezone will be assumed for event's time: # "timezone" => "standard timezone name, e.g. America/Havana", # "location" => "cuba", # "location" => {lon=>-81.137633, lat=>22.17927}, # }, { "name" => "in the 'Ho", "date" => $date_str, "time" => $time_str, "location" => { lon => -116.2, lat => 43.61 }, }, { "name" => "on the Scylla", "date" => $date_str, "time" => $time_str, "location" => { lon => 38.245833, lat => 15.6325 }, }, { "name" => "in Antipo' 'Ho", "date" => $date_str, "time" => $time_str, "location" => { lon => 63.8, lat => -43.61 }, }, { "name" => "at Antipodlean Scylla", "date" => $date_str, "time" => $time_str, "location" => { lon => -141, lat => -15.6325 }, }, ); # Not wanting to divine the configuration file for Log::Log4perl, modu +le used in # the query. Better for SSCCE considerations. thx parv { package Log::Log4perl; sub get_logger ($pkg) { return bless [], $pkg; } sub show (@arg) { warn @arg, "\n"; } sub debug ( $ignore, @rest ) { show( 'DEBUG: ', @rest ); } sub info ( $ignore, @rest ) { show( 'INFO: ', @rest ); } sub warn ( $ignore, @rest ) { show( 'WARNING: ', @rest ); } sub error ( $ignore, @rest ) { die 'ERROR: ', @rest, "\n"; } } my $logger = Log::Log4perl->get_logger(); $logger->info("$0"); ## event loop for my $event (@events) { my $epoch = parse_event($event); my ( $MoonPhase, $MoonIllum, $MoonAge, $MoonDist, $MoonAng, $SunDist +, $SunAng ) = phase($epoch); $logger->info("Moon age: $MoonAge days"); $logger->info( "Moon phase: " . sprintf( "%.1f", 100.0 * $MoonPhase ) . " % of cycle (birth-to-death)" ); $logger->info( "Moon's illuminated fraction: ", sprintf( "%.1f", 100.0 * $MoonIllum ) . " % of full disc" ); ## determine altitude # usage: die unless exists $event->{location}; # restriction my $return = get_elevation_from_coordinates( $event->{location}->{la +t}, $event->{location}->{lon}, 0 ); $logger->info("return from the google is $return meters"); my $observer = $return + 2; #(most people stand) $logger->info( event2str($event) . " at height $observer meters" ); $logger->info("Object\tAzimuth\t\tElevation"); for my $name (qw/Venus Jupiter Moon Saturn/) { my $planet = Astro::Coords->new( planet => $name ); # use DateTime my $date = DateTime->from_epoch( epoch => $epoch, time_zone => 'UT +C' ); $planet->datetime($date); $planet->telescope( Astro::Telescope->new( Name => $event->{name}, Long => deg2rad( $event->{location}->{lon} ), Lat => deg2rad( $event->{location}->{lat} ), Alt => $observer, #meters above +sea level ) ); my $az = $planet->az( format => 'deg' ); my $el = $planet->el( format => 'deg' ); $logger->info("$name\t$az\t\t$el"); } ## end planets loop } ## end event loop sub event2str { my $event = shift; if ( !exists $event->{_is_parsed} ) { warn "event has not been parsed, just dumping it..."; print Dump($event); } my $str = $event->{name} . " on " . $event->{datetime} . " (" . $event->{datetime}->epoch . " seconds unix-epoch)" . " timezone: " . $event->{datetime}->time_zone->name; if ( exists $event->{location} ) { if ( ref( $event->{location} ) eq 'HASH' ) { $str .= " (lat: " . $event->{location}->{lat} . ", lon: " . $event->{location}->{lon} . ")"; } else { $str .= "(" . $event->{location} . ")" } } return $str; } sub parse_event { my $event = shift; if ( !exists $event->{date} ) { die "date field is missing from even +t." } my $datestr = $event->{date}; die "event does not have a 'name' field, please specify one, anythin +g really." unless exists $event->{name}; my $timestr = "00:00:01"; if ( exists $event->{time} ) { $timestr = $event->{time}; print "event2epoch(): setting time to '$timestr' ...\n" if $debug > 0; die "time '$timestr' is not valid, it must be in the form 'hh:mm:s +s'." unless $timestr =~ /^\d{2}:\d{2}:\d{2}$/; } else { $event->{time} = $timestr } my $isostr = $datestr . 'T' . $timestr; my $dt = DateTime::Format::ISO8601->parse_datetime($isostr); die "failed to parse date '$isostr', check date and time fields." unless defined $dt; $event->{datetime} = $dt; my $tzstr = 'UTC'; if ( exists $event->{timezone} ) { $tzstr = $event->{timezone}; print "event2epoch(): found a timezone via 'timezone' field as '$tzstr' (tha +t does not mean it is valid) ...\n" if $debug > 0; } elsif ( exists $event->{location} ) { my $loc = $event->{location}; if ( ( ref($loc) eq '' ) && ( $loc =~ /^[a-zA-Z]$/ ) ) { # we have a location string my @alltzs = DateTime::TimeZone->all_names; my $tzstr; for (@alltzs) { if ( $_ =~ /$loc/i ) { $tzstr = $_; last } } die "event's location can not be converted to a timezone, consider specify +ing the 'timezone' directly or setting 'location' coordinates with: \ +[lat,lon\]." unless $tzstr; print "event2epoch(): setting timezone via 'location' name to '$timestr' ... +\n" if $debug > 0; } elsif ( ( ref($loc) eq 'HASH' ) && ( exists $loc->{lat} ) && ( exists $loc->{lon} ) ) { # we have a [lat,lon] array for location require Geo::Location::TimeZone; my $gltzobj = Geo::Location::TimeZone->new(); $tzstr = $gltzobj->lookup( lat => $loc->{lat}, lon => $loc->{lon +} ); if ( !$tzstr ) { die "timezone lookup from location coordinates lat:" . $loc->{lat} . ", lon:" . $loc->{lon} . " has failed."; } print "event2epoch(): setting timezone via 'location' coordinate +s lat:" . $loc->{lat} . ", lon:" . $loc->{lon} . " ...\n" if $debug > 0; } } if ($tzstr) { print "event2epoch(): deduced timezone to '$tzstr' and setting it +...\n" if $debug > 0; try { $dt->set_time_zone($tzstr) } catch { die "$_\n failed to set the timezone '$tzstr', is it valid?" } } $event->{_is_parsed} = 1; $event->{epoch} = $dt->epoch; return $event->{epoch}; } sub get_elevation_from_coordinates { my ( $lat, $lon, $debug ) = @_; use LWP::UserAgent; use HTTP::Request; use Data::Roundtrip; $debug //= 0; my $ua = LWP::UserAgent->new( agent => 'Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox +/78.0', ); my $response; my $payload = 'latitude=' . $lat . '&longitude=' . $lon . '&application_max_assets_mtime=1559625591'; my $payloadlen = length($payload); # this request was translated from Curl command-line # by [Corion]'s https://corion.net/curl2lwp.psgi my $req = HTTP::Request->new( 'POST' => 'https://www.mapcoordinates.net/admin/component/edit/Vpc_MapCoordinate +s_Advanced_GoogleMapCoords_Component/Component/json-get-elevation', [ 'Connection' => 'keep-alive', 'Accept' => '*/*', 'Accept-Encoding' => 'gzip, x-gzip, deflate, x-bzip2, bzip2', 'Accept-Language' => 'en-US,en;q=0.5', # 'Host' => 'www.mapcoordinates.net:443', 'Referer' => 'https://www.mapcoordinates.net/en', 'User-Agent' => 'Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firef +ox/78.0', 'Content-Length' => $payloadlen, 'Content-Type' => 'application/x-www-form-urlencoded; charse +t=UTF-8', 'DNT' => '1', 'Origin' => 'https://www.mapcoordinates.net', 'X-Requested-With' => 'XMLHttpRequest' ], $payload ); die "call to HTTP::Request has failed" unless $req; if ( $debug > 0 ) { print "$0 : $payload\n$0 : sending above payload, of $payloadlen bytes +...\n"; } $response = $ua->request($req); die "Error fetching: " . $response->status_line unless $response->is_success; my $content = $response->decoded_content; my $data = Data::Roundtrip::json2perl($content); die "failed to parse received data:\n$content\n" unless exists $data->{'elevation'}; return $data->{'elevation'}; } __END__

I was concerned that some of my data were coming back as zero from the google as errors, so I created an amalgamated story to test it. This is 5.mp.monk.txt, which is the path I've tapped out to get into the nitty gritty.

#source 5.1.mp.txt #source 5.mp.txt b 262 c s v p @_ p $debug b 312 b 317 c v n n s v l 280-300 b 289 c n r s v n p $json_string l 198-215 r c c c c s v n p $json_string c c c c s n p $json_string B * c #save 5.mp.monk.txt

There follows abridged output in code tags. I'll comment on it occasionally in p tags.

$ perl -d 5.mp.pl Loading DB routines from perl5db.pl version 1.55 Editor support available. Enter h or 'h h' for help, or 'man perldebug' for more help. Subroutine get_logger redefined at 5.mp.pl line 83. at 5.mp.pl line 83. main::(5.mp.pl:32): my $debug = 1; DB<1> source 5.1.mp.txt + >> #source 5.mp.txt

The source command loads a previous sequence. If execution or values therein had run into trouble, I save the commands and shave off the last one. I clear out previous useless prints and tepid steps, that is repetive n, n, n. Also, any boondoggles from hitting s and going deep into stuff above my head or entirely irrelevant. In this case, source 5.mp.txt was the first 8 commands, where I set all breakpoints:

>> b 262 >> c INFO: 5.mp.pl at 5.mp.pl line 94. event2epoch(): setting time to '03:22:31' ... event2epoch(): setting timezone via 'location' coordinates lat:43.61, +lon:-116.2 ... event2epoch(): deduced timezone to 'Etc/GMT-7' and setting it ... INFO: Moon age: 8.12733852952393 days at 5.mp.pl line 103. INFO: Moon phase: 27.5 % of cycle (birth-to-death) at 5.mp.pl line 104. INFO: Moon's illuminated fraction: 57.9 % of full disc at 5.mp.pl line 107. main::get_elevation_from_coordinates(5.mp.pl:262): 262: my ( $lat, $lon, $debug ) = @_; >> s main::get_elevation_from_coordinates(5.mp.pl:268): 268: $debug //= 0; >> v 265: use HTTP::Request; 266: use Data::Roundtrip; 267 268==> $debug //= 0; 269 270: my $ua = LWP::UserAgent->new( agent => 271 'Moz<p>These values are not the zero values I was concer +ned about</p>illa/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Fir +efox/78.0', ); 272 273: my $response; 274: my $payload = >> p @_ 43.61-116.20 >> p $debug 0 >> b 312 >> b 317

I'll have questions about the above, but let's keep on moving. Hit c to get to continue to a breakpoint. v lets me see where I am with some surrounding code, and then s steps into it:

>> c main::get_elevation_from_coordinates(5.mp.pl:312): 312: $response = $ua->request($req); >> v 309 "$0 : $payload\n$0 : sending above payload, of $payloadl +en bytes...\n"; 310 } 311 312==>b $response = $ua->request($req); 313: die "Error fetching: " . $response->status_line 314 unless $response->is_success; 315 316: my $content = $response->decoded_content; 317:b my $data = Data::Roundtrip::json2perl($content); 318 die "failed to parse received data:\n$content\n" ... >> s HTTP::Message::decoded_content(/usr/share/perl5/HTTP/Message.pm:283): 283: my($self, %opt) = @_; >> v 280 281 sub decoded_content 282 { 283==> my($self, %opt) = @_; 284: my $content_ref; 285: my $content_ref_iscopy; 286 287: eval { 288: $content_ref = $self->content_ref; 289: die "Can't decode ref content" if ref($content_ref) ne "SC +ALAR"; >> l 280-300 280 281 sub decoded_content 282 { 283==> my($self, %opt) = @_; 284: my $content_ref; 285: my $content_ref_iscopy; 286 287: eval { 288: $content_ref = $self->content_ref; 289: die "Can't decode ref content" if ref($content_ref) ne "SC +ALAR"; 290 291: if (my $h = $self->header("Content-Encoding")) { 292: $h =~ s/^\s+//; 293: $h =~ s/\s+$//; 294: for my $ce (reverse split(/\s*,\s*/, lc($h))) { 295: next unless $ce; 296: next if $ce eq "identity" || $ce eq "none"; 297: if ($ce eq "gzip" || $ce eq "x-gzip") { 298: require IO::Uncompress::Gunzip; 299: my $output; 300: IO::Uncompress::Gunzip::gunzip($content_ref, \$out +put, Transparent => 0) >> b 289 >> c HTTP::Message::decoded_content(/usr/share/perl5/HTTP/Message.pm:289): 289: die "Can't decode ref content" if ref($content_ref) ne "SC +ALAR"; >> n HTTP::Message::decoded_content(/usr/share/perl5/HTTP/Message.pm:291): 291: if (my $h = $self->header("Content-Encoding")) { >> r scalar context return from HTTP::Message::decoded_content: '{"elevatio +n":821,"success":true,"requestNum":"163546920137"}'

I took a gander through HTTP::Message, looking at internals. It's typical for me to do s, v, l 280-300 to see where execution has led. It will usually be a place I haven't seen before. Look how informative the result of r is, which is obviously to connote returning from a subroutine.

main::get_elevation_from_coordinates(5.mp.pl:317): 317: my $data = Data::Roundtrip::json2perl($content); 318: die "failed to parse received data:\n$content\n" >> s Data::Roundtrip::json2perl(/usr/local/share/perl/5.30.0/Data/Roundtrip +.pm:192): 192: my $json_string = $_[0]; >> v 189: return $pv 190 } 191 sub json2perl { 192==> my $json_string = $_[0]; 193 #my $params = defined($_[1]) ? $_[1] : {}; 194: my $pv = JSON::decode_json(Encode::encode_utf8($json_strin +g)); 195: if( ! defined $pv ){ warn "json2perl() : error, call to j +son2perl() has failed"; return undef } 196: return $pv; 197 } 198 sub json2json { >> n Data::Roundtrip::json2perl(/usr/local/share/perl/5.30.0/Data/Roundtrip +.pm:194): 194: my $pv = JSON::decode_json(Encode::encode_utf8($json_strin +g)); >> p $json_string {"elevation":821,"success":true,"requestNum":"163546920137"} >> l 198-215 198 sub json2json { 199: my $json_string = $_[0]; 200: my $params = defined($_[1]) ? $_[1] : {}; 201 202: my $pv = json2perl($json_string, $params); 203: if( ! defined $pv ){ warn "json2perl() : error, call to j +son2perl() has failed"; return undef } 204: $json_string = perl2json($pv, $params); 205: if( ! defined $json_string ){ warn "json2perl() : error, +call to perl2json() has failed"; return undef } 206 207: return $json_string; 208 } 209 sub yaml2yaml { 210: my $yaml_string = $_[0]; 211: my $params = defined($_[1]) ? $_[1] : {}; 212 213: my $pv = yaml2perl($yaml_string, $params); 214: if( ! defined $pv ){ warn "yaml2perl() : error, call to y +aml2perl() has failed"; return undef } 215: $yaml_string = perl2yaml($pv, $params); >> r scalar context return from Data::Roundtrip::json2perl: 'elevation' => +821 'requestNum' => 163546920137 'success' => JSON::PP::Boolean=SCALAR(0x55e918df2b78) -> 1

In this part, we step into bliako's json software, which I find quite readable. Again, I take a look and return with r with that nice summary. However, these are not the zero values I was concerned about, so I'll continue with c:

... event2epoch(): setting time to '03:22:31' ... event2epoch(): setting timezone via 'location' coordinates lat:15.6325 +, lon:38.245833 ... event2epoch(): deduced timezone to 'Africa/Asmara' and setting it ... ... {"elevation":841,"success":true,"requestNum":"163546921268"} DB<13> c + INFO: return from the google is 841 meters at 5.mp.pl line 115. INFO: on the Scylla on 2021-10-14T03:22:31 (1634170951 seconds unix-ep +och) timezone: Africa/Nairobi (lat: 15.6325, lon: 38.245833) at heigh +t 843 meters

Apparently, this point is on the Scylla side of the pair. If Charybdis existed, it would have a negative value, which is, of course, a physical nonstarter in the 21st century. I only have this one script so far to probe the validity of these values, but it's certainly true that small changes in input will have huge changes in output in certain neighborhoods. No false zero here.

... event2epoch(): setting time to '03:22:31' ... event2epoch(): setting timezone via 'location' coordinates lat:-43.61, + lon:63.8 ... event2epoch(): deduced timezone to 'Etc/GMT+4' and setting it ... ... DB<13> c + main::get_elevation_from_coordinates(5.mp.pl:317): 317: my $data = Data::Roundtrip::json2perl($content); 318: die "failed to parse received data:\n$content\n" DB<13> s + Data::Roundtrip::json2perl(/usr/local/share/perl/5.30.0/Data/Roundtrip +.pm:192): 192: my $json_string = $_[0]; DB<13> n + Data::Roundtrip::json2perl(/usr/local/share/perl/5.30.0/Data/Roundtrip +.pm:194): 194: my $pv = JSON::decode_json(Encode::encode_utf8($json_strin +g)); DB<13> p $json_string + {"elevation":0,"success":true,"requestNum":"163546928850"}

Then I finally got ^^^^there to see that google thinks that zero is the right value as opposed to returning it to indicate failure. So I got the peek I needed, what now? Clear the breakpoints and see if it rolls to the finish without trouble:

DB<14> B * + Deleting all breakpoints... DB<15> c + INFO: return from the google is 0 meters at 5.mp.pl line 115. INFO: in Antipo' 'Ho on 2021-10-14T03:22:31 (1634196151 seconds unix-e +poch) timezone: -0400 (lat: -43.61, lon: 63.8) at height 2 meters at 5.mp.pl line 117. ... Debugged program terminated. Use q to quit or R to restart, use o inhibit_exit to avoid stopping after program termination, h q, h R or h o to get additional info. DB<15> save 5.mp.monk.txt + commands saved in 5.mp.monk.txt DB<16> q + $

I think I edited out the values from the Scylla with the telescope:

INFO: on the Scylla on 2021-10-14T03:22:31 (1634170951 seconds unix-ep +och) timezone: Africa/Nairobi (lat: 15.6325, lon: 38.245833) at heigh +t 843 meters INFO: Object Azimuth Elevation INFO: Venus 168.793996164728 -81.0110616258686 INFO: Jupiter 258.080321870357 -14.8575841846033 INFO: Moon 251.270738258432 -35.2685064885712 INFO: Saturn 256.410482564197 -30.2637811706649

So if your passage depended on seeing these objects at that place and time, well then, you were screwed. The Gods are cruel.

Q1) What makes my Log4perl section a signature? This version is from parv, and I like that it gives me line numbers, but I don't see code for that. (?)

Q2) How would I turn the above script into "monolithic" foreign code?

Q3) Does Data::Roundtrip cover the same ground that JSON::XS did? I can't disambiguate what XS means.

Q4) What operator is this? I believe it takes my global value of 1 for $debug and makes it zero:

268==>      $debug //= 0;

Q5) Finally, reading all around these issues, I found this one-liner and was wondering if this would fall under the umbrella of what "the debugger" might be. What is happening with this command?

$ perl -Mre=debug -e "/just|another|perl|hacker/" Compiling REx "just|another|perl|hacker" ~ tying lastbr BRANCH (11) to ender END (15) offset 4 Final program: 1: TRIEC-EXACT[ahjp] (15) <just> <another> <perl> <hacker> 15: END (0) stclass AHOCORASICKC-EXACT[ahjp] minlen 4 Freeing REx: "just|another|perl|hacker" $

Thx for your comment


In reply to Re^5: a *working* JSON module (Perl's Debugger), related issues by Aldebaran
in thread Can someone please write a *working* JSON module by cnd

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post; it's "PerlMonks-approved HTML":



  • Are you posting in the right place? Check out Where do I post X? to know for sure.
  • Posts may use any of the Perl Monks Approved HTML tags. Currently these include the following:
    <code> <a> <b> <big> <blockquote> <br /> <dd> <dl> <dt> <em> <font> <h1> <h2> <h3> <h4> <h5> <h6> <hr /> <i> <li> <nbsp> <ol> <p> <small> <strike> <strong> <sub> <sup> <table> <td> <th> <tr> <tt> <u> <ul>
  • Snippets of code should be wrapped in <code> tags not <pre> tags. In fact, <pre> tags should generally be avoided. If they must be used, extreme care should be taken to ensure that their contents do not have long lines (<70 chars), in order to prevent horizontal scrolling (and possible janitor intervention).
  • Want more info? How to link or How to display code and escape characters are good places to start.
Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (4)
As of 2024-04-24 18:29 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found