Skip navigation
zazzo9
Currently Being Moderated

Debugging Alchemy Applications with Netcat

Apr 7, 2009 5:02 PM

The alchemy c library has an implementation of a subset of the GDB/MI machine interface.

 

Assuming the application was linked with debugging enabled (-g), as it boots up, alchemy will attempt to connect to a debugger on localhost:5678 .  If the connection is successful, the virtual machine will immediately halt at the application entry point (the main function), and wait for commands from the debugger in GDB/MI .

 

As far as I could figure out, gdb itself is only a GDB/MI server -- which means that it cannot debug alchemy applications, because alchemy is acting as the server here.  cgdb apparently has a nice MI library in it for anyone hoping to write their own debugger, but cgdb itself does not yet seem to use the library -- tying the interface to the application is still on the todo list.  Eclipse CDT supports MI as well -- I poked at it a little, but lacked the patience to figure out how to make it work.

 

Regardless, any MI clients one might want to use with alchemy are going to expect to read MI from a terminal process, not from a socket, and alchemy sends MI over a socket.  Netcat to the rescue: Netcat is an application whose sole purpose in life is to connect its standard input and output to any given socket.  Using netcat, it would be easy to make a simple shell script that would masquerade as gdb for any MI client that one wanted to connect to alchemy.

 

I chose instead to use netcat to speak GDB/MI directly with my application.  Maybe later I could make a perl script, but I've been searching for some time to find a debugging app that will speak MI, eclipse was the only one I could find, and I couldn't figure out how to make eclipse do it rightly, so I'm basically giving up on that front for now.  The cool thing is that none of it is necessary:

 

nc -lvp 5678

 

This starts netcat listening on port 5678.  Of course, netcat must be downloaded and installed.  After running netcat, boot up an alchemy app compiled with debugging, and netcat shows something like:

 

listening on [any] 5678 ...
connect to [127.0.0.1] from localhost [127.0.0.1] 62459
(gdb)

 

Alchemy is pretending to be gdb!  Isn't that cute.  Now we can type some GDB/MI in:

 

-stack-info-depth
^done,depth="2"
(gdb)
-stack-list-arguments
^done,stack-args=[frame={args=[],level="0"},frame={args=[],level="1"}]
(gdb)
-gdb-show prompt
^done,value="(gdb) "
(gdb)
-info threads
~"* 1 thread 1\n"
^done
(gdb)
-data-list-register-names
^done,register-names=["state","eax","edx","ebp","esp","st0","cf","i0","i1","i2","i3","i4","i5","i6","i7","i8","i9","i10","i11","i12","i13","i14","i15","i16","i17","i18","i19","i20","i21","i22","i23","i24","i25","i26","i27","i28","i29","i30","i31"]
(gdb)
-var-create - * $eax
^done,name="var0",type="int",numchild="0"
(gdb)
-var-evaluate-expression var0
^done,value="0"
(gdb)
-gdb-exit
^done

 

 

I think this is pretty nice.  It's ugly, but it's working debugging.  What follows is a list of the GDB/MI commands Alchemy supports, and what they do, based on the alchemy c library and the gnu reference.  I researched and created this list as I wrote this post -- part of the reason for this was for me to compile what the debugging interface is supposed to do in one place for myself, and others.

 

-environment-cd     (gdb: 'cd')

-environment-directory     (gdb: 'dir')

-gdb-set     (gdb: 'set')

     stubs

These commands are supposed to set the working directory, add a directory to the source search path, and set gdb internal variables, respectively.  In Alchemy, they are simply stubs and do nothing.  Probably they were added to support an MI client used by Adobe that happens to send them.

     result: ^done

 

-gdb-exit     (gdb: 'quit')

Supposed to exit GDB immediately, in Alchemy this just closes the debugging socket.  The application is not resumed if suspended(!).

     result: ^done

 

-gdb-show prompt     (gdb: 'show')

     stub

This is supposed to show the value of a GDB variable.  The only variable it will show in alchemy is 'prompt'.

     result: ^done,value="(gdb) "

 

-data-list-register-names     (insight: 'gdb_regnames')

Lists the names of all the Alchemy virtual machine registers.  The list is returned such that the number of each register is equal to its index.

     result: ^done,register-names=[...]

 

-data-list-changed-registers     (insight: 'gdb_changed_register_list')

     stub

This is supposed to return a list of the numbers of all the registers which have changed.  Alchemy just returns every single register regardless of whether it has changed.

     result: ^done,changed-registers=[...]

 

-info sharedlibrary | threads     (gdb: 'info shared', 'info thread')

     stub

This command doesn't appear to actually be part of MI.  If provided a parameter of 'sharedlibrary', it prints an empty list of shared libraries.  If provided a parameter of 'threads', it specifies 1 thread.

     output:

~"From        To          Syms Read    Shared Object Library\n"

OR

~"* 1 thread 1\n"

     result: ^done

 

-stack-select-frame selectedFrameNum     (gdb: 'frame', 'up', 'down', 'select-frame', 'up-silent', 'down-silent')

Change the currently selected frame.  Select a different frame selectedFrameNum on the stack.

     result: ^done

 

-stack-info-depth

Gives the total stack depth.  The largest meaningful stack frame number should be one less than the returned depth.

     result: ^done,depth=dbgDepth

 

-stack-list-frames [ lowFrame [ highFrame ] ]     (gdb: 'backtrace', 'where')

List the frames currently on the stack, optionally between lowFrame and highFrame inclusive.  For each frame it displays the following info:

        • 'level' -  The frame number, 0 being the topmost frame, i.e., the innermost function.
        • 'addr' -  This should be the program counter, but it is hardcoded to 0xffffffff.  I recommend inspecting the '$state' register instead.
        • 'func' -  Function name.
        • 'file' - File name of the source file where the function lives.
        • 'line' - Line number corresponding to the current position.

           result: ^done,stack=[frame={...},...]

       

      -stack-list-arguments [ showValues [ lowFrame [ highFrame ] ] ]     (insight: 'gdb_get_args')

           stub

      This is supposed to display a list of the arguments for the given frames.  However, it is hardcoded to return an empty set for every frame.

           result: ^done,stack-args=[frame={level="0",args=[]},...]

       

      -stack-list-locals [ showValues ]     (gdb: 'info locals', insight: 'gdb_get_locals')

      Display the local variable names for the currently selected frame.  The showValues parameter is ignored.

           result: ^done,locals=[{name="name"},...]

       

      -exec-continue     (gdb: 'continue')

      -exec-run     (gdb: 'run')

      Both -exec-continue and -exec-run do the same thing: resume the execution of the program until a breakpoint is encountered, or until it exits.

           result if running already: ^error,msg="Already running"

           result if suspended: ^running

       

      -exec-next     (gdb: 'next')

      -exec-step     (gdb: 'step')

      -exec-finish     (gdb: 'finish')

      Resumes the execution of the program until the line number has changed and the highest stack frame is at a given depth.  For -exec-next, execution will break when the recorded line number has changed and the stack frame is the same as the current or shallower.  For -exec-step, the stack frame is ignored and the break will occur as soon as the line number has changed.  For exec-finish, the break will only occur when the stack becomes shallower than it is now.

      This functionality is the same as gdb's "next", "step", "finish" commands.

           result if running already: ^error,msg="Already running"

           result if suspended: ^running

       

      -break-insert [ -t ] function     (gdb: 'break', 'tbreak')

      Set a breakpoint on function.  If -t is set, the breakpoint is temporary.

           result if the location of function cannot be identified: ^error,msg="Can't find: function"

           result if it can: ^done,bkpt={number="number",enabled="y"}

       

      -var-create - * vname

      This operation creates a variable object, which allows the monitoring of a variable or CPU register.  The first parameter must be "-", indicating that the system will automatically generate a unique name "varNNNNNN" by which the object can be referenced.  The second parameter must be "*", indicating that the variable should be evaluated in the current frame.  vname is either the name of a variable in scope, or "$regname" -- a CPU register name.

           result if vname cannot be found: ^error,msg="var-create can't find: vname"

           result if vname is found: ^done,name=varNNNNNN,numchild=0,type="int"

       

      -var-delete name

      Deletes a previously created variable object.  The object is truly deleted and the name should be re-used by Alchemy on a future -var-create.

           result if object cannot be found: ^error,msg="var-delete not tracking: name"

           result on success: ^done

       

      -var-update name

           stub

      This is supposed to re-evaluate the given variable object and any associated expression, but does nothing besides give an error if name is not tracked.

           result if no such variable object: ^error,msg="var-update not tracking: name"

           result if object is tracked: ^done,changelist=[{name="name",in_scope="true",type_changed="false",_order=["name","in_scope","type _changed"]}]

       

      -var-evaluate-expression name

      Evaluates the variable or register represented by the specified variable object and returns its value as a string.

           result if object not found: ^error,msg="var-evaluate-expression not tracking: name"

           result if object is valid: ^done,value=value

       
      Replies
      • Currently Being Moderated
        Apr 7, 2009 11:56 PM   in reply to zazzo9

        Zazzo9, this is great. How much longer before we get an AIR MI debugger?

         
        |
        Mark as:
      • Currently Being Moderated
        Apr 9, 2009 3:31 PM   in reply to zazzo9

        I think it's possible. Take a look at the Gumbo Socket reference:

         

        http://livedocs.adobe.com/flex/gumbo/langref/flash/net/Socket.html

         

        You should be able to open a socket on port 5678. If the socket times out, open it again.

         

        That said, my comment was tongue in cheek Flash developers have a nasty habit of sticking to Flash technologies more than they should (if all you have is a hammer...)

         

        Perl or another scripting language would probably lead to a simpler implementation.

         
        |
        Mark as:
      • Currently Being Moderated
        Jan 31, 2012 6:34 AM   in reply to zazzo9

        Can anyone provide any more information on how to debug while using alchemy. Seems to be very little information about this on the internet. I tried using Netcat (nc -lv 5678) and the perl script above. But both of them never connect. telnet localhost 5678 also never connects.

         

        I compiled my alchemy swc using [ g++ -g {other flags} output.swc ] and linked that into my other project which compiles down to a swf and no luck. also tried running it in air but again.. no luck.

         

        any other pointers or more details would be appreciated.

         
        |
        Mark as:
      • Currently Being Moderated
        Feb 1, 2012 5:39 AM   in reply to zazzo9

        Thanks for the reply. Navcat no longer accepts the arguments l and p together. My project is already developed in alchemy. Just getting tired of always trying to log everything. would be handy if i could get a little debug output.

         

        thanks again.

         
        |
        Mark as:
      • Currently Being Moderated
        Feb 1, 2012 7:50 AM   in reply to zazzo9

        Netcat - 0.7.1

        perl - 5.12

        Flash - 11.1.102.55 (debugger)

        OS - Mac OS 10.7.2

         

         

        Interesting.. it appears both nc and netcat take different arguments..

         

        man nc says....

             -l      Used to specify that nc should listen for an incoming connection rather than initiate a connection to a remote host.  It is an error to use this option in conjunction with the -p, -s, or -z options.  Additionally, any timeouts specified with the -w option are ignored.

         

        netcat -l -p 5678 seems to do the same as nc -lv 5678 for me.. but regardless i'm still not getting any output on that port. i should at least get something with telnet or your perl script.

         


         
        |
        Mark as:
      • Currently Being Moderated
        Apr 19, 2012 7:57 PM   in reply to zazzo9

        I used:

               netcat 0.11

                perl 5.12

                flash 11.2.202.233      flashplayer_11_sa_debug_32bit.exe

                OS - Windows

         

        I run nc -lvp 5678 and I can telnet to it.

        When I run the swf compiled with -debug option, nothing happend.

        Why?

         

        More details:

        alchemy-cygwin-v0.5a  In windows or  alchemy-ubuntu-v0.5a in debian 6

        I modified the stringecho example in alchemy samples directory.

        I added a unimplement call in echo function like this:

        void echo()

        {

            ...

            extern void testfunc();

            testfunc();

        }

         

        build options:

        gcc stringecho.c -g -swc -o stringecho.swc

        mxmlc -debug -library-path+=../stringecho.swc --target-player=11.1 -swf-version=13 -static-link-runtime-shared-libraries=true EchoTest.as

         

        Need your help.

        Thanks very much.

         
        |
        Mark as:

      More Like This

      • Retrieving data ...

      Bookmarked By (0)