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              
62             Both of those are untested.
63              
64             =cut
65              
66             ## We use @EXPORT for the end user's convenience: there's only one function
67             ## exported, it's homonymous with the module, it's an unusual name, and
68             ## it can be suppressed by "use IPC::Run ();".
69              
70 231     231   6842299 use strict;
  231         355  
  231         6290  
71 231     231   755 use warnings;
  231         321  
  231         7425  
72 231     231   838 use Exporter;
  231         340  
  231         10003  
73 231     231   1144 use vars qw{$VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS};
  231         389  
  231         28422  
74              
75             BEGIN {
76 231     231   678 $VERSION = '20260402.0';
77 231         3914 @ISA = qw( Exporter );
78 231         1040 @EXPORT = qw(
79             _debug
80             _debug_desc_fd
81             _debugging
82             _debugging_data
83             _debugging_details
84             _debugging_gory_details
85             _debugging_not_optimized
86             _set_child_debug_name
87             );
88              
89 231         704 @EXPORT_OK = qw(
90             _debug_init
91             _debugging_level
92             _map_fds
93             );
94 231         33993 %EXPORT_TAGS = (
95             default => \@EXPORT,
96             all => [ @EXPORT, @EXPORT_OK ],
97             );
98             }
99              
100             my $disable_debugging = defined $ENV{IPCRUNDEBUG}
101             && ( !$ENV{IPCRUNDEBUG}
102             || lc $ENV{IPCRUNDEBUG} eq "none" );
103              
104 231 50 0 231   97163 eval( $disable_debugging ? <<'STUBS' : <<'SUBS' ) or die $@;
  231 0 0 231   1453828  
  231 0 0 231   17688  
  231 0 0 231   1361  
  231 0 50 0   398  
  231 0 50 0   22848  
  231 0 50 0   972  
  231 0 66 28663   317  
  231 0 0 146884   57485  
  231 0 0 7659   1330  
  231 0   110562   341  
  231 0   0   170361  
  0 0   146884   0  
  0 0   0   0  
  0 0   180736   0  
  0 50   2824   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  
  28663         61832  
  146884         346643  
  146884         251449  
  146884         499629  
  7659         30914  
  110562         330886  
  0         0  
  146884         196757  
  146884         924423  
  146884         310432  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  146884         230846  
  0         0  
  180736         204994  
  180736         256192  
  0         0  
  0         0  
  180736         398719  
  180736         199352  
  180736         284740  
  180736         290117  
  2824         10282362  
  2824         7990  
  2824         8306  
  2824         11105  
  180736         228153  
  180736         292927  
  2824         25356  
  2824         22296  
  2824         25198  
  0            
105             sub _map_fds() { "" }
106             sub _debug {}
107             sub _debug_desc_fd {}
108             sub _debug_init {}
109             sub _set_child_debug_name {}
110             sub _debugging() { 0 }
111             sub _debugging_level() { 0 }
112             sub _debugging_data() { 0 }
113             sub _debugging_details() { 0 }
114             sub _debugging_gory_details() { 0 }
115             sub _debugging_not_optimized() { 0 }
116              
117             1;
118             STUBS
119              
120             use POSIX ();
121             use constant Win32_MODE => $^O =~ /os2|Win32/i;
122              
123             # Replace Win32API::File::INVALID_HANDLE_VALUE, which does not match the C ABI
124             # on 64-bit builds (https://github.com/chorny/Win32API-File/issues/13).
125             use constant C_ABI_INVALID_HANDLE_VALUE => length( pack 'P', undef ) == 4
126             ? 0xffffffff
127             : 0xffffffff << 32 | 0xffffffff;
128              
129             sub _fd_is_open {
130             my ($fd) = @_;
131             if (Win32_MODE) {
132             # Many OS functions can crash on closed FDs. POSIX::close() can hang on
133             # the read end of a pipe (https://github.com/Perl/perl5/issues/19963).
134             # Borrow Gnulib's strategy.
135             require Win32API::File;
136             return Win32API::File::FdGetOsFHandle($fd) != C_ABI_INVALID_HANDLE_VALUE;
137             }
138             else {
139             ## I'd like a quicker way (less user, cpu & especially sys and kernel
140             ## calls) to detect open file descriptors. Let me know...
141             ## Hmmm, could do a 0 length read and check for bad file descriptor...
142             my $test_fd = POSIX::dup( $fd );
143             my $in_use = defined $test_fd;
144             POSIX::close $test_fd if $in_use;
145             return $in_use;
146             }
147             }
148              
149             sub _map_fds {
150             my $map = '';
151             my $digit = 0;
152             my $dummy;
153             for my $fd (0..63) {
154             $map .= _fd_is_open($fd) ? $digit : '-';
155             $digit = 0 if ++$digit > 9;
156             }
157             warn "No fds open???" unless $map =~ /\d/;
158             $map =~ s/(.{1,12})-*$/$1/;
159             return $map;
160             }
161              
162             use vars qw( $parent_pid );
163              
164             $parent_pid = $$;
165              
166             ## TODO: move debugging to its own module and make it compile-time
167             ## optimizable.
168              
169             ## Give kid process debugging nice names
170             my $debug_name;
171              
172             sub _set_child_debug_name {
173             $debug_name = shift;
174             }
175              
176             ## There's a bit of hackery going on here.
177             ##
178             ## We want to have any code anywhere be able to emit
179             ## debugging statements without knowing what harness the code is
180             ## being called in/from, since we'd need to pass a harness around to
181             ## everything.
182             ##
183             ## Thus, $cur_self was born.
184             #
185             my %debug_levels = (
186             none => 0,
187             basic => 1,
188             data => 2,
189             details => 3,
190             gore => 4,
191             gory_details => 4,
192             "gory details" => 4,
193             gory => 4,
194             gorydetails => 4,
195             all => 10,
196             notopt => 0,
197             );
198              
199             my $warned;
200              
201             sub _debugging_level() {
202             my $level = 0;
203              
204             $level = $IPC::Run::cur_self->{debug} || 0
205             if $IPC::Run::cur_self
206             && ( $IPC::Run::cur_self->{debug} || 0 ) >= $level;
207              
208             if ( defined $ENV{IPCRUNDEBUG} ) {
209             my $v = $ENV{IPCRUNDEBUG};
210             $v = $debug_levels{lc $v} if $v =~ /[a-zA-Z]/;
211             unless ( defined $v ) {
212             $warned ||= warn "Unknown debug level $ENV{IPCRUNDEBUG}, assuming 'basic' (1)\n";
213             $v = 1;
214             }
215             $level = $v if $v > $level;
216             }
217             return $level;
218             }
219              
220             sub _debugging_atleast($) {
221             my $min_level = shift || 1;
222              
223             my $level = _debugging_level;
224            
225             return $level >= $min_level ? $level : 0;
226             }
227              
228             sub _debugging() { _debugging_atleast 1 }
229             sub _debugging_data() { _debugging_atleast 2 }
230             sub _debugging_details() { _debugging_atleast 3 }
231             sub _debugging_gory_details() { _debugging_atleast 4 }
232             sub _debugging_not_optimized() { ( $ENV{IPCRUNDEBUG} || "" ) eq "notopt" }
233              
234             sub _debug_init {
235             ## This routine is called only in spawned children to fake out the
236             ## debug routines so they'll emit debugging info.
237             $IPC::Run::cur_self = {};
238             ( $parent_pid,
239             $^T,
240             $IPC::Run::cur_self->{debug},
241             $IPC::Run::cur_self->{DEBUG_FD},
242             $debug_name
243             ) = @_;
244             }
245              
246              
247             sub _debug {
248             # return unless _debugging || _debugging_not_optimized;
249              
250             my $fd = defined &IPC::Run::_debug_fd
251             ? IPC::Run::_debug_fd()
252             : fileno STDERR;
253              
254             my $s;
255             my $debug_id;
256             $debug_id = join(
257             " ",
258             join(
259             "",
260             defined $IPC::Run::cur_self && defined $IPC::Run::cur_self->{ID}
261             ? "#$IPC::Run::cur_self->{ID}"
262             : (),
263             "($$)",
264             ),
265             defined $debug_name && length $debug_name ? $debug_name : (),
266             );
267             my $prefix = join(
268             "",
269             "IPC::Run",
270             sprintf( " %04d", time - $^T ),
271             ( _debugging_details ? ( " ", _map_fds ) : () ),
272             length $debug_id ? ( " [", $debug_id, "]" ) : (),
273             ": ",
274             );
275              
276             my $msg = join( '', map defined $_ ? $_ : "", @_ );
277             chomp $msg;
278             $msg =~ s{^}{$prefix}gm;
279             $msg .= "\n";
280             POSIX::write( $fd, $msg, length $msg );
281             }
282              
283              
284             my @fd_descs = ( 'stdin', 'stdout', 'stderr' );
285              
286             sub _debug_desc_fd {
287             return unless _debugging;
288             my $text = shift;
289             my $op = pop;
290             my $kid = $_[0];
291              
292             Carp::carp join " ", caller(0), $text, $op if defined $op && UNIVERSAL::isa( $op, "IO::Pty" );
293              
294             _debug(
295             $text,
296             ' ',
297             ( defined $op->{FD}
298             ? $op->{FD} < 3
299             ? ( $fd_descs[$op->{FD}] )
300             : ( 'fd ', $op->{FD} )
301             : $op->{FD}
302             ),
303             ( defined $op->{KFD}
304             ? (
305             ' (kid',
306             ( defined $kid ? ( ' ', $kid->{NUM}, ) : () ),
307             "'s ",
308             ( $op->{KFD} < 3
309             ? $fd_descs[$op->{KFD}]
310             : defined $kid
311             && defined $kid->{DEBUG_FD}
312             && $op->{KFD} == $kid->{DEBUG_FD}
313             ? ( 'debug (', $op->{KFD}, ')' )
314             : ( 'fd ', $op->{KFD} )
315             ),
316             ')',
317             )
318             : ()
319             ),
320             );
321             }
322              
323             1;
324              
325             SUBS
326              
327             =pod
328              
329             =head1 AUTHOR
330              
331             Barrie Slaymaker , with numerous suggestions by p5p.
332              
333             =cut