File Coverage

blib/lib/IPC/Run/Debug.pm
Criterion Covered Total %
statement 42 68 61.7
branch 11 48 22.9
condition 5 23 21.7
subroutine 12 18 66.6
pod n/a
total 70 157 44.5


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 117     117   3698208 use strict;
  117         703  
  117         3477  
70 117     117   575 use Exporter;
  117         523  
  117         4611  
71 117     117   597 use vars qw{$VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS};
  117         614  
  117         16857  
72              
73             BEGIN {
74 117     117   477 $VERSION = '20200505.0';
75 117         1924 @ISA = qw( Exporter );
76 117         900 @EXPORT = qw(
77             _debug
78             _debug_desc_fd
79             _debugging
80             _debugging_data
81             _debugging_details
82             _debugging_gory_details
83             _debugging_not_optimized
84             _set_child_debug_name
85             );
86              
87 117         406 @EXPORT_OK = qw(
88             _debug_init
89             _debugging_level
90             _map_fds
91             );
92 117         17553 %EXPORT_TAGS = (
93             default => \@EXPORT,
94             all => [ @EXPORT, @EXPORT_OK ],
95             );
96             }
97              
98             my $disable_debugging = defined $ENV{IPCRUNDEBUG}
99             && ( !$ENV{IPCRUNDEBUG}
100             || lc $ENV{IPCRUNDEBUG} eq "none" );
101              
102 117 0 0 117   61736 eval( $disable_debugging ? <<'STUBS' : <<'SUBS' ) or die $@;
  117 0 0 117   800092  
  117 0 0 0   18388  
  117 0 50 0   862  
  117 0 50 0   236  
  117 0 50 14562   102077  
  0 0 66 73930   0  
  0 0 0 3990   0  
  0 0 0 55378   0  
  0 0   0   0  
  0 0   73930   0  
  0 0   0   0  
  0 0   2208   0  
  0 0   0   0  
  0 50       0  
  0 100       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 50       0  
  0 100       0  
  0 100       0  
  0 100       0  
  14562 50       43841  
  73930         201464  
  73930         150891  
  73930         306259  
  3990         16897  
  55378         184123  
  0         0  
  73930         107387  
  73930         497641  
  73930         165830  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  73930         127596  
  0         0  
  2208         1445668  
  2208         6498  
  2208         17568  
  2208         12927  
  141312         678295  
  141312         329126  
  141312         323950  
  141312         289093  
  141312         291642  
  2208         22391  
  2208         27438  
  2208         26313  
  0            
103             sub _map_fds() { "" }
104             sub _debug {}
105             sub _debug_desc_fd {}
106             sub _debug_init {}
107             sub _set_child_debug_name {}
108             sub _debugging() { 0 }
109             sub _debugging_level() { 0 }
110             sub _debugging_data() { 0 }
111             sub _debugging_details() { 0 }
112             sub _debugging_gory_details() { 0 }
113             sub _debugging_not_optimized() { 0 }
114              
115             1;
116             STUBS
117              
118             use POSIX ();
119              
120             sub _map_fds {
121             my $map = '';
122             my $digit = 0;
123             my $in_use;
124             my $dummy;
125             for my $fd (0..63) {
126             ## I'd like a quicker way (less user, cpu & especially sys and kernel
127             ## calls) to detect open file descriptors. Let me know...
128             ## Hmmm, could do a 0 length read and check for bad file descriptor...
129             ## but that segfaults on Win32
130             my $test_fd = POSIX::dup( $fd );
131             $in_use = defined $test_fd;
132             POSIX::close $test_fd if $in_use;
133             $map .= $in_use ? $digit : '-';
134             $digit = 0 if ++$digit > 9;
135             }
136             warn "No fds open???" unless $map =~ /\d/;
137             $map =~ s/(.{1,12})-*$/$1/;
138             return $map;
139             }
140              
141             use vars qw( $parent_pid );
142              
143             $parent_pid = $$;
144              
145             ## TODO: move debugging to its own module and make it compile-time
146             ## optimizable.
147              
148             ## Give kid process debugging nice names
149             my $debug_name;
150              
151             sub _set_child_debug_name {
152             $debug_name = shift;
153             }
154              
155             ## There's a bit of hackery going on here.
156             ##
157             ## We want to have any code anywhere be able to emit
158             ## debugging statements without knowing what harness the code is
159             ## being called in/from, since we'd need to pass a harness around to
160             ## everything.
161             ##
162             ## Thus, $cur_self was born.
163             #
164             my %debug_levels = (
165             none => 0,
166             basic => 1,
167             data => 2,
168             details => 3,
169             gore => 4,
170             gory_details => 4,
171             "gory details" => 4,
172             gory => 4,
173             gorydetails => 4,
174             all => 10,
175             notopt => 0,
176             );
177              
178             my $warned;
179              
180             sub _debugging_level() {
181             my $level = 0;
182              
183             $level = $IPC::Run::cur_self->{debug} || 0
184             if $IPC::Run::cur_self
185             && ( $IPC::Run::cur_self->{debug} || 0 ) >= $level;
186              
187             if ( defined $ENV{IPCRUNDEBUG} ) {
188             my $v = $ENV{IPCRUNDEBUG};
189             $v = $debug_levels{lc $v} if $v =~ /[a-zA-Z]/;
190             unless ( defined $v ) {
191             $warned ||= warn "Unknown debug level $ENV{IPCRUNDEBUG}, assuming 'basic' (1)\n";
192             $v = 1;
193             }
194             $level = $v if $v > $level;
195             }
196             return $level;
197             }
198              
199             sub _debugging_atleast($) {
200             my $min_level = shift || 1;
201              
202             my $level = _debugging_level;
203            
204             return $level >= $min_level ? $level : 0;
205             }
206              
207             sub _debugging() { _debugging_atleast 1 }
208             sub _debugging_data() { _debugging_atleast 2 }
209             sub _debugging_details() { _debugging_atleast 3 }
210             sub _debugging_gory_details() { _debugging_atleast 4 }
211             sub _debugging_not_optimized() { ( $ENV{IPCRUNDEBUG} || "" ) eq "notopt" }
212              
213             sub _debug_init {
214             ## This routine is called only in spawned children to fake out the
215             ## debug routines so they'll emit debugging info.
216             $IPC::Run::cur_self = {};
217             ( $parent_pid,
218             $^T,
219             $IPC::Run::cur_self->{debug},
220             $IPC::Run::cur_self->{DEBUG_FD},
221             $debug_name
222             ) = @_;
223             }
224              
225              
226             sub _debug {
227             # return unless _debugging || _debugging_not_optimized;
228              
229             my $fd = defined &IPC::Run::_debug_fd
230             ? IPC::Run::_debug_fd()
231             : fileno STDERR;
232              
233             my $s;
234             my $debug_id;
235             $debug_id = join(
236             " ",
237             join(
238             "",
239             defined $IPC::Run::cur_self ? "#$IPC::Run::cur_self->{ID}" : (),
240             "($$)",
241             ),
242             defined $debug_name && length $debug_name ? $debug_name : (),
243             );
244             my $prefix = join(
245             "",
246             "IPC::Run",
247             sprintf( " %04d", time - $^T ),
248             ( _debugging_details ? ( " ", _map_fds ) : () ),
249             length $debug_id ? ( " [", $debug_id, "]" ) : (),
250             ": ",
251             );
252              
253             my $msg = join( '', map defined $_ ? $_ : "", @_ );
254             chomp $msg;
255             $msg =~ s{^}{$prefix}gm;
256             $msg .= "\n";
257             POSIX::write( $fd, $msg, length $msg );
258             }
259              
260              
261             my @fd_descs = ( 'stdin', 'stdout', 'stderr' );
262              
263             sub _debug_desc_fd {
264             return unless _debugging;
265             my $text = shift;
266             my $op = pop;
267             my $kid = $_[0];
268              
269             Carp::carp join " ", caller(0), $text, $op if defined $op && UNIVERSAL::isa( $op, "IO::Pty" );
270              
271             _debug(
272             $text,
273             ' ',
274             ( defined $op->{FD}
275             ? $op->{FD} < 3
276             ? ( $fd_descs[$op->{FD}] )
277             : ( 'fd ', $op->{FD} )
278             : $op->{FD}
279             ),
280             ( defined $op->{KFD}
281             ? (
282             ' (kid',
283             ( defined $kid ? ( ' ', $kid->{NUM}, ) : () ),
284             "'s ",
285             ( $op->{KFD} < 3
286             ? $fd_descs[$op->{KFD}]
287             : defined $kid
288             && defined $kid->{DEBUG_FD}
289             && $op->{KFD} == $kid->{DEBUG_FD}
290             ? ( 'debug (', $op->{KFD}, ')' )
291             : ( 'fd ', $op->{KFD} )
292             ),
293             ')',
294             )
295             : ()
296             ),
297             );
298             }
299              
300             1;
301              
302             SUBS
303              
304             =pod
305              
306             =head1 AUTHOR
307              
308             Barrie Slaymaker , with numerous suggestions by p5p.
309              
310             =cut