# file io-open-hack.pl use strict; use warnings; use IO::File; BEGIN{ package IO::File { use Hash::Util::FieldHash qw(id_2obj); Hash::Util::FieldHash::fieldhash my %fh; *IO::File::_new = \&IO::File::new; *IO::File::_open = \&IO::File::open; { no warnings 'redefine'; *IO::File::new = sub { my $io = &_new; $fh{$io} = { new => "@{[caller]}" }; $io; }; *IO::File::open = sub { my $io = $_[0]; $fh{$io}->{open} = "$_[1] at @{[caller]}"; &_open; }; } sub open_fds { grep { $_->{fh}->opened } known_fds(); } sub known_fds { map { { new => $fh{$_}->{new}, # next is dubious, it leaks the object... fh => id_2obj($_), # ... so we should return it stringified? #fh => "@{[id_2obj($_)]}", open => $fh{$_}->{open}, } } keys %fh; } } } package main; use IO::File; { my $fh = IO::File->new(); $fh->open( "blorfldyick",'>'); print $fh "foo\n"; print "after open:\n"; report_fds($_) for qw(open known); close $fh; print "after close:\n"; report_fds($_) for qw(open known); } # $fh is out of scope here print "after scope:\n"; report_fds($_) for qw(open known); sub report_fds { my $type = shift; my $meth = "${type}_fds"; my @fds = IO::File->$meth(); print " $type files:"; if (@fds) { print $/; print " fh: $_->{fh}\n" . " new: $_->{new}\n" . " open: $_->{open}\n" for @fds; } else { print " none\n"; } } __END__ after open: open files: fh: IO::File=GLOB(0x137a3b8) new: main io-open-hack.pl 45 open: blorfldyick at main io-open-hack.pl 46 known files: fh: IO::File=GLOB(0x137a3b8) new: main io-open-hack.pl 45 open: blorfldyick at main io-open-hack.pl 46 after close: open files: none known files: fh: IO::File=GLOB(0x137a3b8) new: main io-open-hack.pl 45 open: blorfldyick at main io-open-hack.pl 46 after scope: open files: none known files: none