Commit 7b7467cb authored by Luke Macken's avatar Luke Macken

Merge branch 'release/2.0'

parents 3098b008 35e04fce
Luke Macken <lmacken@redhat.com>
David Malcolm <dmalcolm@redhat.com>
......@@ -3,7 +3,7 @@ pyrasite
.. split here
Pyrasite lets you to inject arbitrary code into running Python processes.
Tools for injecting arbitrary code into running Python processes.
:homepage: http://pyrasite.com
:documentation: http://pyrasite.rtfd.org
......@@ -36,9 +36,12 @@ The graphical interface can be found here: https://github.com/lmacken/pyrasite-g
Authors
~~~~~~~
`Luke Macken <http://twitter.com/lmacken>`_
Created by `Luke Macken <http://twitter.com/lmacken>`_ with the help of
`David Malcolm <http://dmalcolm.livejournal.com>`_ and many other
`contributors <https://github.com/lmacken/pyrasite/contributors>`_.
.. image:: http://api.coderwall.com/lmacken/endorsecount.png
:target: http://coderwall.com/lmacken
License
~~~~~~~
David Malcolm <dmalcolm@redhat.com>
.. image:: https://www.gnu.org/graphics/gplv3-127x51.png
:target: https://www.gnu.org/licenses/gpl.txt
pyrasite - A command-line interface for injecting code into a running Python process
====================================================================================
``pyrasite`` - Inject arbitrary code into a running Python process
==================================================================
::
......
Development
===========
Running from git
----------------
::
git clone git://github.com/lmacken/pyrasite.git
cd pyrasite
python pyrasite/main.py
::
git clone git://github.com/lmacken/pyrasite-gui.git
cd pyrasite-gui
python pyrasite_gui/gui.py
Git flow
--------
Use `git-flow <https://github.com/nvie/gitflow.git>`_.
Style
-----
* `PEP8 <www.python.org/dev/peps/pep-0008>`_
pyrasite-gui - A graphical interface for Pyrasite
=================================================
``pyrasite-gui`` - A graphical interface for Pyrasite
=====================================================
The GUI has been moved into it's own repository:
The pyrasite-gui is a graphical interface for Pyrasite that lets you
easily monitor, analyze, introspect, and alter running Python programs.
https://github.com/lmacken/pyrasite-gui
:source: https://github.com/lmacken/pyrasite-gui
:download: http://pypi.python.org/pypi/pyrasite-gui
Requirements
------------
- Python debuginfo (needed for live object inspection)
- PyGObject3 Introspection bindings
- WebKitGTK3
- `meliae <https://launchpad.net/meliae>`_ (easy_install/pip may not work for this install. If not, use the tarball from the distribution website. You may need to install `Cython <http://cython.org>`_ in order to get meliae to build)
- `pycallgraph <http://pycallgraph.slowchop.com>`_
- `psutil <http://code.google.com/p/psutil>`_
Distribution-specific instructions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: bash
# Fedora
yum --enablerepo=updates-testing install python-psutil python-debuginfo python-pycallgraph pygobject3 webkitgtk3 python-meliae
# Ubuntu:
apt-get install python-dbg python-pycallgraph python-gobject-dev gir1.2-webkit-3.0 python-meliae python-psutil
# Arch
pacman -S python2-psutil python2-gobject python2-pycallgraph libwebkit3 python2-meliae
Screenshots
-----------
.. image:: http://lewk.org/img/pyrasite/pyrasite-info.png
......
......@@ -4,62 +4,28 @@ Installing
Requirements
~~~~~~~~~~~~
Core
----
* `gdb <https://www.gnu.org/s/gdb>`_ (version 7.3+)
* `gdb <https://www.gnu.org/s/gdb>`_ (version 7.3+)
GUI
---
Python Compatiblity
~~~~~~~~~~~~~~~~~~~
- `Pyrasite <https://github.com/lmacken/pyrasite>`_
- Python debuginfo (needed for live object inspection)
Pyrasite works with Python 2.4 and newer. Injection works between versions
as well, so you can run Pyrasite under Python 3 and inject into 2, and
vice versa.
- Fedora: python-debuginfo, Ubuntu: python-dbg
- PyGObject3 Introspection bindings
- Fedora: pygobject3, Ubuntu: python-gobject-dev, Arch: python2-gobject
- WebKitGTK3
- Fedora: webkitgtk3, Ubuntu: gir1.2-webkit-3.0, Arch: libwebkit3
- `meliae <https://launchpad.net/meliae>`_
- easy_install/pip may not work for this install. If not, use the tarball from the distribution website. You may need to install `Cython <http://cython.org>`_ in order to get meliae to build.
- Fedora: python-meliae, Ubuntu: python-meliae, Arch: python2-meliae
- `pycallgraph <http://pycallgraph.slowchop.com>`_
- Fedora: python-pycallgraph, Ubuntu: python-pycallgraph, Arch: python2-pycallgraph
- `psutil <http://code.google.com/p/psutil>`_
- Fedora: python-psutil, Ubuntu: python-psutil, Arch: python2-psutil
Download
~~~~~~~~
Download the latest stable release from PyPi: http://pypi.python.org/pypi/pyrasite
::
pip install pyrasite
Installing
~~~~~~~~~~
Running from git
~~~~~~~~~~~~~~~~
You can download the latest tarballs, RPMs, and debs from `PyPi <http://pypi.python.org/pypi/pyrasite>`_. Installing the package specific to your distribution is recommended. However, you
can also install it using ``pip`` if you wish
::
git clone git://github.com/lmacken/pyrasite.git
cd pyrasite
python -m pyrasite.main
pip install pyrasite pyrasite-gui
.. note::
If you're on Python 2.4, you can run pyrasite by doing
``PYTHONPATH=$(pwd) python pyrasite/main.py``
.. seealso:: `pyrasite-gui <http://pyrasite.readthedocs.org/en/latest/GUI.html>`_ for instructions on installing the graphical interface
Additional installation notes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
......@@ -67,20 +33,6 @@ Additional installation notes
Fedora
------
pyrasite and pycallgraph are currently not available in Fedora. You can run
pyrasite on Fedora by doing the following:
.. code-block:: bash
sudo yum -y install python-meliae python-devel python-psutil pygobject3 graphviz python-virtualenv git-core gcc
sudo yum -y --enablerepo=\*-debuginfo install python-debuginfo
git clone -b develop git://github.com/lmacken/pyrasite.git
cd pyrasite
virtualenv [--system-site-packages if on F16+] env
source env/bin/activate
python setup.py develop
pyrasite-gui
If you're using Fedora 17 or later, you'll need to disable an SELinux boolean to allow ptrace.
.. code-block:: bash
......@@ -111,4 +63,5 @@ Since version 10.10, Ubuntu ships with a `controversial patch <https://lkml.org/
echo 0 > /proc/sys/kernel/yama/ptrace_scope
You can make this change permanent by setting ``ptrace_scope`` to ``0`` in
``/etc/sysctl.d/10-ptrace.conf``.
``pyrasite-memory-viewer`` - View the largest objects in your process
=====================================================================
Pyrasite provides a tool to view object memory usage statistics, and the
live value, of largest objects in your process. This requires `urwid
<http://pypi.python.org/pypi/urwid>`_ and `meliae
<https://launchpad.net/meliae>`_ to be installed.
::
$ pyrasite-memory-viewer <PID>
.. image:: http://lewk.org/img/pyrasite-memory-viewer.png
This tool automatically injects the following payload:
.. literalinclude:: ../pyrasite/payloads/dump_memory.py
:language: python
:start-after: html
You can easily dump the object memory usage JSON data by hand, if you wish:
::
$ pyrasite <PID> pyrasite/payloads/dump_memory.py
Payloads
========
Example Payloads
================
Viewing the largest objects in your process
-------------------------------------------
These payloads can be found in the `pyrasite/payloads
<https://github.com/lmacken/pyrasite/tree/master/pyrasite/payloads>`_
directory.
This payload uses `meliae <https://launchpad.net/meliae>`_ to dump all of
the objects in your process to an `objects.json` file (currently dumped in
the working directory of your process).
We recommend using python-meliae from your OS distribution, if available.
If it is not, you will need to first install Cython, and then meliae
seperately. If pip/easy_install does not work, you may need to use the
tarball from the upstream website.
Dumping thread stacks
---------------------
.. literalinclude:: ../pyrasite/payloads/dump_memory.py
.. literalinclude:: ../pyrasite/payloads/dump_stacks.py
:language: python
:start-after: html
::
$ pyrasite <PID> pyrasite/payloads/dump_memory.py
Pyrasite also provides a tool to view the values of largest objects in your
process. This requires `urwid <http://pypi.python.org/pypi/urwid>`_ to be
installed.
::
$ pyrasite-memory-viewer <PID> objects.json
Viewing loaded modules
----------------------
.. image:: http://lewk.org/img/pyrasite-memory-viewer.png
.. literalinclude:: ../pyrasite/payloads/dump_modules.py
:language: python
Call Graph
----------
......@@ -43,48 +26,28 @@ graph using `pycallgraph <http://pycallgraph.slowchop.com>`_.
.. literalinclude:: ../pyrasite/payloads/start_callgraph.py
:language: python
:start-after: http
.. literalinclude:: ../pyrasite/payloads/stop_callgraph.py
:language: python
:start-after: http
::
$ pyrasite <PID> pyrasite/payloads/start_callgraph.py
$ pyrasite <PID> pyrasite/payloads/stop_callgraph.py
The callgraph is then generated using `graphviz <http://www.graphviz.org>`_ and
saved to `callgraph.png`. You can see an example callgraph `here <http://pycallgraph.slowchop.com/pycallgraph/wiki/RegExpExample>`_.
Viewing loaded modules
----------------------
.. literalinclude:: ../pyrasite/payloads/dump_modules.py
:language: python
::
$ pyrasite <PID> pyrasite/payloads/dump_modules.py
Dumping thread stacks
---------------------
.. literalinclude:: ../pyrasite/payloads/dump_stacks.py
:language: python
::
$ pyrasite <PID> pyrasite/payloads/dump_stacks.py
Forcing garbage collection
---------------------------
.. literalinclude:: ../pyrasite/payloads/force_garbage_collection.py
:language: python
::
Dumping out object memory usage statistics
------------------------------------------
.. literalinclude:: ../pyrasite/payloads/dump_memory.py
:language: python
$ pyrasite <PID> pyrasite/payloads/force_garbage_collection.py
.. seealso:: :doc:`MemoryViewer`
Reverse Subprocess Shell
------------------------
......
pyrasite-shell - Give it a pid, get a shell
===========================================
``pyrasite-shell`` - Give it a pid, get a shell
===============================================
You can easily drop into a shell and execute commands in a running process
using the ``pyrasite-shell``.
......@@ -11,11 +11,11 @@ using the ``pyrasite-shell``.
.. code-block:: bash
$ pyrasite-shell $(pgrep -f "python -v")
pyrasite shell 2.0beta7
$ pyrasite-shell $(pgrep -f "ipython")
Pyrasite Shell 2.0beta9
Connected to 'ipython'
Python 2.7.2 (default, Oct 27 2011, 01:40:22)
[GCC 4.6.1 20111003 (Red Hat 4.6.1-10)] on linux2
>>> print(x)
foo
......
......@@ -20,6 +20,8 @@ Contents
Installing
CLI
Shell
MemoryViewer
GUI
Payloads
API
Development
%global betaver beta9
Name: pyrasite
Version: 2.0
Release: 0.1.%{betaver}%{?dist}
Release: 1%{?dist}
Summary: Code injection and monitoring of running Python processes
Group: Development/Languages
License: GPLv3
URL: http://pyrasite.com
Source0: http://pypi.python.org/packages/source/p/%{name}/%{name}-%{version}%{betaver}.tar.gz
Source0: http://pypi.python.org/packages/source/p/%{name}/%{name}-%{version}.tar.gz
BuildArch: noarch
BuildRequires: python-devel
BuildRequires: python-setuptools-devel
BuildRequires: python-nose
BuildRequires: python-sphinx
Requires: gdb
Requires: gdb >= 7.3
%if 0%{?rhel} <= 6
BuildRequires: python-argparse
Requires: python-argparse
%endif
%description
Pyrasite uses the GNU debugger to inject code into a running Python process.
......@@ -23,25 +27,29 @@ It is comprised of a command-line tool, and a Python API. This package
also comes with a variety of example payloads.
%prep
%setup -q -n %{name}-%{version}%{betaver}
%setup -q
%build
%{__python} setup.py build
make -C docs man
%check
%{__python} setup.py test
%install
%{__python} setup.py install -O1 --skip-build --root $RPM_BUILD_ROOT
%{__python} setup.py install -O1 --skip-build --root %{buildroot}
mkdir -p %{buildroot}%{_mandir}/man1
gzip -c docs/_build/man/pyrasite.1 > %{buildroot}/%{_mandir}/man1/pyrasite.1.gz
%files
%defattr(-,root,root,-)
%doc README.rst
%doc README.rst LICENSE
%doc %{_mandir}/man1/pyrasite.1.gz
%{_bindir}/pyrasite
%{_bindir}/pyrasite-memory-viewer
%{_bindir}/pyrasite-shell
%{python_sitelib}/*
%changelog
* Mon Mar 12 2012 Luke Macken <lmacken@redhat.com> 2.0-0.1.beta1
* Mon Mar 12 2012 Luke Macken <lmacken@redhat.com> 2.0-1
- Initial package for Fedora
__version__ = '2.0beta9'
__version__ = '2.0'
__all__ = ('inject', 'inspect', 'PyrasiteIPC',
'ReverseConnection', 'ReversePythonConnection')
__license__ = """\
......
......@@ -23,10 +23,12 @@ import os
import socket
import struct
import tempfile
import pyrasite
import subprocess
from os.path import dirname, abspath, join
import pyrasite
class PyrasiteIPC(object):
"""Pyrasite Inter-Python Communication.
......@@ -59,13 +61,29 @@ class PyrasiteIPC(object):
# shell payloads with netcat.
reliable = True
def __init__(self, pid):
def __init__(self, pid, reverse='ReversePythonConnection'):
super(PyrasiteIPC, self).__init__()
self.pid = pid
self.sock = None
self.server_sock = None
self.hostname = None
self.port = None
self.reverse = reverse
def __enter__(self):
self.connect()
return self
def __exit__(self, *args, **kwargs):
self.close()
@property
def title(self):
if not getattr(self, '_title', None):
p = subprocess.Popen('ps --no-heading -o cmd= -p %d' % self.pid,
stdout=subprocess.PIPE, shell=True)
self._title = p.communicate()[0].decode('utf-8')
return self._title.strip()
def connect(self):
"""
......@@ -116,7 +134,7 @@ class PyrasiteIPC(object):
line = line.replace('reliable = True', 'reliable = False')
tmp.write(line)
tmp.write('ReversePythonConnection().start()\n')
tmp.write('%s().start()\n' % self.reverse)
tmp.close()
payload.close()
......
......@@ -40,24 +40,48 @@ def ptrace_check():
p = subprocess.Popen([getsebool, 'deny_ptrace'],
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = p.communicate()
if out.decode('utf-8') == u'deny_ptrace --> on\n':
if str(out) == 'deny_ptrace --> on\n':
print("WARNING: ptrace is disabled. Injection will not work.")
print("You can enable it by running the following:")
print("sudo setsebool -P deny_ptrace=off")
print("")
def get_payload_dir():
return os.path.join(os.path.dirname(pyrasite.__file__), 'payloads')
def list_payloads():
return sorted(fn for fn in os.listdir(get_payload_dir())
if fn.endswith('.py') and not fn.startswith('_'))
def expand_payload(payload):
"""If a standard payload with this name exists, return its full path.
Otherwise return the input value unchanged.
"""
if os.path.sep not in payload:
fn = os.path.join(get_payload_dir(), payload)
if os.path.isfile(fn):
return fn
return payload
def main():
ptrace_check()
parser = argparse.ArgumentParser(
description='pyrasite - inject code into a running python process',
epilog="For updates, visit https://github.com/lmacken/pyrasite")
parser.add_argument('pid',
parser.add_argument('pid', nargs='?',
help="The ID of the process to inject code into")
parser.add_argument('filename',
parser.add_argument('payload', nargs='?', default='',
help="The Python script to be executed inside the"
" running process, also referred to as 'payload'")
" running process. Can be one of the standard"
" payloads (see --list-payloads) or a filname.")
parser.add_argument('-l', '--list-payloads', help='List standard payloads',
default=False, action='store_const', const=True)
parser.add_argument('--gdb-prefix', dest='gdb_prefix',
help='GDB prefix (if specified during installation)',
default="")
......@@ -70,19 +94,25 @@ def main():
args = parser.parse_args()
if args.list_payloads:
print("Available payloads:")
for payload in list_payloads():
print(" %s" % payload)
sys.exit()
try:
pid = int(args.pid)
except ValueError:
print("Error: The first argument must be a pid")
sys.exit(2)
filename = args.filename
filename = expand_payload(args.payload)
if filename:
if not os.path.exists(filename):
print("Error: Invalid path or file doesn't exist")
sys.exit(3)
else:
print("Error: The second argument must be a filename")
print("Error: The second argument must be a filename or a payload name")
sys.exit(4)
pyrasite.inject(pid, filename, verbose=args.verbose,
......
......@@ -5,5 +5,5 @@
# https://launchpad.net/meliae
# http://jam-bazaar.blogspot.com/2009/11/memory-debugging-with-meliae.html
from meliae import scanner
scanner.dump_all_objects('objects.json')
import os, meliae.scanner
meliae.scanner.dump_all_objects('/tmp/pyrasite-%d-objects.json' % os.getpid())
......@@ -23,6 +23,7 @@ import sys
import socket
import traceback
import threading
from code import InteractiveConsole
if sys.version_info[0] == 3:
from io import StringIO
......@@ -113,3 +114,78 @@ class ReversePythonConnection(ReverseConnection):
buffer.close()
self.send(output)
return True
class DistantInteractiveConsole(InteractiveConsole):
def __init__(self, ipc):
InteractiveConsole.__init__(self, globals())
self.ipc = ipc
self.set_buffer()
def set_buffer(self):
self.out_buffer = StringIO()
sys.stdout = sys.stderr = self.out_buffer
def unset_buffer(self):
sys.stdout, sys.stderr = sys.__stdout__, sys.__stderr__
value = self.out_buffer.getvalue()
self.out_buffer.close()
return value
def raw_input(self, prompt=""):
output = self.unset_buffer()
# payload format: 'prompt' ? '\n' 'output'
self.ipc.send('\n'.join((prompt, output)))
cmd = self.ipc.recv()
self.set_buffer()
return cmd
class ReversePythonShell(threading.Thread, pyrasite.PyrasiteIPC):
"""A reverse Python shell that behaves like Python interactive interpreter.
"""
host = 'localhost'
port = 9001
reliable = True
def __init__(self, host=None, port=None):
super(ReversePythonShell, self).__init__()
def run(self):
try:
for res in socket.getaddrinfo(self.host, self.port,
socket.AF_UNSPEC, socket.SOCK_STREAM):
af, socktype, proto, canonname, sa = res
try:
self.sock = socket.socket(af, socktype, proto)
try:
self.sock.connect(sa)
except socket.error:
self.sock.close()
self.sock = None
continue
except socket.error:
self.sock = None
continue
break
if not self.sock:
raise Exception('pyrasite cannot establish reverse ' +
'connection to %s:%d' % (self.host, self.port))
DistantInteractiveConsole(self).interact()
except SystemExit:
pass
except:
traceback.print_exc(file=sys.__stderr__)
sys.stdout, sys.stderr = sys.__stdout__, sys.__stderr__
self.close()
""" This is kept in a separate file so that python2.4 never picks it up. """
import pyrasite
def context_manager_business(case):
# Check that the context manager injects ipc correctly.
with pyrasite.PyrasiteIPC(case.p.pid) as ipc:
assert ipc.cmd('print("mu")') == 'mu\n'
# Check that the context manager closes the ipc correctly.
try:
ipc.cmd('print("mu")')
assert False, "The connection was not closed."
except IOError as e:
assert "Bad file descriptor" in str(e)
......@@ -17,12 +17,12 @@
import os
import sys
import unittest
import subprocess
import pyrasite
from pyrasite.tests.utils import generate_program, run_program, stop_program, \
interpreters
interpreters, unittest
class TestCodeInjection(unittest.TestCase):
......@@ -47,8 +47,8 @@ class TestCodeInjection(unittest.TestCase):
os.unlink(program)
def test_many_payloads_into_program_with_many_threads(self):
program = generate_program(threads=50)
num_payloads = 50
program = generate_program(threads=25)
num_payloads = 25
try:
for exe in interpreters():
p = run_program(program, exe=exe)
......@@ -65,6 +65,21 @@ class TestCodeInjection(unittest.TestCase):
finally:
os.unlink(program)
def test_pyrasite_script(self):
program = generate_program()
try:
for exe in interpreters():
print("sys.executable = %s" % sys.executable)
print("injecting into %s" % exe)
p = run_program(program, exe=exe)
subprocess.call([sys.executable, 'pyrasite/main.py',
str(p.pid), 'pyrasite/payloads/helloworld.py'],
env={'PYTHONPATH': os.getcwd()})
stop_program(p)
stdout, stderr = p.communicate()
self.assert_output_contains(stdout, stderr, 'Hello World!')
finally:
os.unlink(program)
if __name__ == '__main__':
unittest.main()
......@@ -16,10 +16,34 @@
# Copyright (C) 2011, 2012 Red Hat, Inc.
import os
import unittest
import sys
import pyrasite
from pyrasite.tests.utils import run_program, generate_program, stop_program
from pyrasite.tests.utils import run_program, generate_program, stop_program, unittest
class TestIPCContextManager(unittest.TestCase):
def setUp(self):
self.prog = generate_program()
self.p = run_program(self.prog)
def tearDown(self):
stop_program(self.p)
def test_context_manager(self):
# Check that we're on a version of python that
# supports context managers
info = sys.version_info
major, minor = info[0], info[1]
if major <= 2 and minor <= 5:
self.skipTest("Context Managers not supported on Python<=2.5")
# Otherwise import a module which contains modern syntax.
# It really contains our test case, but we have pushed it out into
# another module so that python 2.4 never sees it.
import pyrasite.tests.context_manager_case
pyrasite.tests.context_manager_case.context_manager_business(self)
class TestIPC(unittest.TestCase):
......
......@@ -16,11 +16,17 @@
# Copyright (C) 2011-2012 Red Hat, Inc., Luke Macken <lmacken@redhat.com>
import os
import sys
import glob
import time
import textwrap
import tempfile
import subprocess
import unittest
if sys.version_info[0] == 2:
if sys.version_info[1] < 7:
import unittest2 as unittest
def generate_program(threads=1):
......@@ -30,21 +36,20 @@ def generate_program(threads=1):
import os, time, threading
running = True
pidfile = '/tmp/pyrasite_%d' % os.getpid()
open(pidfile, 'w').close()
def cpu_bound():
i = 2
y = 0
def fib(n):
return fib(n - 1) + fib(n - 2)
i = 0
while running:
y += fib(i)
i += 1
while os.path.exists(pidfile):
time.sleep(0.1)
""")
# CPU-bound threads
for t in range(threads):
script += "threading.Thread(target=cpu_bound).start()\n"
script += "open(pidfile, 'w').close()\n"
script += textwrap.dedent("""
while os.path.exists(pidfile):
time.sleep(0.1)
running = False
""")
tmp.write(script)
tmp.close()
return filename
......
#!/usr/bin/python
# This file is part of pyrasite.
#
# pyrasite is free software: you can redistribute it and/or modify
......@@ -30,8 +29,9 @@ import urwid
import urwid.raw_display
from meliae import loader
from os.path import join, abspath, dirname
from pyrasite.inspect import ObjectInspector
import pyrasite
class PyrasiteMemoryViewer(object):
......@@ -47,7 +47,7 @@ class PyrasiteMemoryViewer(object):
]
def __init__(self, pid, objects):
self.inspector = ObjectInspector(pid)
self.pid = pid
self.objects = objects
self.summary = objects.summarize()
......@@ -67,7 +67,7 @@ class PyrasiteMemoryViewer(object):
def display_object(self, w, state):
if state:
value = self.inspector.inspect(w.obj.max_address)
value = pyrasite.inspect(self.pid, w.obj.max_address)
self.object_output.set_text(value)
def get_object_buttons(self, group=[]):
......@@ -134,16 +134,19 @@ class PyrasiteMemoryViewer(object):
def main():
if len(sys.argv) != 3:
print "[ pyrasite memory viewer ]\n"
print "Usage: %s <pid> <objects.json>" % sys.argv[0]
print "\n pid - the running process id"
print " objects.json - the output of the dump-memory payload"
print
if len(sys.argv) != 2:
print("[ pyrasite memory viewer ]\n")
print("Usage: %s <pid> <objects.json>" % sys.argv[0])
print("\n pid - the running process id")
print("")
sys.exit(1)
pid = int(sys.argv[1])
filename = sys.argv[2]
payload = abspath(join(dirname(__file__), '..',
'payloads', 'dump_memory.py'))
pyrasite.inject(pid, payload)
filename = '/tmp/pyrasite-%d-objects.json' % pid
objects = loader.load(filename)
objects.compute_referrers()
......
......@@ -26,17 +26,50 @@ def shell():
print("Usage: pyrasite-shell <PID>")
sys.exit(1)
ipc = pyrasite.PyrasiteIPC(int(sys.argv[1]))
ipc = pyrasite.PyrasiteIPC(int(sys.argv[1]), 'ReversePythonShell')
ipc.connect()
print("pyrasite shell %s" % pyrasite.__version__)
print(ipc.cmd('import sys; print("Python " + sys.version + ' +
'" on " + sys.platform)'))
print("Pyrasite Shell %s" % pyrasite.__version__)
print("Connected to '%s'" % ipc.title)
prompt, payload = ipc.recv().split('\n', 1)
print(payload)
try:
import readline
except ImportError:
pass
# py3k compat
try:
input_ = raw_input
except NameError:
input_ = input
try:
while True:
print(ipc.cmd(raw_input('>>> ')))
try:
input_line = input_(prompt)
except EOFError:
input_line = 'exit()'
print('')
except KeyboardInterrupt:
input_line = 'None'
print('')
ipc.send(input_line)
payload = ipc.recv()
if payload is None:
break
prompt, payload = payload.split('\n', 1)
if payload != '':
print(payload)
except:
pass
print('')
raise
ipc.close()
print()
if __name__ == '__main__':
shell()
import sys
from setuptools import setup, find_packages
version = '2.0beta9'
try:
# These imports are not used, but make
# tests pass smoothly on python2.7
import multiprocessing
import logging
except Exception:
pass
version = '2.0'
f = open('README.rst')
long_description = f.read().split('split here')[1]
f.close()
requirements = []
if sys.version_info[0] == 3:
if sys.version_info[1] < 2:
requirements.append('argparse')
elif sys.version_info[0] == 2:
if sys.version_info[1] < 7:
requirements.append('argparse')
tests_require = ['nose']
if sys.version_info[0] == 2:
if sys.version_info[1] < 7:
tests_require.append('unittest2')
setup(name='pyrasite',
version=version,
description="Inject code into a running Python process",
......@@ -18,8 +40,8 @@ setup(name='pyrasite',
packages=find_packages(exclude=['ez_setup', 'examples', 'tests']),
include_package_data=True,
zip_safe=False,
install_requires=[],
tests_require=['nose'],
install_requires=requirements,
tests_require=tests_require,
test_suite='nose.collector',
entry_points="""
[console_scripts]
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment