| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package IPC::Run::Debug; | 
| 2 |  |  |  |  |  |  |  | 
| 3 |  |  |  |  |  |  | =pod | 
| 4 |  |  |  |  |  |  |  | 
| 5 |  |  |  |  |  |  | =head1 NAME | 
| 6 |  |  |  |  |  |  |  | 
| 7 |  |  |  |  |  |  | IPC::Run::Debug - debugging routines for IPC::Run | 
| 8 |  |  |  |  |  |  |  | 
| 9 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 10 |  |  |  |  |  |  |  | 
| 11 |  |  |  |  |  |  | ## | 
| 12 |  |  |  |  |  |  | ## Environment variable usage | 
| 13 |  |  |  |  |  |  | ## | 
| 14 |  |  |  |  |  |  | ## To force debugging off and shave a bit of CPU and memory | 
| 15 |  |  |  |  |  |  | ## by compile-time optimizing away all debugging code in IPC::Run | 
| 16 |  |  |  |  |  |  | ## (debug => ...) options to IPC::Run will be ignored. | 
| 17 |  |  |  |  |  |  | export IPCRUNDEBUG=none | 
| 18 |  |  |  |  |  |  |  | 
| 19 |  |  |  |  |  |  | ## To force debugging on (levels are from 0..10) | 
| 20 |  |  |  |  |  |  | export IPCRUNDEBUG=basic | 
| 21 |  |  |  |  |  |  |  | 
| 22 |  |  |  |  |  |  | ## Leave unset or set to "" to compile in debugging support and | 
| 23 |  |  |  |  |  |  | ## allow runtime control of it using the debug option. | 
| 24 |  |  |  |  |  |  |  | 
| 25 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 26 |  |  |  |  |  |  |  | 
| 27 |  |  |  |  |  |  | Controls IPC::Run debugging.  Debugging levels are now set by using words, | 
| 28 |  |  |  |  |  |  | but the numbers shown are still supported for backwards compatibility: | 
| 29 |  |  |  |  |  |  |  | 
| 30 |  |  |  |  |  |  | 0  none         disabled (special, see below) | 
| 31 |  |  |  |  |  |  | 1  basic        what's running | 
| 32 |  |  |  |  |  |  | 2  data         what's being sent/received | 
| 33 |  |  |  |  |  |  | 3  details      what's going on in more detail | 
| 34 |  |  |  |  |  |  | 4  gory         way too much detail for most uses | 
| 35 |  |  |  |  |  |  | 10 all          use this when submitting bug reports | 
| 36 |  |  |  |  |  |  | noopts       optimizations forbidden due to inherited STDIN | 
| 37 |  |  |  |  |  |  |  | 
| 38 |  |  |  |  |  |  | The C level is special when the environment variable IPCRUNDEBUG | 
| 39 |  |  |  |  |  |  | is set to this the first time IPC::Run::Debug is loaded: it prevents | 
| 40 |  |  |  |  |  |  | the debugging code from being compiled in to the remaining IPC::Run modules, | 
| 41 |  |  |  |  |  |  | saving a bit of cpu. | 
| 42 |  |  |  |  |  |  |  | 
| 43 |  |  |  |  |  |  | To do this in a script, here's a way that allows it to be overridden: | 
| 44 |  |  |  |  |  |  |  | 
| 45 |  |  |  |  |  |  | BEGIN { | 
| 46 |  |  |  |  |  |  | unless ( defined $ENV{IPCRUNDEBUG} ) { | 
| 47 |  |  |  |  |  |  | eval 'local $ENV{IPCRUNDEBUG} = "none"; require IPC::Run::Debug"' | 
| 48 |  |  |  |  |  |  | or die $@; | 
| 49 |  |  |  |  |  |  | } | 
| 50 |  |  |  |  |  |  | } | 
| 51 |  |  |  |  |  |  |  | 
| 52 |  |  |  |  |  |  | This should force IPC::Run to not be debuggable unless somebody sets | 
| 53 |  |  |  |  |  |  | the IPCRUNDEBUG flag; modify this formula to grep @ARGV if need be: | 
| 54 |  |  |  |  |  |  |  | 
| 55 |  |  |  |  |  |  | BEGIN { | 
| 56 |  |  |  |  |  |  | unless ( grep /^--debug/, @ARGV ) { | 
| 57 |  |  |  |  |  |  | eval 'local $ENV{IPCRUNDEBUG} = "none"; require IPC::Run::Debug"' | 
| 58 |  |  |  |  |  |  | or die $@; | 
| 59 |  |  |  |  |  |  | } | 
| 60 |  |  |  |  |  |  |  | 
| 61 |  |  |  |  |  |  | Both of those are untested. | 
| 62 |  |  |  |  |  |  |  | 
| 63 |  |  |  |  |  |  | =cut | 
| 64 |  |  |  |  |  |  |  | 
| 65 |  |  |  |  |  |  | ## We use @EXPORT for the end user's convenience: there's only one function | 
| 66 |  |  |  |  |  |  | ## exported, it's homonymous with the module, it's an unusual name, and | 
| 67 |  |  |  |  |  |  | ## it can be suppressed by "use IPC::Run ();". | 
| 68 |  |  |  |  |  |  |  | 
| 69 | 126 |  |  | 126 |  | 4051214 | use strict; | 
|  | 126 |  |  |  |  | 887 |  | 
|  | 126 |  |  |  |  | 3818 |  | 
| 70 | 126 |  |  | 126 |  | 623 | use warnings; | 
|  | 126 |  |  |  |  | 252 |  | 
|  | 126 |  |  |  |  | 3076 |  | 
| 71 | 126 |  |  | 126 |  | 728 | use Exporter; | 
|  | 126 |  |  |  |  | 180 |  | 
|  | 126 |  |  |  |  | 6401 |  | 
| 72 | 126 |  |  | 126 |  | 1200 | use vars qw{$VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS}; | 
|  | 126 |  |  |  |  | 324 |  | 
|  | 126 |  |  |  |  | 20121 |  | 
| 73 |  |  |  |  |  |  |  | 
| 74 |  |  |  |  |  |  | BEGIN { | 
| 75 | 126 |  |  | 126 |  | 663 | $VERSION = '20231003.0'; | 
| 76 | 126 |  |  |  |  | 2450 | @ISA     = qw( Exporter ); | 
| 77 | 126 |  |  |  |  | 602 | @EXPORT  = qw( | 
| 78 |  |  |  |  |  |  | _debug | 
| 79 |  |  |  |  |  |  | _debug_desc_fd | 
| 80 |  |  |  |  |  |  | _debugging | 
| 81 |  |  |  |  |  |  | _debugging_data | 
| 82 |  |  |  |  |  |  | _debugging_details | 
| 83 |  |  |  |  |  |  | _debugging_gory_details | 
| 84 |  |  |  |  |  |  | _debugging_not_optimized | 
| 85 |  |  |  |  |  |  | _set_child_debug_name | 
| 86 |  |  |  |  |  |  | ); | 
| 87 |  |  |  |  |  |  |  | 
| 88 | 126 |  |  |  |  | 283 | @EXPORT_OK = qw( | 
| 89 |  |  |  |  |  |  | _debug_init | 
| 90 |  |  |  |  |  |  | _debugging_level | 
| 91 |  |  |  |  |  |  | _map_fds | 
| 92 |  |  |  |  |  |  | ); | 
| 93 | 126 |  |  |  |  | 22975 | %EXPORT_TAGS = ( | 
| 94 |  |  |  |  |  |  | default => \@EXPORT, | 
| 95 |  |  |  |  |  |  | all => [ @EXPORT, @EXPORT_OK ], | 
| 96 |  |  |  |  |  |  | ); | 
| 97 |  |  |  |  |  |  | } | 
| 98 |  |  |  |  |  |  |  | 
| 99 |  |  |  |  |  |  | my $disable_debugging = defined $ENV{IPCRUNDEBUG} | 
| 100 |  |  |  |  |  |  | && ( !$ENV{IPCRUNDEBUG} | 
| 101 |  |  |  |  |  |  | || lc $ENV{IPCRUNDEBUG} eq "none" ); | 
| 102 |  |  |  |  |  |  |  | 
| 103 | 126 | 50 | 0 | 126 |  | 67335 | eval( $disable_debugging ? <<'STUBS' : <<'SUBS' ) or die $@; | 
|  | 126 | 0 | 0 | 126 |  | 913223 |  | 
|  | 126 | 0 | 0 | 126 |  | 10150 |  | 
|  | 126 | 0 | 0 | 126 |  | 950 |  | 
|  | 126 | 0 | 50 | 0 |  | 288 |  | 
|  | 126 | 0 | 50 | 0 |  | 17548 |  | 
|  | 126 | 0 | 50 | 0 |  | 959 |  | 
|  | 126 | 0 | 66 | 16225 |  | 253 |  | 
|  | 126 | 0 | 0 | 81629 |  | 38153 |  | 
|  | 126 | 0 | 0 | 4502 |  | 1033 |  | 
|  | 126 | 0 |  | 60902 |  | 297 |  | 
|  | 126 | 0 |  | 0 |  | 115783 |  | 
|  | 0 | 0 |  | 81629 |  | 0 |  | 
|  | 0 | 0 |  | 0 |  | 0 |  | 
|  | 0 | 0 |  | 145152 |  | 0 |  | 
|  | 0 | 50 |  | 2268 |  | 0 |  | 
|  | 0 | 100 |  | 0 |  | 0 |  | 
|  | 0 | 0 |  |  |  | 0 |  | 
|  | 0 | 0 |  |  |  | 0 |  | 
|  | 0 | 0 |  |  |  | 0 |  | 
|  | 0 | 50 |  |  |  | 0 |  | 
|  | 0 | 100 |  |  |  | 0 |  | 
|  | 0 | 50 |  |  |  | 0 |  | 
|  | 0 | 100 |  |  |  | 0 |  | 
|  | 0 | 100 |  |  |  | 0 |  | 
|  | 0 | 50 |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 16225 |  |  |  |  | 51228 |  | 
|  | 81629 |  |  |  |  | 218856 |  | 
|  | 81629 |  |  |  |  | 162064 |  | 
|  | 81629 |  |  |  |  | 395340 |  | 
|  | 4502 |  |  |  |  | 19397 |  | 
|  | 60902 |  |  |  |  | 218253 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 81629 |  |  |  |  | 130702 |  | 
|  | 81629 |  |  |  |  | 556448 |  | 
|  | 81629 |  |  |  |  | 205947 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 81629 |  |  |  |  | 169328 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 145152 |  |  |  |  | 253627 |  | 
|  | 145152 |  |  |  |  | 313135 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 145152 |  |  |  |  | 734817 |  | 
|  | 145152 |  |  |  |  | 344860 |  | 
|  | 145152 |  |  |  |  | 344507 |  | 
|  | 145152 |  |  |  |  | 419692 |  | 
|  | 2268 |  |  |  |  | 1613213 |  | 
|  | 2268 |  |  |  |  | 6734 |  | 
|  | 2268 |  |  |  |  | 11620 |  | 
|  | 2268 |  |  |  |  | 12277 |  | 
|  | 145152 |  |  |  |  | 241812 |  | 
|  | 145152 |  |  |  |  | 355038 |  | 
|  | 2268 |  |  |  |  | 22679 |  | 
|  | 2268 |  |  |  |  | 23371 |  | 
|  | 2268 |  |  |  |  | 24811 |  | 
|  | 0 |  |  |  |  |  |  | 
| 104 |  |  |  |  |  |  | sub _map_fds()                 { "" } | 
| 105 |  |  |  |  |  |  | sub _debug                     {} | 
| 106 |  |  |  |  |  |  | sub _debug_desc_fd             {} | 
| 107 |  |  |  |  |  |  | sub _debug_init                {} | 
| 108 |  |  |  |  |  |  | sub _set_child_debug_name      {} | 
| 109 |  |  |  |  |  |  | sub _debugging()               { 0 } | 
| 110 |  |  |  |  |  |  | sub _debugging_level()         { 0 } | 
| 111 |  |  |  |  |  |  | sub _debugging_data()          { 0 } | 
| 112 |  |  |  |  |  |  | sub _debugging_details()       { 0 } | 
| 113 |  |  |  |  |  |  | sub _debugging_gory_details()  { 0 } | 
| 114 |  |  |  |  |  |  | sub _debugging_not_optimized() { 0 } | 
| 115 |  |  |  |  |  |  |  | 
| 116 |  |  |  |  |  |  | 1; | 
| 117 |  |  |  |  |  |  | STUBS | 
| 118 |  |  |  |  |  |  |  | 
| 119 |  |  |  |  |  |  | use POSIX (); | 
| 120 |  |  |  |  |  |  | use constant Win32_MODE => $^O =~ /os2|Win32/i; | 
| 121 |  |  |  |  |  |  |  | 
| 122 |  |  |  |  |  |  | # Replace Win32API::File::INVALID_HANDLE_VALUE, which does not match the C ABI | 
| 123 |  |  |  |  |  |  | # on 64-bit builds (https://github.com/chorny/Win32API-File/issues/13). | 
| 124 |  |  |  |  |  |  | use constant C_ABI_INVALID_HANDLE_VALUE => length( pack 'P', undef ) == 4 | 
| 125 |  |  |  |  |  |  | ? 0xffffffff | 
| 126 |  |  |  |  |  |  | : 0xffffffff << 32 | 0xffffffff; | 
| 127 |  |  |  |  |  |  |  | 
| 128 |  |  |  |  |  |  | sub _fd_is_open { | 
| 129 |  |  |  |  |  |  | my ($fd) = @_; | 
| 130 |  |  |  |  |  |  | if (Win32_MODE) { | 
| 131 |  |  |  |  |  |  | # Many OS functions can crash on closed FDs.  POSIX::close() can hang on | 
| 132 |  |  |  |  |  |  | # the read end of a pipe (https://github.com/Perl/perl5/issues/19963). | 
| 133 |  |  |  |  |  |  | # Borrow Gnulib's strategy. | 
| 134 |  |  |  |  |  |  | require Win32API::File; | 
| 135 |  |  |  |  |  |  | return Win32API::File::FdGetOsFHandle($fd) != C_ABI_INVALID_HANDLE_VALUE; | 
| 136 |  |  |  |  |  |  | } | 
| 137 |  |  |  |  |  |  | else { | 
| 138 |  |  |  |  |  |  | ## I'd like a quicker way (less user, cpu & especially sys and kernel | 
| 139 |  |  |  |  |  |  | ## calls) to detect open file descriptors.  Let me know... | 
| 140 |  |  |  |  |  |  | ## Hmmm, could do a 0 length read and check for bad file descriptor... | 
| 141 |  |  |  |  |  |  | my $test_fd = POSIX::dup( $fd ); | 
| 142 |  |  |  |  |  |  | my $in_use = defined $test_fd; | 
| 143 |  |  |  |  |  |  | POSIX::close $test_fd if $in_use; | 
| 144 |  |  |  |  |  |  | return $in_use; | 
| 145 |  |  |  |  |  |  | } | 
| 146 |  |  |  |  |  |  | } | 
| 147 |  |  |  |  |  |  |  | 
| 148 |  |  |  |  |  |  | sub _map_fds { | 
| 149 |  |  |  |  |  |  | my $map = ''; | 
| 150 |  |  |  |  |  |  | my $digit = 0; | 
| 151 |  |  |  |  |  |  | my $dummy; | 
| 152 |  |  |  |  |  |  | for my $fd (0..63) { | 
| 153 |  |  |  |  |  |  | $map .= _fd_is_open($fd) ? $digit : '-'; | 
| 154 |  |  |  |  |  |  | $digit = 0 if ++$digit > 9; | 
| 155 |  |  |  |  |  |  | } | 
| 156 |  |  |  |  |  |  | warn "No fds open???" unless $map =~ /\d/; | 
| 157 |  |  |  |  |  |  | $map =~ s/(.{1,12})-*$/$1/; | 
| 158 |  |  |  |  |  |  | return $map; | 
| 159 |  |  |  |  |  |  | } | 
| 160 |  |  |  |  |  |  |  | 
| 161 |  |  |  |  |  |  | use vars qw( $parent_pid ); | 
| 162 |  |  |  |  |  |  |  | 
| 163 |  |  |  |  |  |  | $parent_pid = $$; | 
| 164 |  |  |  |  |  |  |  | 
| 165 |  |  |  |  |  |  | ## TODO: move debugging to its own module and make it compile-time | 
| 166 |  |  |  |  |  |  | ## optimizable. | 
| 167 |  |  |  |  |  |  |  | 
| 168 |  |  |  |  |  |  | ## Give kid process debugging nice names | 
| 169 |  |  |  |  |  |  | my $debug_name; | 
| 170 |  |  |  |  |  |  |  | 
| 171 |  |  |  |  |  |  | sub _set_child_debug_name { | 
| 172 |  |  |  |  |  |  | $debug_name = shift; | 
| 173 |  |  |  |  |  |  | } | 
| 174 |  |  |  |  |  |  |  | 
| 175 |  |  |  |  |  |  | ## There's a bit of hackery going on here. | 
| 176 |  |  |  |  |  |  | ## | 
| 177 |  |  |  |  |  |  | ## We want to have any code anywhere be able to emit | 
| 178 |  |  |  |  |  |  | ## debugging statements without knowing what harness the code is | 
| 179 |  |  |  |  |  |  | ## being called in/from, since we'd need to pass a harness around to | 
| 180 |  |  |  |  |  |  | ## everything. | 
| 181 |  |  |  |  |  |  | ## | 
| 182 |  |  |  |  |  |  | ## Thus, $cur_self was born. | 
| 183 |  |  |  |  |  |  | # | 
| 184 |  |  |  |  |  |  | my %debug_levels = ( | 
| 185 |  |  |  |  |  |  | none    => 0, | 
| 186 |  |  |  |  |  |  | basic   => 1, | 
| 187 |  |  |  |  |  |  | data    => 2, | 
| 188 |  |  |  |  |  |  | details => 3, | 
| 189 |  |  |  |  |  |  | gore           => 4, | 
| 190 |  |  |  |  |  |  | gory_details   => 4, | 
| 191 |  |  |  |  |  |  | "gory details" => 4, | 
| 192 |  |  |  |  |  |  | gory           => 4, | 
| 193 |  |  |  |  |  |  | gorydetails    => 4, | 
| 194 |  |  |  |  |  |  | all     => 10, | 
| 195 |  |  |  |  |  |  | notopt  => 0, | 
| 196 |  |  |  |  |  |  | ); | 
| 197 |  |  |  |  |  |  |  | 
| 198 |  |  |  |  |  |  | my $warned; | 
| 199 |  |  |  |  |  |  |  | 
| 200 |  |  |  |  |  |  | sub _debugging_level() { | 
| 201 |  |  |  |  |  |  | my $level = 0; | 
| 202 |  |  |  |  |  |  |  | 
| 203 |  |  |  |  |  |  | $level = $IPC::Run::cur_self->{debug} || 0 | 
| 204 |  |  |  |  |  |  | if $IPC::Run::cur_self | 
| 205 |  |  |  |  |  |  | && ( $IPC::Run::cur_self->{debug} || 0 ) >= $level; | 
| 206 |  |  |  |  |  |  |  | 
| 207 |  |  |  |  |  |  | if ( defined $ENV{IPCRUNDEBUG} ) { | 
| 208 |  |  |  |  |  |  | my $v = $ENV{IPCRUNDEBUG}; | 
| 209 |  |  |  |  |  |  | $v = $debug_levels{lc $v} if $v =~ /[a-zA-Z]/; | 
| 210 |  |  |  |  |  |  | unless ( defined $v ) { | 
| 211 |  |  |  |  |  |  | $warned ||= warn "Unknown debug level $ENV{IPCRUNDEBUG}, assuming 'basic' (1)\n"; | 
| 212 |  |  |  |  |  |  | $v = 1; | 
| 213 |  |  |  |  |  |  | } | 
| 214 |  |  |  |  |  |  | $level = $v if $v > $level; | 
| 215 |  |  |  |  |  |  | } | 
| 216 |  |  |  |  |  |  | return $level; | 
| 217 |  |  |  |  |  |  | } | 
| 218 |  |  |  |  |  |  |  | 
| 219 |  |  |  |  |  |  | sub _debugging_atleast($) { | 
| 220 |  |  |  |  |  |  | my $min_level = shift || 1; | 
| 221 |  |  |  |  |  |  |  | 
| 222 |  |  |  |  |  |  | my $level = _debugging_level; | 
| 223 |  |  |  |  |  |  |  | 
| 224 |  |  |  |  |  |  | return $level >= $min_level ? $level : 0; | 
| 225 |  |  |  |  |  |  | } | 
| 226 |  |  |  |  |  |  |  | 
| 227 |  |  |  |  |  |  | sub _debugging()               { _debugging_atleast 1 } | 
| 228 |  |  |  |  |  |  | sub _debugging_data()          { _debugging_atleast 2 } | 
| 229 |  |  |  |  |  |  | sub _debugging_details()       { _debugging_atleast 3 } | 
| 230 |  |  |  |  |  |  | sub _debugging_gory_details()  { _debugging_atleast 4 } | 
| 231 |  |  |  |  |  |  | sub _debugging_not_optimized() { ( $ENV{IPCRUNDEBUG} || "" ) eq "notopt" } | 
| 232 |  |  |  |  |  |  |  | 
| 233 |  |  |  |  |  |  | sub _debug_init { | 
| 234 |  |  |  |  |  |  | ## This routine is called only in spawned children to fake out the | 
| 235 |  |  |  |  |  |  | ## debug routines so they'll emit debugging info. | 
| 236 |  |  |  |  |  |  | $IPC::Run::cur_self = {}; | 
| 237 |  |  |  |  |  |  | (  $parent_pid, | 
| 238 |  |  |  |  |  |  | $^T, | 
| 239 |  |  |  |  |  |  | $IPC::Run::cur_self->{debug}, | 
| 240 |  |  |  |  |  |  | $IPC::Run::cur_self->{DEBUG_FD}, | 
| 241 |  |  |  |  |  |  | $debug_name | 
| 242 |  |  |  |  |  |  | ) = @_; | 
| 243 |  |  |  |  |  |  | } | 
| 244 |  |  |  |  |  |  |  | 
| 245 |  |  |  |  |  |  |  | 
| 246 |  |  |  |  |  |  | sub _debug { | 
| 247 |  |  |  |  |  |  | #   return unless _debugging || _debugging_not_optimized; | 
| 248 |  |  |  |  |  |  |  | 
| 249 |  |  |  |  |  |  | my $fd = defined &IPC::Run::_debug_fd | 
| 250 |  |  |  |  |  |  | ? IPC::Run::_debug_fd() | 
| 251 |  |  |  |  |  |  | : fileno STDERR; | 
| 252 |  |  |  |  |  |  |  | 
| 253 |  |  |  |  |  |  | my $s; | 
| 254 |  |  |  |  |  |  | my $debug_id; | 
| 255 |  |  |  |  |  |  | $debug_id = join( | 
| 256 |  |  |  |  |  |  | " ", | 
| 257 |  |  |  |  |  |  | join( | 
| 258 |  |  |  |  |  |  | "", | 
| 259 |  |  |  |  |  |  | defined $IPC::Run::cur_self && defined $IPC::Run::cur_self->{ID} | 
| 260 |  |  |  |  |  |  | ? "#$IPC::Run::cur_self->{ID}" | 
| 261 |  |  |  |  |  |  | : (), | 
| 262 |  |  |  |  |  |  | "($$)", | 
| 263 |  |  |  |  |  |  | ), | 
| 264 |  |  |  |  |  |  | defined $debug_name && length $debug_name ? $debug_name        : (), | 
| 265 |  |  |  |  |  |  | ); | 
| 266 |  |  |  |  |  |  | my $prefix = join( | 
| 267 |  |  |  |  |  |  | "", | 
| 268 |  |  |  |  |  |  | "IPC::Run", | 
| 269 |  |  |  |  |  |  | sprintf( " %04d", time - $^T ), | 
| 270 |  |  |  |  |  |  | ( _debugging_details ? ( " ", _map_fds ) : () ), | 
| 271 |  |  |  |  |  |  | length $debug_id ? ( " [", $debug_id, "]" ) : (), | 
| 272 |  |  |  |  |  |  | ": ", | 
| 273 |  |  |  |  |  |  | ); | 
| 274 |  |  |  |  |  |  |  | 
| 275 |  |  |  |  |  |  | my $msg = join( '', map defined $_ ? $_ : "", @_ ); | 
| 276 |  |  |  |  |  |  | chomp $msg; | 
| 277 |  |  |  |  |  |  | $msg =~ s{^}{$prefix}gm; | 
| 278 |  |  |  |  |  |  | $msg .= "\n"; | 
| 279 |  |  |  |  |  |  | POSIX::write( $fd, $msg, length $msg ); | 
| 280 |  |  |  |  |  |  | } | 
| 281 |  |  |  |  |  |  |  | 
| 282 |  |  |  |  |  |  |  | 
| 283 |  |  |  |  |  |  | my @fd_descs = ( 'stdin', 'stdout', 'stderr' ); | 
| 284 |  |  |  |  |  |  |  | 
| 285 |  |  |  |  |  |  | sub _debug_desc_fd { | 
| 286 |  |  |  |  |  |  | return unless _debugging; | 
| 287 |  |  |  |  |  |  | my $text = shift; | 
| 288 |  |  |  |  |  |  | my $op = pop; | 
| 289 |  |  |  |  |  |  | my $kid = $_[0]; | 
| 290 |  |  |  |  |  |  |  | 
| 291 |  |  |  |  |  |  | Carp::carp join " ", caller(0), $text, $op  if defined $op  && UNIVERSAL::isa( $op, "IO::Pty" ); | 
| 292 |  |  |  |  |  |  |  | 
| 293 |  |  |  |  |  |  | _debug( | 
| 294 |  |  |  |  |  |  | $text, | 
| 295 |  |  |  |  |  |  | ' ', | 
| 296 |  |  |  |  |  |  | ( defined $op->{FD} | 
| 297 |  |  |  |  |  |  | ? $op->{FD} < 3 | 
| 298 |  |  |  |  |  |  | ? ( $fd_descs[$op->{FD}] ) | 
| 299 |  |  |  |  |  |  | : ( 'fd ', $op->{FD} ) | 
| 300 |  |  |  |  |  |  | : $op->{FD} | 
| 301 |  |  |  |  |  |  | ), | 
| 302 |  |  |  |  |  |  | ( defined $op->{KFD} | 
| 303 |  |  |  |  |  |  | ? ( | 
| 304 |  |  |  |  |  |  | ' (kid', | 
| 305 |  |  |  |  |  |  | ( defined $kid ? ( ' ', $kid->{NUM}, ) : () ), | 
| 306 |  |  |  |  |  |  | "'s ", | 
| 307 |  |  |  |  |  |  | ( $op->{KFD} < 3 | 
| 308 |  |  |  |  |  |  | ? $fd_descs[$op->{KFD}] | 
| 309 |  |  |  |  |  |  | : defined $kid | 
| 310 |  |  |  |  |  |  | && defined $kid->{DEBUG_FD} | 
| 311 |  |  |  |  |  |  | && $op->{KFD} == $kid->{DEBUG_FD} | 
| 312 |  |  |  |  |  |  | ? ( 'debug (', $op->{KFD}, ')' ) | 
| 313 |  |  |  |  |  |  | : ( 'fd ', $op->{KFD} ) | 
| 314 |  |  |  |  |  |  | ), | 
| 315 |  |  |  |  |  |  | ')', | 
| 316 |  |  |  |  |  |  | ) | 
| 317 |  |  |  |  |  |  | : () | 
| 318 |  |  |  |  |  |  | ), | 
| 319 |  |  |  |  |  |  | ); | 
| 320 |  |  |  |  |  |  | } | 
| 321 |  |  |  |  |  |  |  | 
| 322 |  |  |  |  |  |  | 1; | 
| 323 |  |  |  |  |  |  |  | 
| 324 |  |  |  |  |  |  | SUBS | 
| 325 |  |  |  |  |  |  |  | 
| 326 |  |  |  |  |  |  | =pod | 
| 327 |  |  |  |  |  |  |  | 
| 328 |  |  |  |  |  |  | =head1 AUTHOR | 
| 329 |  |  |  |  |  |  |  | 
| 330 |  |  |  |  |  |  | Barrie Slaymaker , with numerous suggestions by p5p. | 
| 331 |  |  |  |  |  |  |  | 
| 332 |  |  |  |  |  |  | =cut |