{ "info": { "author": "Google", "author_email": "pyringe-dev@googlegroups.com", "bugtrack_url": null, "classifiers": [ "Environment :: Console", "Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Topic :: Software Development :: Debuggers" ], "description": "**DISCLAIMER: This is not an official google project, this is just something I wrote while at Google.**\n\nPyringe\n=======\n\nWhat this is\n------------\n\nPyringe is a python debugger capable of attaching to running processes, inspecting their state and even of injecting python code into them while they're running. With pyringe, you can list threads, get tracebacks, inspect locals/globals/builtins of running functions, all without having to prepare your program for it.\n\nWhat this is not\n----------------\n\nA \"Google project\". It's my internship project that got open-sourced. Sorry for the confusion.\n\nWhat do I need?\n---------------\n\nPyringe internally uses gdb to do a lot of its heavy lifting, so you will need a fairly recent build of gdb (version 7.4 onwards, and only if gdb was configured with `--with-python`). You will also need the symbols for whatever build of python you're running. \nOn Fedora, the package you're looking for is `python-debuginfo`, on Debian it's called `python2.7-dbg` (adjust according to version). Arch Linux users: see [issue #5][]. \nHaving [Colorama](https://pypi.python.org/pypi/colorama) will get you output in boldface, but it's optional.\n\n[issue #5]: https://github.com/google/pyringe/issues/5\n\nHow do I get it?\n----------------\n\nGet it from the [Github repo][], [PyPI][], or via pip (`pip install pyringe`).\n\n[Github repo]: https://github.com/google/pyringe\n[PyPI]: https://pypi.python.org/pypi/pyringe\n\nIs this Python3-friendly?\n-------------------------\n\nShort answer: **No, sorry.** Long answer: \nThere's three potentially different versions of python in play here: \n1. The version running pyringe \n2. The version being debugged \n3. The version of `libpythonXX.so` your build of gdb was linked against \n\n`2` Is currently the dealbreaker here. Cpython has changed a bit in the meantime[1], and making all features work while debugging python3 will have to take a back seat for now until the more glaring issues have been taken care of. \nAs for `1` and `3`, the `2to3` tool may be able to handle it automatically. But then, as long as `2` hasn't been taken care of, this isn't really a use case in the first place.\n\n[1] - For example, `pendingbusy` (which is used for injection) has been renamed to `busy` and been given a function-local scope, making it harder to interact with via gdb.\n\nWill this work with PyPy?\n-------------------------\n\nUnfortunately, no. Since this makes use of some CPython internals and implementation details, only CPython is supported. If you don't know what PyPy or CPython are, you'll probably be fine.\n\nWhy not PDB?\n------------\n\nPDB is great. Use it where applicable! But sometimes it isn't. \nLike when python itself crashes, gets stuck in some C extension, or you want to inspect data without stopping a program. In such cases, PDB (and all other debuggers that run within the interpreter itself) are next to useless, and without pyringe you'd be left with having to debug using `print` statements. Pyringe is just quite convenient in these cases.\n\n\nI injected a change to a local var into a function and it's not showing up!\n---------------------------------------------------------------------------\n\nThis is a known limitation. Things like `inject('var = 2')` won't work, but `inject('var[1] = 1337')` should. This is because most of the time, python internally uses a fast path for looking up local variables that doesn't actually perform the dictionary lookup in `locals()`. In general, code you inject into processes with pyringe is very different from a normal python function call.\n\nHow do I use it?\n----------------\n\nYou can start the debugger by executing `python -m pyringe`. Alternatively:\n\n\n```python\nimport pyringe\npyringe.interact()\n```\n\nIf that reminds you of the code module, good; this is intentional. \nAfter starting the debugger, you'll be greeted by what behaves almost like a regular python REPL. \nTry the following:\n\n\n```python\n==> pid:[None] #threads:[0] current thread:[None]\n>>> help()\nAvailable commands:\n attach: Attach to the process with the given pid.\n bt: Get a backtrace of the current position.\n [...]\n==> pid:[None] #threads:[0] current thread:[None]\n>>> attach(12679)\n==> pid:[12679] #threads:[11] current thread:[140108099462912]\n>>> threads()\n[140108099462912, 140108107855616, 140108116248323, 140108124641024, 140108133033728, 140108224739072, 140108233131776, 140108141426432, 140108241524480, 140108249917184, 140108269324032]\n```\n\nThe IDs you see here correspond to what `threading.current_thread().ident` would tell you. \nAll debugger functions are just regular python functions that have been exposed to the REPL, so you can do things like the following.\n\n```python\n==> pid:[12679] #threads:[11] current thread:[140108099462912]\n>>> for tid in threads():\n... if not tid % 10:\n... thread(tid)\n... bt()\n... \nTraceback (most recent call last):\n File \"/usr/lib/python2.7/threading.py\", line 524, in __bootstrap\n self.__bootstrap_inner()\n File \"/usr/lib/python2.7/threading.py\", line 551, in __bootstrap_inner\n self.run()\n File \"/usr/lib/python2.7/threading.py\", line 504, in run\n self.__target(*self.__args, **self.__kwargs)\n File \"./test.py\", line 46, in Idle\n Thread_2_Func(1)\n File \"./test.py\", line 40, in Wait\n time.sleep(n)\n==> pid:[12679] #threads:[11] current thread:[140108241524480]\n>>> \n```\n\nYou can access the inferior's locals and inspect them like so:\n\n```python\n==> pid:[12679] #threads:[11] current thread:[140108241524480]\n>>> inflocals()\n{'a': , 'LOL': 'success!', 'b': , 'n': 1}\n==> pid:[12679] #threads:[11] current thread:[140108241524480]\n>>> p('a')\n\n==> pid:[12679] #threads:[11] current thread:[140108241524480]\n>>> p('a').attr\n'Some_magic_string'\n==> pid:[12679] #threads:[11] current thread:[140108241524480]\n>>> \n```\n\nAnd sure enough, the definition of `a`'s class reads:\n\n```python\nclass Example(object):\n cl_attr = False\n def __init__(self):\n self.attr = 'Some_magic_string'\n```\n\nThere's limits to how far this proxying of objects goes, and everything that isn't trivial data will show up as strings (like `''`). \nYou can inject python code into running programs. Of course, there are caveats but... see for yourself:\n\n```python\n==> pid:[12679] #threads:[11] current thread:[140108241524480]\n>>> inject('import threading')\n==> pid:[12679] #threads:[11] current thread:[140108241524480]\n>>> inject('print threading.current_thread().ident')\n==> pid:[12679] #threads:[11] current thread:[140108241524480]\n>>> \n```\n\nThe output of my program in this case reads:\n\n```\n140108241524480\n```\n\nIf you need additional pointers, just try using python's help (`pyhelp()` in the debugger) on debugger commands.", "description_content_type": null, "docs_url": null, "download_url": "UNKNOWN", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/google/pyringe", "keywords": null, "license": "Apache License, Version 2.0", "maintainer": null, "maintainer_email": null, "name": "pyringe", "package_url": "https://pypi.org/project/pyringe/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/pyringe/", "project_urls": { "Download": "UNKNOWN", "Homepage": "https://github.com/google/pyringe" }, "release_url": "https://pypi.org/project/pyringe/1.0.2/", "requires_dist": null, "requires_python": null, "summary": "Python debugger capable of attaching to processes", "version": "1.0.2" }, "last_serial": 1086927, "releases": { "1.0": [ { "comment_text": "", "digests": { "md5": "7f5a54c9293a0c68d699617267bda3b9", "sha256": "5c2ded6d9734abc7776ce9f09baf9a689937e548862976bab52a7ab73a13fd3c" }, "downloads": -1, "filename": "pyringe-1.0.tar.gz", "has_sig": false, "md5_digest": "7f5a54c9293a0c68d699617267bda3b9", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 41297, "upload_time": "2014-03-30T15:35:33", "url": "https://files.pythonhosted.org/packages/0d/07/77cc615e1dbc522739aa4e90fa66a27e1751398ed27cd75252a3bef3a113/pyringe-1.0.tar.gz" } ], "1.0.1": [ { "comment_text": "", "digests": { "md5": "87d440c56b016f989b0c793e5186aa80", "sha256": "414262d88420cc446397ee018b0886bc33caaa08bd45a365bb0181aae00716d8" }, "downloads": -1, "filename": "pyringe-1.0.1.tar.gz", "has_sig": false, "md5_digest": "87d440c56b016f989b0c793e5186aa80", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 42560, "upload_time": "2014-04-24T13:43:32", "url": "https://files.pythonhosted.org/packages/61/ae/583884e45e3ea5d73d47f4ca6e42a9ca38cbe7091408b6681c17f392eb91/pyringe-1.0.1.tar.gz" } ], "1.0.2": [ { "comment_text": "", "digests": { "md5": "21ff4f8c7beaf2498aaa9c3a46df01c9", "sha256": "b93f5710c05c8022d04b0145b54825d8748469c04deb7bc6b12fa44174e39324" }, "downloads": -1, "filename": "pyringe-1.0.2.tar.gz", "has_sig": false, "md5_digest": "21ff4f8c7beaf2498aaa9c3a46df01c9", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 42804, "upload_time": "2014-05-09T15:13:45", "url": "https://files.pythonhosted.org/packages/7c/c6/6cef124c38227ece01350414c7866727179d17f64b88b8bf513386c0e4be/pyringe-1.0.2.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "21ff4f8c7beaf2498aaa9c3a46df01c9", "sha256": "b93f5710c05c8022d04b0145b54825d8748469c04deb7bc6b12fa44174e39324" }, "downloads": -1, "filename": "pyringe-1.0.2.tar.gz", "has_sig": false, "md5_digest": "21ff4f8c7beaf2498aaa9c3a46df01c9", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 42804, "upload_time": "2014-05-09T15:13:45", "url": "https://files.pythonhosted.org/packages/7c/c6/6cef124c38227ece01350414c7866727179d17f64b88b8bf513386c0e4be/pyringe-1.0.2.tar.gz" } ] }