quick print type debugging

E

Eric Mahurin

Anybody think something like this would be useful?

module Kernel
def d(message=3D"",&block)
ret =3D block.call
$stderr.puts("DEBUG: #{block.inspect} #{message} =3D>
#{ret}") if $DEBUG
ret
end
end


irb(main):020:0> $DEBUG=3Dtrue
=3D> true
irb(main):019:0> d{1+2*3}
DEBUG: #<Proc:0xb7df9adc@(irb):19> =3D> 7
=3D> 7
irb(main):020:0> $DEBUG=3Dfalse
=3D> false
irb(main):021:0> d{1+2*3}
=3D> 7

If you have an expression to want to see the result of, you
just surround it with d{...}. The block.inspect gives you the
filename and line number and you get the evaluated value. No
need to have a separate "p" or "puts" statement (and possibly
assignment to a variable) because this returns the value.

What would be nice is if you could somehow convert the block
back to text (and put it in the debug message). Anybody know
how to do that? I know I could make the block be a string of
the code instead to solve the problem (d{"..."}), but that
would be uglier and less efficient when we have debugging off
(because we have to eval).


__________________________________________________
Do You Yahoo!?
Tired of spam? Yahoo! Mail has the best spam protection around=20
http://mail.yahoo.com=20
 
A

Austin Ziegler

Anybody think something like this would be useful?

Yes. I'd even support an RCR for this. This would also sort of mandate
a block-to-string RCR (or at least block-to-nodes-to-string) which
I've wanted for a while, but haven't had enough knowledge to express
clearly.

-austin
--=20
Austin Ziegler * (e-mail address removed)
* Alternate: (e-mail address removed)
 
A

Ara.T.Howard

Anybody think something like this would be useful?

module Kernel
def d(message="",&block)
ret = block.call
$stderr.puts("DEBUG: #{block.inspect} #{message} =>
#{ret}") if $DEBUG
ret
end
end

yeah - but maybe ENV['DEBUG'] vs $DEBUG so one could just

DEBUG=1 ruby a.rb

$DEBUG makes many packages VERY noisy when loaded...
irb(main):020:0> $DEBUG=true
=> true
irb(main):019:0> d{1+2*3}
DEBUG: #<Proc:0xb7df9adc@(irb):19> => 7
=> 7
irb(main):020:0> $DEBUG=false
=> false
irb(main):021:0> d{1+2*3}
=> 7

If you have an expression to want to see the result of, you
just surround it with d{...}. The block.inspect gives you the
filename and line number and you get the evaluated value. No
need to have a separate "p" or "puts" statement (and possibly
assignment to a variable) because this returns the value.

What would be nice is if you could somehow convert the block
back to text (and put it in the debug message). Anybody know
how to do that? I know I could make the block be a string of
the code instead to solve the problem (d{"..."}), but that
would be uglier and less efficient when we have debugging off
(because we have to eval).


__________________________________________________
Do You Yahoo!?
Tired of spam? Yahoo! Mail has the best spam protection around
http://mail.yahoo.com

-a
--
===============================================================================
| email :: ara [dot] t [dot] howard [at] noaa [dot] gov
| phone :: 303.497.6469
| Your life dwells amoung the causes of death
| Like a lamp standing in a strong breeze. --Nagarjuna
===============================================================================
 
E

Eric Mahurin

--- "[email protected] said:
require 'dev-utils/debug' # http://dev-utils.rubyforge.org
=20
x =3D 5; y =3D 6
=20
trace 'x + y' # -> outputs "x + y =3D 11"
=20
Doesn't tell you file and line numbers, but do you need that
if it
prints the expression?

I just took a peek at your code. This could be quite slow
because it uses Binding#of_caller (searches stack, uses
continuations) and eval. I don't particularly like the idea of
putting code in a string (editors treat it like a string rather
than code). Oh, and having this debug/trace method return the
expression value would be very useful. So, when you have this:

z =3D x + y

you can just do this to have minimal code change to debug/trace
something:

z =3D trace('x+y') # or d{x+y} # or d2{'x+y'}

Here would be an implementation of d (expression in a block -
doesn't print expression) and d2 (expression in a string in a
block - prints the expression):

$DEBUG_LEVEL =3D ENV['DEBUG']||0
module Kernel
# block contains expression to be evaluated and traced
def d(level=3D1,&block)
result =3D block.call
if $DEBUG_LEVEL>=3Dlevel
file,line =3D /\A#<Proc:0x[0-9A-Fa-f]+@(\S+?):(\d+)>\Z/.
match(block.inspect).captures
$stderr.printf("DEBUG: %s:%d #=3D> %s\n",
file,line,result.to_s)
end
result
end
# block contains string of expression to be evaluated and
traced
def d2(level=3D1,&block)
expr =3D block.call
result =3D eval(expr,block.binding)
if $DEBUG_LEVEL>=3Dlevel
file,line =3D /\A#<Proc:0x[0-9A-Fa-f]+@(\S+?):(\d+)>\Z/.
match(block.inspect).captures
$stderr.printf("DEBUG: %s:%d - %s #=3D> %s\n",
file,line,expr,result.to_s)
end
result
end
end

I don't have ruby on the OS I'm on now, so the above is
untested.

You get it all with d2, but you have the inefficiency of eval
and the ugliness of the expression in a string. But, at least
no Binding#of_caller. I'd probably rather use d and hope that
later down the road we get the ability to convert a Proc back
to the code string. Even if it is inefficient - you only need
it while debugging. Something like Florian's implementation
could be used, be you need one more piece of information - a
column or token number (along with file and line we already
have); otherwise you can't distinguish multiple blocks/lambdas
on the same line.

I just thought of something else. Maybe you could get around
the eval inefficiency in d2, by storing the eval'ed code back
into block (untested):

result =3D begin
block.call_string
rescue
expr =3D block.call
proc =3D eval("lambda {#{expr}}",block.binding)
block_meta_class =3D (class << block;self;end)
block_meta_class.send:)define_method,:call_string,&proc)
retry
end

Again, this is untested. The idea with above is to only need
to eval once. Something like this should work as long as block
is the same Proc object every time d2 is called in a certain
part of the code (block is compiled and converted to a Proc
once).


__________________________________________________
Do You Yahoo!?
Tired of spam? Yahoo! Mail has the best spam protection around=20
http://mail.yahoo.com=20
 
F

Florian Groß

Eric said:
I just took a peek at your code. This could be quite slow
because it uses Binding#of_caller (searches stack, uses
continuations) and eval. I don't particularly like the idea of
putting code in a string (editors treat it like a string rather
than code). Oh, and having this debug/trace method return the
expression value would be very useful.

Why do you think that this is too slow? It's something that only happens
when debugging. Putting the string into a block doesn't help at all
because strings are generated at parse-time so you avoid no overhead at all.

True, getting the caller's binding isn't exactly fast, but it only
happens when debugging. I think you are overrating this as you can make
trace() a noop when $DEBUG is false. (Just like I do with assert { } in
ruby-breakpoint.)

I'm pretty sure that Proc#source is way slower than the
Binding.of_caller solution, by the way.
 
E

Eric Mahurin

--- Florian Gro=DF said:
Eric Mahurin wrote:
=20
=20
Why do you think that this is too slow? It's something that
only happens=20
when debugging. Putting the string into a block doesn't help
at all=20
because strings are generated at parse-time so you avoid no
overhead at all.
=20
True, getting the caller's binding isn't exactly fast, but it
only=20
happens when debugging. I think you are overrating this as
you can make=20
trace() a noop when $DEBUG is false. (Just like I do with
assert { } in=20
ruby-breakpoint.)
=20
I'm pretty sure that Proc#source is way slower than the=20
Binding.of_caller solution, by the way.


My proposal was that this expression passed to this trace/debug
method always be evaluated (and returned) so that you could
transparently trace any sub-expression in a larger expression.=20
I think this would make it an extremely quick way to add traces
anywhere in a piece of code (except LHS of assigns - but you
could trace the whole assign). I was thinking you might leave
some of this in your code indefinitely and control by a debug
level, but some you may put in temporarily.

I wouldn't mind if getting the file, line, and source of the
expression was slow because that would only be done during
debugging - but not the expression evaluation.

Maybe a way to get around the slowness of Binding#of_caller
would be to modify the string expression after the first call
and give it a "call" method (similar to what I proposed for the
expression in a string in a block). That way you'd only need
Binding#of_caller and eval once per string expression.


__________________________________________________
Do You Yahoo!?
Tired of spam? Yahoo! Mail has the best spam protection around=20
http://mail.yahoo.com=20
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,065
Latest member
OrderGreenAcreCBD

Latest Threads

Top