Skip to content

Instantly share code, notes, and snippets.

@cjepeway

cjepeway/bt Secret

Last active August 29, 2015 14:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cjepeway/42215154aff709d3efd7 to your computer and use it in GitHub Desktop.
Save cjepeway/42215154aff709d3efd7 to your computer and use it in GitHub Desktop.
bt will stuff call sites–lines of source code–into perl6 back traces
#!/usr/bin/env perl6
constant RAKUDO-SRC = '/home/jepeway/src/tz/rakudo-fork';
#say "[working]";
for lines() {
#say "checking [$_]";
.say;
next unless m{(<[ . , ; \w \d / \- + @ ]>*) ':' (.*)};
my ($file, $lineno) = ($0, $1);
$file = RAKUDO-SRC ~ '/' ~ $file
unless $file.IO.e;
next unless $file.IO.e;
#say "$file:$lineno";
my $fh = open $file;
my $n = 0;
for $fh.lines() {
next unless ++$n == $lineno;
"• $_".say;
last
}
$fh.close();
}
jepeway@go-go-go:~/src/tz/p6$ make t/05-datetimex P6=/home/jepeway/src/tz/rakudo-fork/install/bin/perl6
prove --nocolor -e '/home/jepeway/src/tz/rakudo-fork/install/bin/perl6 -I /home/jepeway/src/tz/p6' -v t/05-datetimex
t/05-datetimex ..
1..8
ok 1 - UTC - seconds since posix epoch
not ok 2 - American/New_York - seconds since posix epoch
# Failed test 'American/New_York - seconds since posix epoch'
# at t/05-datetimex line 27
# expected: '0'
# got: '3600'
abstract method unimplemented in subclass
in method utc-offset-in-seconds at /home/jepeway/src/tz/p6/TimeZone.pm:6
in method posix at /home/jepeway/src/tz/p6/DateTimeX.pm:7
in method in-timezone at /home/jepeway/src/tz/p6/DateTimeX.pm:42
in method new at src/gen/m-CORE.setting:20304
in method new at /home/jepeway/src/tz/p6/DateTimeX.pm:34
in method later at src/gen/m-CORE.setting:20399
in method earlier at src/gen/m-CORE.setting:20451
in block <unit> at t/05-datetimex:39
# Looks like you planned 8 tests, but ran 2
# Looks like you failed 1 tests of 2
Dubious, test returned 1 (wstat 256, 0x100)
Failed 7/8 subtests
Test Summary Report
-------------------
t/05-datetimex (Wstat: 256 Tests: 2 Failed: 1)
Failed test: 2
Non-zero exit status: 1
Parse errors: Bad plan. You planned 8 tests but ran 2.
Files=1, Tests=2, 0 wallclock secs ( 0.01 usr 0.00 sys + 0.57 cusr 0.00 csys = 0.58 CPU)
Result: FAIL
make: *** [t/05-datetimex] Error 1
jepeway@go-go-go:~/src/tz/p6$ bt < /tmp/z
jepeway@go-go-go:~/src/tz/p6$ make t/05-datetimex P6=/home/jepeway/src/tz/rakudo-fork/install/bin/perl6
prove --nocolor -e '/home/jepeway/src/tz/rakudo-fork/install/bin/perl6 -I /home/jepeway/src/tz/p6' -v t/05-datetimex
t/05-datetimex ..
1..8
ok 1 - UTC - seconds since posix epoch
not ok 2 - American/New_York - seconds since posix epoch
# Failed test 'American/New_York - seconds since posix epoch'
# at t/05-datetimex line 27
# expected: '0'
# got: '3600'
abstract method unimplemented in subclass
in method utc-offset-in-seconds at /home/jepeway/src/tz/p6/TimeZone.pm:6
• multi method utc-offset-in-seconds(DateTime $when) returns Int { self.unimplemented(&?ROUTINE); }
in method posix at /home/jepeway/src/tz/p6/DateTimeX.pm:7
• method posix() {
in method in-timezone at /home/jepeway/src/tz/p6/DateTimeX.pm:42
• my DateTime $dt .= new(self.posix, :timezone($tz.utc-offset-in-seconds(self)));
in method new at src/gen/m-CORE.setting:20304
• self.bless(:$year, :$month, :$day,
in method new at /home/jepeway/src/tz/p6/DateTimeX.pm:34
• my $dt = self.new: floor($p - $leap-second).Int, :&formatter, :$timezone;
in method later at src/gen/m-CORE.setting:20399
• return self.new(self.Instant + $amount, :timezone($.timezone));
in method earlier at src/gen/m-CORE.setting:20451
• method earlier(*%unit) {
in block <unit> at t/05-datetimex:39
• my $time-change-utc = DateTimeX.new(:year(2014), :month(3), :day(9), :hour(2), :minute(0), :second(0), :timezone(UTC-TZ.new())).earlier(seconds => $off);
# Looks like you planned 8 tests, but ran 2
# Looks like you failed 1 tests of 2
Dubious, test returned 1 (wstat 256, 0x100)
Failed 7/8 subtests
Test Summary Report
-------------------
t/05-datetimex (Wstat: 256 Tests: 2 Failed: 1)
Failed test: 2
Non-zero exit status: 1
Parse errors: Bad plan. You planned 8 tests but ran 2.
Files=1, Tests=2, 0 wallclock secs ( 0.01 usr 0.00 sys + 0.57 cusr 0.00 csys = 0.58 CPU)
Result: FAIL
make: *** [t/05-datetimex] Error 1
@raiph
Copy link

raiph commented Apr 27, 2015

I might have misread your code, and the following may well have syntax errors, etc. but maybe, to replace lines 12 - 24:

    next unless $file.IO.e or ($file = RAKUDO-SRC ~ '/' ~ $file).IO.e;
    say "• ", $file.slurp-rest.lines[$lineno-1];

@cjepeway
Copy link
Author

The "next unless .. .IO.e" totally works.

Would the .slurp-rest read in the rest of the file? Or is there gather/take or some other kind of to-me magic going on whereby p6 knows that we're done with the file at $lineno and after?

Well, I suppose I'll find out.

@cjepeway
Copy link
Author

And it seems like there's something that could be done with the .IO.e.

Like

next if none map ($file, RAKUDO-SRC ~ '/' ~ $file).IO.e

that would also pluck out the first one to exist.

@raiph
Copy link

raiph commented Apr 29, 2015

Updated

is there gather/take or some other kind of to-me magic going on whereby p6 knows that we're done with the file at $lineno

No. I've concluded what I wrote was sloppily redundant -- I think my suggested code was mostly an improvement on what you had written but the use of a slurp for this was about me being insufficiently P6 fluent, not Perl 6 being lazy. To be specific:

  • The slurp* routines are eager. The .slurp-rest method (which used to be called just .slurp) will suck up the (rest of the) entire file (or whatever) associated with the IO::Handle invocant.
  • All the .lines methods are lazy. In my example code it's calling the .lines method on a string, the result of the slurp. It will stop when it reaches the $lineno line but that laziness comes after the non-lazy slurp has taken the time and RAM to slurp the whole file.

I think idiomatic lazy code would be something more like:

say "• ", $file.IO.lines[$lineno-1];

That drops the eager and unnecessary slurp and uses the lazy .lines method directly on the file handle returned by the .IO method called on $file. Aiui that will just read up to the $lineno line and then stop.

But I suspected that would leave the $file handle open. So instead I inserted a slurp, which I know opens and closes the handle, and then called Str's .lines method on its output. So my first suggestion was a sloppy slurpy version rather than a lightweight lazy one.

Since initially writing this comment I've started browsing the various lines method declarations in the source code of the existing IO::Handle module. This has led me to rewrite this comment and add these notes about what I see from the source code:

next if none map ($file, RAKUDO-SRC ~ '/' ~ $file).IO.e

I don't think that will compile. Also, nothing assigns to $file (for use with the subsequent slurp or whatever) if the RAKUDO-SRC prefix is needed. And if you included the missing assignment, the map would mean the assignment and test would happen even if it wasn't needed. Also, none is a junction and junctions may do things in an unknown order (possibly concurrently) whereas your original logic was guaranteed to first test the file without the prefix and stick with that if it existed.

Maybe:

next unless first *.IO.e, $file, $file [R~]= RAKUDO-SRC ~ '/';

p6doc covers first.

[R~] is the Reverse metaop applied to concat. It prepends instead of appends.

The A [op]= B construction is the same as A = A op B; the result of the operation is assigned back in to A (in this case A is $file).

Getting even more crazy:

next unless first *.IO.e, $file, RAKUDO-SRC ~ '/' R[R~]= $file;

The outer R of the slightly insane R[R~]= reverses the assignment. So, iff the unprepended $file doesn't exist, the RAKUDO-SRC ~ '/' on the left gets prepended to the variable ($file) on the right. :)

@cjepeway
Copy link
Author

My word, @raiph, thanks for the write-up.

Yeah, knew that map line wouldn't work (or even parse), just meant to illustrate where I was reaching.

And, dayum, never expected this to go where you've taken it. Hoped mind you, felt perhaps it could, but...wow.

I'm really starting to love perl6. A lot of that is b/c of folk who just jump in and explain the way you have.

On to the code...

Both list elts given to first are evaluated, so $file ends up always points to the rakudo source dir.

You can see it here

my $f = 'a';
say first * ~~ /a/, $f, $f [R~] 'b';

which yields ba. That's b/c:

my $f = "a";
say ($f, $f [R~]= "b");

assigns to $f and gets you ba ba.

Maybe the 2nd elt can be thunked, somehow? So that the RAKUDO-SRC expression is only evaluated if first gets to it?

As for $file.IO.lines()[$i]] vs. looping through $file by hand:

$ time

Well...speaking of time, I'm out of it. I'll update this comment with timings later today (I hope). But indexing into .IO.lines() is way faster.

@cjepeway
Copy link
Author

So, ~3x faster on my box:

$ time perl6 -e 'my $n = 15000 ;  say "/usr/share/dict/words".IO.lines()[$n-1]; '
Tom

real    0m0.331s
user    0m0.306s
sys     0m0.024s
$ time perl6 -e 'my $n = 15000 ; my $fh = open "/usr/share/dict/words"; my $i = 0; for $fh.lines() { next unless ++$i == $n; .say; last } '
Tom

real    0m0.976s
user    0m0.947s
sys     0m0.028s

I'll be looking for your comments on #perl6 re: bugs in .lines()...but I might first try to find them on my own as an exercise.

Thanks again, this has been grand.

@raiph
Copy link

raiph commented May 1, 2015

I'm really starting to love perl6. A lot of that is b/c of folk who just jump in and explain the way you have.

:)

Both list elts given to first are evaluated, so $file ends up always points to the rakudo source dir.

Oh! :)

say first * ~~ /a/, $f, $f [R~] 'b';

Note you could have just written:

say first /a/, $f, $f [R~]= 'b';

(Because Any method first, which is called by sub first, has a Regex variant.)

But yeah. I was thinking Rakudo wouldn't evaluate all the list elems because first is lazy. But it does. So it's your turn. :) I'll return later to read what you've found out about this...

Interesting timing difference.

This stuff is all about list processing speed. And that will (hopefully) be profoundly altered by the recently begun GLR. Basically I recommend most folk don't worry about timing for this sort of stuff until August. If you want to focus on timing related to basic list operations because you find the news exciting, then pay close attention to GLR news.

Thanks again, this has been grand.

Yw. Please also feel free to post reddits and comments in /r/perl6. And of course, ask questions on #perl6. :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment