File Coverage

blib/lib/IPC/Run/Debug.pm
Criterion Covered Total %
statement 54 82 65.8
branch 13 52 25.0
condition 5 26 19.2
subroutine 16 22 72.7
pod n/a
total 88 182 48.3


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