———————package
VCP::Debug ;
=head1 NAME
VCP::Debug - debugging support for VCP
=head1 SYNOPSIS
=head1 DESCRIPTION
=head1 EXPORTS
The following functions may be exported: L</debug>, L</enable_debug>,
L</debugging>
L</disable_debug>, along with the tags ':all' and ':debug'. Use the latter
to head off future namespace pollution in case :all gets expanded in the
future..
A warning will be emitted on program exit for any specs that aren't used,
to help you make sure that you are using sensible specs.
=over
=cut
use
strict ;
use
Exporter ;
@ISA
=
qw( Exporter )
;
@EXPORT_OK
=
qw(
debug
enable_debug
disable_debug
debugging
explicitly_debugging
)
;
%EXPORT_TAGS
= (
'all'
=> \
@EXPORT_OK
,
'debug'
=> \
@EXPORT_OK
,
) ;
$VERSION
= 0.1 ;
# TODO:
#=item use
#=item import
#
#In addition to all of the routines and tags that C<use> and C<import> normally
#take (see above), you may also pass in pairwise debugging definitions like
#so:
#
# use VCP::debug (
# ":all",
# DEBUGGING_FOO => "foo,bar",
# ) ;
#
#Any all caps export import requests are created as subroutines that may well be
#optimized away at compile time if "enable_debugging" has not been called. This
#requires a conspiracy between the author of a module and the author of the main
#program to call enable_debugging I<before> C<use>ing any modules that leverage
#this feature, otherwise compile-time optimizations won't occur.
#
=item debug
debug $foo if debugging $self ;
Emits a line of debugging (a "\n" will be appended). Use debug_some
to avoid the "\n". Any undefined parameters will be displayed as
C<E<lt>undefE<gt>>.
=cut
my
$dump_undebugged
;
my
$reported_specs
;
my
@debug_specs
;
my
%used_specs
;
my
%debugging
;
END {
$used_specs
{
'##NEVER_MATCH##'
} = 1 ;
my
@unused
=
grep
!
$used_specs
{
$_
},
@debug_specs
;
warn
"vcp: Unused debug specs: "
,
join
(
', '
,
map
"/$_/"
,
@unused
),
"\n"
if
@unused
;
if
(
@unused
||
$dump_undebugged
) {
my
@undebugged
=
grep
{
my
$name
=
$_
;
!
grep
$name
=~ /
$_
/i,
keys
%used_specs
}
map
lc
$_
,
sort
keys
%debugging
;
if
(
@undebugged
) {
warn
"vcp: Undebugged things: "
,
join
(
', '
,
@undebugged
),
"\n"
;
}
else
{
warn
"vcp: No undebugged things\n"
;
}
}
}
sub
debug {
return
unless
@debug_specs
;
if
(
@_
) {
my
$t
=
join
(
''
,
map
defined
$_
?
$_
:
"<undef>"
,
@_
) ;
if
(
length
$t
) {
STDERR
$t
,
substr
(
$t
, -1 ) eq
"\n"
? () :
"\n"
;
}
}
}
sub
debug_some {
return
unless
@debug_specs
;
STDERR
map
defined
$_
?
$_
:
"<undef>"
,
@_
if
@_
;
}
=item debugging
debug "blah" if debugging ;
Returns TRUE if the caller's module is being debugged
debug "blah" if debugging $self ;
debug "blah" if debugging $other, $self ; ## ORs the arguments together
Returns TRUE if any of the arguments are being debugged. Plain
strings can be passed or blessed references.
=cut
sub
_report_specs {
my
@report
=
grep
! /
##NEVER_MATCH##/, @debug_specs ;
STDERR
"Debugging "
,
join
(
', '
,
map
"/$_/"
,
@report
),
"\n"
if
@report
;
$reported_specs
= 1 ;
}
sub
debugging {
return
undef
unless
@debug_specs
;
my
$result
;
my
@missed
;
for
my
$where
(
@_
?
map
ref
$_
||
$_
,
@_
:
scalar
caller
) {
if
( !
exists
$debugging
{
$where
} ) {
# print STDERR "missed $where\n" ;
## If this is the first miss, then these may not have been reported.
_report_specs
unless
$reported_specs
;
## We go ahead and evaluate all specs instead of returning when the
## first is found so that we can set $used_specs for all specs that
## match.
$debugging
{
$where
} = 0 ;
for
my
$spec
(
@debug_specs
) {
next
if
$spec
eq
'##NEVER_MATCH##'
;
# print STDERR " /$spec/:\n" ;
if
(
$where
=~ /
$spec
/i ) {
$debugging
{
$where
} = 1 ;
$used_specs
{
$spec
} = 1 ;
$result
= 1 ;
## no last: we want to build up %used_specs. There
## aren't usually many specs anyway.
}
else
{
# print STDERR " ! /$spec/\n" ;
}
}
}
# print STDERR "$where ", $debugging{$where} ? 'yes' : 'no', "\n" ;
return
1
if
$debugging
{
$where
} ;
}
return
$result
;
}
=item explicitly_debugging
debug "blah" if explicitly_debugging ;
Returns TRUE if the caller's module is being debugged by a literal match
instead of a pattern match. This is used when debugging output would normally
be congested with too much crap from a particular subsystem when using a
wildcard debug spec (like ".*"), but you want the ability to turn on debugging
for that subsystem:
debug "blah" if explicitly_debugging "VCP::Dest::sort" ;
requires an explicit C<VCP::Dest::sort> to be given in the debug specs.
debug "blah" if explicitly_debugging $self ;
debug "blah" if explicitly_debugging $other, $self ; ## ORs the args
Returns TRUE if any of the arguments are being debugged. Plain
strings can be passed or blessed references.
=cut
my
%explicitly_debugging
;
sub
explicitly_debugging {
return
undef
unless
@debug_specs
;
my
$result
;
my
@missed
;
for
my
$where
(
@_
?
map
ref
$_
||
$_
,
@_
:
scalar
caller
) {
if
( !
exists
$explicitly_debugging
{
$where
} ) {
# print STDERR "missed $where\n" ;
## If this is the first miss, then these may not have been reported.
_report_specs
unless
$reported_specs
;
## We go ahead and evaluate all specs instead of returning when the
## first is found so that we can set $used_specs for all specs that
## match.
$explicitly_debugging
{
$where
} = 0 ;
for
my
$spec
(
@debug_specs
) {
next
if
$spec
eq
'##NEVER_MATCH##'
;
# print STDERR " /$spec/:\n" ;
if
(
lc
$where
eq
lc
$spec
) {
$explicitly_debugging
{
$where
} = 1 ;
$used_specs
{
$spec
} = 1 ;
$result
= 1 ;
## no last: we want to build up %used_specs. There
## aren't usually many specs anyway.
}
else
{
# print STDERR " ! /$spec/\n" ;
}
}
}
# print STDERR "$where ", $debugging{$where} ? 'yes' : 'no', "\n" ;
return
1
if
$explicitly_debugging
{
$where
} ;
}
return
$result
;
}
=item disable_debug
Disable all debugging.
=cut
sub
disable_debug() {
@debug_specs
= () ;
return
;
}
=item enable_debug
enable_debug ;
enable_debug( ...debug specs... ) ;
A debug spec is a regular expression that matches the name of a module.
=cut
sub
enable_debug {
my
%specs
=
map
{ (
$_
=> 1 ) }
@debug_specs
,
@_
;
my
@new_debug_specs
=
%specs
?
keys
%specs
:
qr/^/
;
_report_specs
if
$reported_specs
&&
@debug_specs
!=
@new_debug_specs
;
@debug_specs
=
map
(
/^what$/i && (
$dump_undebugged
= 1 ) ?
'##NEVER_MATCH##'
:
$_
,
@new_debug_specs
) ;
return
;
}
=head1 COPYRIGHT
Copyright 2000, Perforce Software, Inc. All Rights Reserved.
This module and the VCP package are licensed according to the terms given in
the file LICENSE accompanying this distribution, a copy of which is included in
L<vcp>.
=head1 AUTHOR
Barrie Slaymaker <barries@slaysys.com>
=cut
1