Settings

Theme

Exploring and Dynamically Patching Django/Python Using GDB

stripe.com

81 points by ebroder 14 years ago · 11 comments

Reader

robbles 14 years ago

Couldn't you do almost the exact same thing using pdb, without all the C API / register stuff?

Maybe I misunderstood the post, but it sounds like you're just stopping right before the request returns, reloading the module, generating a new response, and allowing execution to continue.

Why is GDB required for any of this?

  • ebroderOP 14 years ago

    You're right! You could do all of this with pdb, but only if you have enough foresight to run the app under pdb to begin with (which I definitely never do).

    Using GDB, you don't have to change the app or remember to run it in a particular way.

    • mickeyp 14 years ago

      You could install a custom signal, like SIGUSR1, and have it trigger pdb when that signal is sent to your process.

    • robbles 14 years ago

      Right, I see the use in this now. How difficult would it be to wrap this up in a reusable script that you could run directly, instead of having to manually work through the debugger prompt?

      • ebroderOP 14 years ago

        Hmm, it'd probably be doable. In particular, you can use the "commands" command to script what happens when you hit a breakpoint (a friend talked about this in a Ksplice GDB blog post: https://blogs.oracle.com/ksplice/entry/8_gdb_tricks_you_shou...)

        Normally, the "finish" command will interrupt any script you're in the middle of executing, so we have to do a bit of an ugly hack to make sure our script keeps running:

        b PyEval_EvalFrameEx if strcmp(PyString_AsString(f->f_code->co_name), "handle_uncaught_exception") == 0

        commands

        disable

        frame 3

        python

        gdb.execute('finish')

        gdb.execute('shell git stash pop')

        gdb.execute('call PyImport_ReloadModule(PyImport_AddModule("monospace.views"))')

        gdb.execute('call PyImport_ReloadModule(PyImport_AddModule("monospace.urls"))')

        gdb.execute('set $self = PyDict_GetItemString(f->f_locals, "self")')

        gdb.execute('set $request = PyDict_GetItemString(f->f_locals, "request")')

        gdb.execute('set $get_response = PyObject_GetAttrString($self, "get_response")')

        gdb.execute('set $args = Py_BuildValue("(O)", $request)')

        gdb.execute('set $rax PyObject_Call($get_response, $args, 0)')

        gdb.execute('enable')

        gdb.execute('c')

        end

        c

        end

        (Bah. Edited because I couldn't get monospace text working)

        You could put those commands into a file and run "gdb -p <my django process> -x <my commands file>"

        Of course, instead of shelling out to git stash pop, you'd probably want to pause so you could update the code. And you may need to reload more modules than just monospace.views and monospace.urls, depending on the change.

        • Luyt 14 years ago

          > Edited because I couldn't get monospace text working

          If you start each line with a few spaces, HN will format it as source code:

              b PyEval_EvalFrameEx if strcmp(PyString_AsString(f->f_code->co_name), "handle_uncaught_exception") == 0
              commands
              disable
              frame 3
              python
              gdb.execute('finish')
              gdb.execute('shell git stash pop')
              gdb.execute('call PyImport_ReloadModule(PyImport_AddModule("monospace.views"))')
              gdb.execute('call PyImport_ReloadModule(PyImport_AddModule("monospace.urls"))')
              gdb.execute('set $self = PyDict_GetItemString(f->f_locals, "self")')
              gdb.execute('set $request = PyDict_GetItemString(f->f_locals, "request")')
              gdb.execute('set $get_response = PyObject_GetAttrString($self, "get_response")')
              gdb.execute('set $args = Py_BuildValue("(O)", $request)')
              gdb.execute('set $rax PyObject_Call($get_response, $args, 0)')
              gdb.execute('enable')
              gdb.execute('c')
              end
              c
              end
      • rmaccloy 14 years ago

        This actually exists, sort of: http://pyrasite.readthedocs.org/en/latest/index.html

        I've done this manually a few times, but with foresight attaching signal handlers that open up a port or dump stack (a la `jstack`) is usually less finicky.

    • lucian1900 14 years ago

      Then perhaps it would be easier to use gdb just for bootstrapping pdb into the app?

pc 14 years ago

I've always missed this feature from Seaside/Smalltalk. (Not to mention having a proper language-level debugger at your disposal.)

agumonkey 14 years ago

nice use of cpython knowledge. very black magic, but very usefull in time.

Keyboard Shortcuts

j
Next item
k
Previous item
o / Enter
Open selected item
?
Show this help
Esc
Close modal / clear selection