Mr. Guid 0.2 (Cross-platform Ruby GUI Debugger)

Mr. Guid 0.2 is a milestone release because it can now be run on both
Linux and Windows without any compatibility issues.

Notable changes since 0.1.4:

  • All debugging is done via TCP instead of UNIX sockets
  • Instance variables view
  • Increased efficiency overall
    (Thanks to Matthias G. for his help)

Website: http://mr-guid.rubyforge.org
Project page: http://rubyforge.org/projects/mr-guid

Mitchell’s Ruby GUI Debugger (Mr. Guid) is a simple cross-platform Ruby
GUI debugger written in Ruby using the Ruby/Gtk2 bindings for GTK+. It
is only meant to be a debugger, not an editor or IDE.

thanks for this !!!
i have waited so long for a decent debugger for ruby.

– henon

Sorry all, I am having some instability issues in the windows version I
will have a fix soon.

Okay, my experiments show that TCP in the Windows version of Ruby
doesn’t work like the Unix/Linux version. This is causing the
instability of the windows version of Mr. Guid. My only suggestion
right now for the Windows users is to get Ruby running in Cygwin.

I would appreciate more help figuring out how to work around this
problem of course so that cygwin is not needed.

Here is what I have figured out:

  1. The TCPServer starts correctly and runs the ‘accept’ method waiting
    for a connection
  2. The TCPSocket successfully connects to the TCPServer.
  3. The TCPServer “freezes” (or craps out) when trying to print data
    (print, puts methods)
  4. The TCPSocket “freezes” (or craps out) when trying to recieve data
    (gets method)

3 and 4 work as expected in Unix/Linux, so Mr. Guid functions just
fine. It’s just pesky Windows…

From: “mitchell” [email protected]

  1. The TCPServer starts correctly and runs the ‘accept’ method waiting
    for a connection
  2. The TCPSocket successfully connects to the TCPServer.
  3. The TCPServer “freezes” (or craps out) when trying to print data
    (print, puts methods)
  4. The TCPSocket “freezes” (or craps out) when trying to recieve data
    (gets method)

3 and 4 work as expected in Unix/Linux, so Mr. Guid functions just
fine. It’s just pesky Windows…

Hi, I’m just jumping in here… dunno if this helps. But up
through 1.8.2, Ruby didn’t support non-blocking socket I/O on
Windows. I haven’t personally tried non-blocking I/O on 1.8.4
Windows Ruby yet–however, looking through the sources, it
appears it is now supported!

That said, I’m not certain gets/puts are nonblocking-savvy on
Windows or not, even in 1.8.4. But it looks like send() should
be, now.

(And even in 1.8.4 I’m guessing popen() will still block in
Windows. (It definitely blocks in 1.8.2…))

Well anyway, for what it’s worth …

Regards,

Bill

Unfortunately, I cannot use the send or recv commands because of
buffering issues with TCP. It has to be gets/puts which is quite
unfortunate, but I have no other simple workaround.

could you introduce a switch to turn of the usage of TCPServer
completely? maybe that would enable debugging locally on windows
(hopefully).

  • henon

mitchell wrote:

Mr. Guid 0.2 is a milestone release because it can now be run on both
Linux and Windows without any compatibility issues.

Looks cool. Simple and to the point.

Any MacOS X plans?

–Steve

This is not possible. Mr. Guid depends on TCP to do its debugging. Any
other method of communication between the debugger and Mr. Guid that I
can think of is inefficient. If you have any ideas, I would like to
hear them :slight_smile:

From: “mitchell” [email protected]

Unfortunately, I cannot use the send or recv commands because of
buffering issues with TCP. It has to be gets/puts which is quite
unfortunate, but I have no other simple workaround.

Hi, could you explain more about what you mean by buffereing issues?

I primarily use send/recv with TCP. It’s not hard to make a
puts/gets wrapper around send/recv, for instance.

For ex: (untested, but similar to what i frequently have used)

def send_string(sock, str)
begin
while str.length > 0
if select(nil, [sock], nil, nil)
sent = sock.send(str, 0)
str = str[sent…-1]
end
end
rescue IOError, SystemCallError, SocketError
# … set eof flag?
end
end

def puts(sock, str)
send_string(sock, “#{str}\n”)
end

sock.fcntl(Fcntl::F_SETFL, sock.fcntl(Fcntl::F_GETFL) |
Fcntl::O_NONBLOCK) if defined? Fcntl::O_NONBLOCK

puts(sock, “hello”)

Regards,

Bill

Bill,

Mr. Guid acts on each line the debugger sends to it. The gets method
would recieve one line at a time, which is just what I want, but this
only seems to work in linux. For recv, I have to get a block of data
not necessarily delimited by line. This means if the previous line
takes a longer amount of time to process than the debugger can send
data (say listing global variables), the next recv call gets a chunk of
data rather than line by line. I therefore lose data to process. (You
can observe this by changing every ‘gets’ method to ‘recv’ and every
‘puts’ method to ‘send’ in both mr_guid.rb and gdebug.rb and trying to
get a list of global variables)

I guess the previous message didn’t get through…

Bill,

Mr. Guid processes debugging output as each line comes in. The gets
function does just what I need to in linux. In windows, it freezes up
the application for some unknown reason. Although send/recv work in
windows, recv gets a block of data based on what the debugger has sent.
If Mr. Guid takes longer to process debug output than what the debugger
is sending in, (like displaying global variables), the recv grabs the
chunk of data that’s been accumulating and processes it as what it
thinks is a separate line of output when in reality it’s a few lines.
This leads to dropped data.

Hi mitchell,

From: “mitchell” [email protected]

Mr. Guid processes debugging output as each line comes in. The gets
function does just what I need to in linux. In windows, it freezes up
the application for some unknown reason. Although send/recv work in
windows, recv gets a block of data based on what the debugger has sent.
If Mr. Guid takes longer to process debug output than what the debugger
is sending in, (like displaying global variables), the recv grabs the
chunk of data that’s been accumulating and processes it as what it
thinks is a separate line of output when in reality it’s a few lines.
This leads to dropped data.

What I meant, is it’s fairly trivial to write wrappers
around send/recv that act like gets/puts. I provided
an example of a “puts” version of send in my previous email.

It’s similarly easy to write a wrapper around recv that
acts like “gets”.

BTW, has anyone tried Mr. Guid on 1.8.4 Windows? Maybe
gets/puts will work on 1.8.4 on windows if you set
nonblocking mode on your socket. (?)

Regards,

Bill

Well Bill, if you can write some wrappers for gets and puts to get the
windows version working, I’d love that! I’m quite new to sockets and
networking stuff in general, I just took a crash course on the basics.
I also looked into this “nonblocking” thing but have no idea how it’s
used, or even if it works fully in windows now.

On 2/25/06, Bill K. [email protected] wrote:

[…]

What I meant, is it’s fairly trivial to write wrappers
around send/recv that act like gets/puts. I provided
an example of a “puts” version of send in my previous email.

It’s similarly easy to write a wrapper around recv that
acts like “gets”.

why don’t you post a gets that is a wrapper of recv? i think that would
help
mitchell a lot. i would do it myself,
but i am a complete TCP noob. thx in advance.
– henon

[…]

Steve wrote:

Any MacOS X plans?

Theoretically it should work on OSX because it can run linux apps. I
dont have a mac or access to one, so I could be wrong, but all you
would need I think is (preferrably the unix/linux versions of) ruby and
ruby-gtk2 (libglade and gtk2)

From: “Meinrad R.” [email protected]

why don’t you post a gets that is a wrapper of recv? i think that would help
mitchell a lot. i would do it myself,
but i am a complete TCP noob. thx in advance.

Hi, I’ll be happy to when I get a chance… Trying to get
this software done in time to send to the people who have
already gotten on the airplane to demo it at the show. :slight_smile:

Regards,

Bill

Hey thanks Bill! I am happy to say that your wrapper works…most of
the time. There is an issue of when the debugger is finished, I cannot
get the last bit of text send through the socket. I’ll look into it
more, but you delivered pretty much exactly what I needed :slight_smile:

Here’s a status report as of Feb 26:

With Bill’s wrappers, I can get remote debugging to work, but local
debugging still freezes because of thread issues in windows. I am
growing very tired very fast with that operating system and would
definately appreciate any help getting Mr. Guid to play nicely with it.
Until then however, I cannot spend too much time concerning myself with
the issue. For all the windows users who wish to use Mr. Guid, I am
sorry. My only recommendation right now is to get ruby running in
cygwin. I will probably tinker with that and post my results and or
HOWTO’s.

Sorry for all the inconvenience.

From: “mitchell” [email protected]

Well Bill, if you can write some wrappers for gets and puts to get the
windows version working, I’d love that! I’m quite new to sockets and
networking stuff in general, I just took a crash course on the basics.
I also looked into this “nonblocking” thing but have no idea how it’s
used, or even if it works fully in windows now.

Hi mitchell,

As it happens windows ruby didn’t support nonblocking I/O in 1.8.2
and prior anyway. I haven’t personally tried 1.8.4 on windows yet,
but I believe nonblocking socket I/O is finally supported (!!!)
for which I am/will-be very grateful. :slight_smile:

However, the following methods should work as nonblocking
replacements for gets/puts on windows, even under 1.8.2.

I’ve tested them on ruby 1.8.2 (2004-12-25) [i386-mswin32] and
they appear to work. (Apologies if there are any bugs.)

Hope this helps,

Regards,

Bill

require ‘socket’

class TCPSocket

Nonblocking puts.

Since 1.8.2 on win32 doesn’t support nonblocking socket I/O,

we’ll select on each character we send().

alias_method :orig_puts, :puts
def puts(str)
str = “#{str}\n” unless str[-1] == ?\n
str.each_byte do |val|
Kernel.select(nil, [self], nil, nil)
self.send(val.chr, 0)
end
end

Buffered nonblocking gets. Returns partial last line if any.

Returns nil when eof reached.

alias_method :orig_gets, :gets
def gets
buf = (@nb_gets_buf ||= “”)
while (lineparse = buf.split(/\n/, 2)).length < 2
Kernel.select([self], nil, nil, nil)
dat = self.recv(65536) # arbitrary read length
if dat.empty? # reached eof?
@nb_gets_buf = nil
return buf.empty? ? nil : buf # return partial last line if we
have one
end
buf << dat
end
buf.replace(lineparse[1]) # store remaining unparsed data back in
buffer
lineparse[0] + “\n”
end
end

here’s the code I tested it with… i should have made them automated

tests i guess - these require visual inspection of the output…

it prints a big ugly mess of stuff to the console… ;-/

require ‘thread’
$stdout.sync = true

sv = TCPServer.new(12345)
cl = TCPSocket.new(‘localhost’, 12345)
sv_cl = sv.accept

happy_th = Thread.new { loop { puts “happy!”; sleep 1 } }

gobble_th = Thread.new {
while line = sv_cl.gets
print "gobble!(#{line.inspect}) "
sleep(0.1) if rand > 0.99 # make sure we fall behind the main
puts() thread
end
}

0.upto(12288) do |i|
print "#{i} "
cl.puts(i.to_s)
end
cl.send(“p”, 0) # end with a “partial” line

cl.close
gobble_th.join

if the final gobbles you see in the console before the program

exits are:

gobble!(“12288\n”) gobble!(“p”)

… then it “worked”.