Giter Site home page Giter Site logo

python-manhole's Introduction

Overview

docs Documentation Status
tests
GitHub Actions Build Status
Coverage Status Coverage Status
package
PyPI Package latest release PyPI Wheel Supported versions Supported implementations
Commits since latest release

Manhole is in-process service that will accept unix domain socket connections and present the stacktraces for all threads and an interactive prompt. It can either work as a python daemon thread waiting for connections at all times or a signal handler (stopping your application and waiting for a connection).

Access to the socket is restricted to the application's effective user id or root.

This is just like Twisted's manhole. It's simpler (no dependencies), it only runs on Unix domain sockets (in contrast to Twisted's manhole which can run on telnet or ssh) and it integrates well with various types of applications.

Documentation

http://python-manhole.readthedocs.org/en/latest/

Usage

Install it:

pip install manhole

You can put this in your django settings, wsgi app file, some module that's always imported early etc:

import manhole
manhole.install() # this will start the daemon thread

# and now you start your app, eg: server.serve_forever()

Now in a shell you can do either of these:

netcat -U /tmp/manhole-1234
socat - unix-connect:/tmp/manhole-1234
socat readline unix-connect:/tmp/manhole-1234

Socat with readline is best (history, editing etc). If your socat doesn't have readline try this.

Sample output:

$ nc -U /tmp/manhole-1234

Python 2.7.3 (default, Apr 10 2013, 06:20:15)
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> dir()
['__builtins__', 'dump_stacktraces', 'os', 'socket', 'sys', 'traceback']
>>> print 'foobar'
foobar

Alternative client

There's a new experimental manhole-cli bin since 1.1.0, that emulates socat:

usage: manhole-cli [-h] [-t TIMEOUT] [-1 | -2 | -s SIGNAL] PID

Connect to a manhole.

positional arguments:
  PID                   A numerical process id, or a path in the form:
                        /tmp/manhole-1234

optional arguments:
  -h, --help            show this help message and exit
  -t TIMEOUT, --timeout TIMEOUT
                        Timeout to use. Default: 1 seconds.
  -1, -USR1             Send USR1 (10) to the process before connecting.
  -2, -USR2             Send USR2 (12) to the process before connecting.
  -s SIGNAL, --signal SIGNAL
                        Send the given SIGNAL to the process before
                        connecting.

Features

  • Uses unix domain sockets, only root or same effective user can connect.
  • Can run the connection in a thread or in a signal handler (see oneshot_on option).
  • Can start the thread listening for connections from a signal handler (see activate_on option)
  • Compatible with apps that fork, reinstalls the Manhole thread after fork - had to monkeypatch os.fork/os.forkpty for this.
  • Compatible with gevent and eventlet with some limitations - you need to either:

    • Use oneshot_on, or
    • Disable thread monkeypatching (eg: gevent.monkey.patch_all(thread=False), eventlet.monkey_patch(thread=False)

    Note: on eventlet you might need to setup the hub first to prevent circular import problems:

    python

    import eventlet eventlet.hubs.get_hub() # do this first eventlet.monkey_patch(thread=False)

  • The thread is compatible with apps that use signalfd (will mask all signals for the Manhole threads).

Options

manhole.install(
    verbose=True,
    verbose_destination=2,
    patch_fork=True,
    activate_on=None,
    oneshot_on=None,
    sigmask=manhole.ALL_SIGNALS,
    socket_path=None,
    reinstall_delay=0.5,
    locals=None,
    strict=True,
)
  • verbose - Set it to False to squelch the logging.
  • verbose_destination - Destination for verbose messages. Set it to a file descriptor or handle. Default is unbuffered stderr (stderr 2 file descriptor).
  • patch_fork - Set it to False if you don't want your os.fork and os.forkpy monkeypatched
  • activate_on - Set to "USR1", "USR2" or some other signal name, or a number if you want the Manhole thread to start when this signal is sent. This is desirable in case you don't want the thread active all the time.
  • thread - Set to True to start the always-on ManholeThread. Default: True. Automatically switched to False if oneshot_on or activate_on are used.
  • oneshot_on - Set to "USR1", "USR2" or some other signal name, or a number if you want the Manhole to listen for connection in the signal handler. This is desireable in case you don't want threads at all.
  • sigmask - Will set the signal mask to the given list (using signalfd.sigprocmask). No action is done if signalfd is not importable. NOTE: This is done so that the Manhole thread doesn't steal any signals; Normally that is fine because Python will force all the signal handling to be run in the main thread but signalfd doesn't.
  • socket_path - Use a specific path for the unix domain socket (instead of /tmp/manhole-<pid>). This disables patch_fork as children cannot reuse the same path.
  • reinstall_delay - Delay the unix domain socket creation reinstall_delay seconds. This alleviates cleanup failures when using fork+exec patterns.
  • locals - Names to add to manhole interactive shell locals.
  • daemon_connection - The connection thread is daemonic (dies on app exit). Default: False.
  • redirect_stderr - Redirect output from stderr to manhole console. Default: True.
  • strict - If True then AlreadyInstalled will be raised when attempting to install manhole twice. Default: True.

Environment variable installation

Manhole can be installed via the PYTHONMANHOLE environment variable.

This:

PYTHONMANHOLE='' python yourapp.py

Is equivalent to having this in yourapp.py:

import manhole
manhole.install()

Any extra text in the environment variable is passed to manhole.install(). Example:

PYTHONMANHOLE='oneshot_on="USR2"' python yourapp.py

What happens when you actually connect to the socket

  1. Credentials are checked (if it's same user or root)
  2. sys.__std*__/sys.std* are redirected to the UDS
  3. Stacktraces for each thread are written to the UDS
  4. REPL is started so you can fiddle with the process

Known issues

  • Using threads and file handle (not raw file descriptor) verbose_destination can cause deadlocks. See bug reports: PyPy and Python 3.4.

SIGTERM and socket cleanup

By default Python doesn't call the atexit callbacks with the default SIGTERM handling. This makes manhole leave stray socket files around. If this is undesirable you should install a custom SIGTERM handler so atexit is properly invoked.

Example:

import signal
import sys

def handle_sigterm(signo, frame):
    sys.exit(128 + signo)  # this will raise SystemExit and cause atexit to be called

signal.signal(signal.SIGTERM, handle_sigterm)

Using Manhole with uWSGI

Because uWSGI overrides signal handling Manhole is a bit more tricky to setup. One way is to use "uWSGI signals" (not the POSIX signals) and have the workers check a file for the pid you want to open the Manhole in.

Stick something this in your WSGI application file:

python

from __future__ import print_function import sys import os import manhole

stack_dump_file = '/tmp/manhole-pid' uwsgi_signal_number = 17

try:

import uwsgi

if not os.path.exists(stack_dump_file):

open(stack_dump_file, 'w')

def open_manhole(dummy_signum):
with open(stack_dump_file, 'r') as fh:

pid = fh.read().strip() if pid == str(os.getpid()): inst = manhole.install(strict=False, thread=False) inst.handle_oneshot(dummy_signum, dummy_signum)

uwsgi.register_signal(uwsgi_signal_number, 'workers', open_manhole) uwsgi.add_file_monitor(uwsgi_signal_number, stack_dump_file)

print("Listening for stack mahole requests via %r" % (stack_dump_file,), file=sys.stderr)

except ImportError:

print("Not running under uwsgi; unable to configure manhole trigger", file=sys.stderr)

except IOError:

print("IOError creating manhole trigger %r" % (stack_dump_file,), file=sys.stderr)

# somewhere bellow you'd have something like from django.core.wsgi import get_wsgi_application application = get_wsgi_application() # or def application(environ, start_response): start_response('200 OK', [('Content-Type', 'text/plain'), ('Content-Length', '2')]) yield b'OK'

To open the Manhole just run echo 1234 > /tmp/manhole-pid and then manhole-cli 1234.

Requirements

OS

Linux, OS X

Runtime

Python 2.7, 3.4, 3.5, 3.6 or PyPy

Similar projects

python-manhole's People

Contributors

akheron avatar anton-ryzhov avatar bitdeli-chef avatar honnix avatar ionelmc avatar jcea avatar jgarte avatar nirs avatar razzmatazz avatar timgates42 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

python-manhole's Issues

new release

The last tagged release is very old, could you tag a new release?
Even a minor release is fine.

Tests too slow, need much faster feedback

Even after #13, tests are too slow.

$ tox -e 2.7 -- py.test -v --durations=10
2.7 develop-inst-nodeps: /home/nsoffer/src/python-manhole
2.7 runtests: PYTHONHASHSEED='4080427460'
2.7 runtests: commands[0] | py.test -v --durations=10
============================================================================= test session starts ==============================================================================
platform linux2 -- Python 2.7.5 -- py-1.4.23 -- pytest-2.6.1 -- /home/nsoffer/src/python-manhole/.tox/2.7/bin/python2.7
plugins: cov, capturelog
collected 14 items

tests/test_manhole.py::test_simple PASSED
tests/test_manhole.py::test_fork_exec PASSED
tests/test_manhole.py::test_socket_path PASSED
tests/test_manhole.py::test_socket_path_with_fork PASSED
tests/test_manhole.py::test_exit_with_grace PASSED
tests/test_manhole.py::test_with_fork PASSED
tests/test_manhole.py::test_with_forkpty PASSED
tests/test_manhole.py::test_auth_fail PASSED
tests/test_manhole.py::test_activate_on_usr2 PASSED
tests/test_manhole.py::test_activate_on_with_oneshot_on PASSED
tests/test_manhole.py::test_oneshot_on_usr2 PASSED
tests/test_manhole.py::test_fail_to_cry PASSED
tests/test_manhole.py::test_oneshot_on_usr2_error PASSED
tests/test_manhole.py::test_interrupt_on_accept PASSED

========================================================================== slowest 10 test durations ===========================================================================
11.11s call tests/test_manhole.py::test_oneshot_on_usr2_error
10.81s call tests/test_manhole.py::test_oneshot_on_usr2
10.70s call tests/test_manhole.py::test_activate_on_usr2
4.39s call tests/test_manhole.py::test_simple
2.62s call tests/test_manhole.py::test_with_fork
2.57s call tests/test_manhole.py::test_with_forkpty
2.56s call tests/test_manhole.py::test_socket_path_with_fork
2.12s call tests/test_manhole.py::test_interrupt_on_accept
1.16s call tests/test_manhole.py::test_fork_exec
0.61s call tests/test_manhole.py::test_exit_with_grace
==================================================================== 14 passed, 2 warnings in 49.98 seconds ====================================================================

Tests too complicated, separate to test module and helper module

Having both the tests and the code that should run in another process in the same module make the module too complicated, hard to work with, and slower to run.

Plan:

  1. Move the code for running another process to helper.py
  2. Create small function for each fixture
  3. Run one function for each invocation, getting the function name from the command line

This should also speed up the test a little bit, as we don't need to import so many unused module on each invocation.

manhole socket not removed when terminating a process

Although manhole register the removal function using atexit module, the removal function is not run when sending the process SIGTERM.

To reproduce:

  1. Create a script::

    import manhole
    import time
    manhole.install(socket_path='exit.manhole')
    time.sleep(30)

  2. Run the script in the backround

    python exit.py &

  3. And terminate it

    kill

The removal function is not called, and manhole socket is not removed.

Tested on Fedora 19.

This seems faulty implementation of atexit module.

Workarounds:

  • terminate the process using SIGINT
  • manually remove the socket in the application - easy when using socket_path option

Unwanted manhole installed in child process during subprocess.Popen

Since patch_fork defualts to True, after forking, a new manhole is installed in the child process. Since starting new processes includes fork and execv, an unwanted manhole is installed when starting a process using the subprocess module.

To reproduce:

  1. Create this script
import os
import subprocess
import manhole

manhole.install()
p = subprocess.Popen(['sleep', '1'])
path = '/tmp/manhole-%d' % p.pid
assert not os.path.exists(path)
  1. Run it in a loop
$ while true; do python bug.py || break; done
Manhole: Patched <built-in function fork> and <built-in function forkpty>.
Manhole: Manhole UDS path: /tmp/manhole-29643
Manhole: Fork detected. Reinstalling Manhole.
Manhole: Waiting for new connection (in pid:29643) ...
Manhole: Patched <built-in function fork> and <built-in function forkpty>.
Manhole: Manhole UDS path: /tmp/manhole-29649
Manhole: Waiting for new connection (in pid:29649) ...
Manhole: Fork detected. Reinstalling Manhole.
Manhole: Patched <built-in function fork> and <built-in function forkpty>.
Manhole: Manhole UDS path: /tmp/manhole-29655
Manhole: Waiting for new connection (in pid:29655) ...
Manhole: Fork detected. Reinstalling Manhole.
Manhole: Patched <built-in function fork> and <built-in function forkpty>.
Manhole: Manhole UDS path: /tmp/manhole-29661
Manhole: Waiting for new connection (in pid:29661) ...
Manhole: Fork detected. Reinstalling Manhole.
Manhole: Patched <built-in function fork> and <built-in function forkpty>.
Manhole: Fork detected. Reinstalling Manhole.
Manhole: Manhole UDS path: /tmp/manhole-29667
Manhole: Waiting for new connection (in pid:29667) ...
Manhole: Patched <built-in function fork> and <built-in function forkpty>.
Manhole: Manhole UDS path: /tmp/manhole-29673
Manhole: Waiting for new connection (in pid:29673) ...
Manhole: Fork detected. Reinstalling Manhole.
Manhole: Patched <built-in function fork> and <built-in function forkpty>.
Manhole: Manhole UDS path: /tmp/manhole-29679
Manhole: Waiting for new connection (in pid:29679) ...
Manhole: Fork detected. Reinstalling Manhole.
Manhole: Patched <built-in function fork> and <built-in function forkpty>.
Manhole: Manhole UDS path: /tmp/manhole-29685
Manhole: Waiting for new connection (in pid:29685) ...
Manhole: Fork detected. Reinstalling Manhole.
Traceback (most recent call last):
  File "bug.py", line 10, in <module>
    assert not os.path.exists(path)
AssertionError

"manhole-cli" is eating 6% of my CPU being just idle

Manhole 1.5.0. Ubuntu 16.04.

Steps to reproduce:

  1. Use "manhole-cli" to connect to a process.
  2. Do "top" in another terminal. Observe "manhole-cli" eating a lot of CPU doing nothing. In my case, 6% of my CPU is being burned for no reason.
  3. "strace -f -p PID" shows a flood of this:
[...]
[pid 14394] poll([{fd=4, events=POLLIN|POLLPRI|POLLERR|POLLHUP}, {fd=3, events=POLLIN|POLLPRI|POLLERR|POLLHUP}], 2, 1) = 0 (Timeout)
[pid 14394] poll([{fd=4, events=POLLIN|POLLPRI|POLLERR|POLLHUP}, {fd=3, events=POLLIN|POLLPRI|POLLERR|POLLHUP}], 2, 1) = 0 (Timeout)
[pid 14394] poll([{fd=4, events=POLLIN|POLLPRI|POLLERR|POLLHUP}, {fd=3, events=POLLIN|POLLPRI|POLLERR|POLLHUP}], 2, 1) = 0 (Timeout)
[...]

installing manhole causes sys.getfilesystemencoding() return None

Not sure how this could happen.

If I start python with manhole env set:

root@39d6880c52eb:/# python
Python 2.7.14 (default, Jun  5 2018, 08:26:18)
[GCC 4.8.4] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> Manhole[41:1528398043.5660]: Manhole UDS path: /tmp/manhole-41
Manhole[41:1528398043.5662]: Waiting for new connection (in pid:41) ...
import sys
>>> sys.getfilesystemencoding()
>>>

If I unset the env first:

root@39d6880c52eb:/# unset PYTHONMANHOLE
root@39d6880c52eb:/# python
Python 2.7.14 (default, Jun  5 2018, 08:26:18)
[GCC 4.8.4] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.getfilesystemencoding()
'ANSI_X3.4-1968'
>>>

User defined locals in manhole environment

Typically, when you add a manhole to existing application, the application was not designed for this, so finding stuff you need from the manhole is hard or even impossible.

For example, in vdsm, it will be hard to access the most important object - irs - because it is defined as a local variable in a function of the __main__ module:
https://github.com/oVirt/vdsm/blob/master/vdsm/vdsm#L54

This can be solved easily by adding locals argument:

manhole.install(locals={'irs': irs})

So in the manhole, you can access irs as local variable.

>>> irs.getDeviceList(...)

Manhole already define some locals when running the console. The user locals and manhole locals should be probably merged.

If user add locals with the same names as manhole locals, user locals should override manhole locals. Logging a warning in this case may be helpful.

1.8.0: pytest is failing and deprecacion warnings

I'm trying to package your module as an rpm package. So I'm using the typical PEP517 based build, install and test cycle used on building packages from non-root account.

  • python3 -sBm build -w
  • install .whl file in </install/prefix>
  • run pytest with PYTHONPATH pointing to sitearch and sitelib inside </install/prefix>

Here is pytest output with some deprecation warnings as well:

Here is pytest output:
+ PYTHONPATH=/home/tkloczko/rpmbuild/BUILDROOT/python-manhole-1.8.0-2.fc35.x86_64/usr/lib64/python3.8/site-packages:/home/tkloczko/rpmbuild/BUILDROOT/python-manhole-1.8.0-2.fc35.x86_64/usr/lib/python3.8/site-packages
+ /usr/bin/pytest -ra
=========================================================================== test session starts ============================================================================
platform linux -- Python 3.8.12, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /home/tkloczko/rpmbuild/BUILD/python-manhole-1.8.0, configfile: setup.cfg, testpaths: tests
plugins: asyncio-0.16.0, steps-1.8.0, harvest-1.10.3
collected 45 items

tests/test_manhole.py ....FF..FF...............ss.....Fs.F                                                                                                           [ 80%]
tests/test_manhole_cli.py .........                                                                                                                                  [100%]

================================================================================= FAILURES =================================================================================
____________________________________________________________________ test_connection_handler_exec[str] _____________________________________________________________________
tests/test_manhole.py:105: in test_connection_handler_exec
    wait_for_strings(proc.read, TIMEOUT, 'TETE')
/usr/lib/python3.8/site-packages/process_tests.py:247: in wait_for_strings
    raise AssertionError("Waited %0.2fsecs but %s did not appear in output in the given order !" % (
E   AssertionError: Waited 10.00secs but ['TETE'] did not appear in output in the given order !
--------------------------------------------------------------------------- Captured stdout call ---------------------------------------------------------------------------
*********** OUTPUT ***********

******************************
*********** OUTPUT ***********
Manhole[2975552:1641271765.2248]: Started ManholeConnectionThread thread. Checking credentials ...
Manhole[2975552:1641271765.2248]: Accepted connection on fd:4 from PID:2975520 UID:1001 GID:1001
Manhole[2975552:1641271765.2748]: Running: "print('FOOBAR')\n".
Manhole[2975552:1641271765.3249]: Running: 'tete()\n'.

******************************
____________________________________________________________________ test_connection_handler_exec[func] ____________________________________________________________________
tests/test_manhole.py:105: in test_connection_handler_exec
    wait_for_strings(proc.read, TIMEOUT, 'TETE')
/usr/lib/python3.8/site-packages/process_tests.py:247: in wait_for_strings
    raise AssertionError("Waited %0.2fsecs but %s did not appear in output in the given order !" % (
E   AssertionError: Waited 10.00secs but ['TETE'] did not appear in output in the given order !
--------------------------------------------------------------------------- Captured stdout call ---------------------------------------------------------------------------
*********** OUTPUT ***********

******************************
*********** OUTPUT ***********
Manhole[2975588:1641271776.7484]: Started ManholeConnectionThread thread. Checking credentials ...
Manhole[2975588:1641271776.7485]: Accepted connection on fd:4 from PID:2975520 UID:1001 GID:1001
Manhole[2975588:1641271776.7987]: Running: "print('FOOBAR')\n".
Manhole[2975588:1641271776.8489]: Running: 'tete()\n'.

******************************
__________________________________________________________________________ test_daemon_connection __________________________________________________________________________
tests/test_manhole.py:143: in test_daemon_connection
    pytest.raises((socket.error, OSError), assert_manhole_running, proc, uds_path, extra=terminate_and_read)
tests/test_manhole.py:56: in assert_manhole_running
    extra(client)
tests/test_manhole.py:137: in terminate_and_read
    wait_for_strings(proc.read, TIMEOUT, 'Died with KeyboardInterrupt', 'DIED.')
/usr/lib/python3.8/site-packages/process_tests.py:247: in wait_for_strings
    raise AssertionError("Waited %0.2fsecs but %s did not appear in output in the given order !" % (
E   AssertionError: Waited 10.00secs but ['DIED.', 'Died with KeyboardInterrupt'] did not appear in output in the given order !
--------------------------------------------------------------------------- Captured stdout call ---------------------------------------------------------------------------
*********** OUTPUT ***********

######### ProcessID=2975625, ThreadID=140147246274112 #########
File: "/usr/lib64/python3.8/threading.py", line 890, in _bootstrap
  self._bootstrap_inner()
File: "/usr/lib64/python3.8/threading.py", line 932, in _bootstrap_inner
  self.run()
File: "/home/tkloczko/rpmbuild/BUILDROOT/python-manhole-1.8.0-2.fc35.x86_64/usr/lib/python3.8/site-packages/manhole/__init__.py", line 238, in run
  self.connection_handler(self.client)
File: "/home/tkloczko/rpmbuild/BUILDROOT/python-manhole-1.8.0-2.fc35.x86_64/usr/lib/python3.8/site-packages/manhole/__init__.py", line 305, in handle_connection_repl
  handle_repl(_MANHOLE.locals)
File: "/home/tkloczko/rpmbuild/BUILDROOT/python-manhole-1.8.0-2.fc35.x86_64/usr/lib/python3.8/site-packages/manhole/__init__.py", line 354, in handle_repl
  dump_stacktraces()
File: "/home/tkloczko/rpmbuild/BUILDROOT/python-manhole-1.8.0-2.fc35.x86_64/usr/lib/python3.8/site-packages/manhole/__init__.py", line 633, in dump_stacktraces
  for filename, lineno, name, line in traceback.extract_stack(stack):

######### ProcessID=2975625, ThreadID=140147254666816 #########
File: "/usr/lib64/python3.8/threading.py", line 890, in _bootstrap
  self._bootstrap_inner()
File: "/usr/lib64/python3.8/threading.py", line 932, in _bootstrap_inner
  self.run()
File: "/home/tkloczko/rpmbuild/BUILDROOT/python-manhole-1.8.0-2.fc35.x86_64/usr/lib/python3.8/site-packages/manhole/__init__.py", line 207, in run
  client.join()
File: "/usr/lib64/python3.8/threading.py", line 1011, in join
  self._wait_for_tstate_lock()
File: "/usr/lib64/python3.8/threading.py", line 1027, in _wait_for_tstate_lock
  elif lock.acquire(block, timeout):

######### ProcessID=2975625, ThreadID=140147484260160 #########
File: "/home/tkloczko/rpmbuild/BUILD/python-manhole-1.8.0/tests/helper.py", line 215, in <module>
  time.sleep(TIMEOUT)
#############################################


Python 3.8.12 (default, Dec 17 2021, 08:35:49)
[GCC 11.2.1 20211203 (Red Hat 11.2.1-7)] on linux
Type "help", "copyright", "credits" or "license" for more information.
(ManholeConsole)
>>> FOOBAR
>>>
******************************
*********** OUTPUT ***********
Manhole[2975625:1641271789.7462]: Patched <built-in function fork> and <built-in function forkpty>.
Manhole[2975625:1641271789.7463]: Manhole UDS path: /tmp/manhole-2975625
Manhole[2975625:1641271789.7463]: Waiting for new connection (in pid:2975625) ...
Manhole[2975625:1641271789.7832]: Started ManholeConnectionThread thread. Checking credentials ...
Manhole[2975625:1641271789.7832]: Accepted connection on fd:4 from PID:2975520 UID:1001 GID:1001
Fatal Python error: could not acquire lock for <_io.BufferedReader name=4> at interpreter shutdown, possibly due to daemon threads
Python runtime state: finalizing (tstate=0x55e9be27f720)

Thread 0x00007f7692d49640 (most recent call first):
  File "/usr/lib64/python3.8/socket.py", line 669 in readinto
  File "/usr/lib64/python3.8/code.py", line 274 in raw_input
  File "/usr/lib64/python3.8/code.py", line 227 in interact
  File "/home/tkloczko/rpmbuild/BUILDROOT/python-manhole-1.8.0-2.fc35.x86_64/usr/lib/python3.8/site-packages/manhole/__init__.py", line 365 in handle_repl
  File "/home/tkloczko/rpmbuild/BUILDROOT/python-manhole-1.8.0-2.fc35.x86_64/usr/lib/python3.8/site-packages/manhole/__init__.py", line 305 in handle_connection_repl
  File "/home/tkloczko/rpmbuild/BUILDROOT/python-manhole-1.8.0-2.fc35.x86_64/usr/lib/python3.8/site-packages/manhole/__init__.py", line 238 in run
  File "/usr/lib64/python3.8/threading.py", line 932 in _bootstrap_inner
  File "/usr/lib64/python3.8/threading.py", line 890 in _bootstrap

Thread 0x00007f769354a640 (most recent call first):
  File "/usr/lib64/python3.8/threading.py", line 1027 in _wait_for_tstate_lock
  File "/usr/lib64/python3.8/threading.py", line 1011 in join
  File "/home/tkloczko/rpmbuild/BUILDROOT/python-manhole-1.8.0-2.fc35.x86_64/usr/lib/python3.8/site-packages/manhole/__init__.py", line 207 in run
  File "/usr/lib64/python3.8/threading.py", line 932 in _bootstrap_inner
  File "/usr/lib64/python3.8/threading.py", line 890 in _bootstrap

Current thread 0x00007f76a103f740 (most recent call first):
<no Python frame>

******************************
________________________________________________________________________ test_non_daemon_connection ________________________________________________________________________
tests/test_manhole.py:164: in test_non_daemon_connection
    assert_manhole_running(proc, uds_path, extra=terminate_and_read, oneshot=True)
tests/test_manhole.py:56: in assert_manhole_running
    extra(client)
tests/test_manhole.py:158: in terminate_and_read
    wait_for_strings(proc.read, TIMEOUT, 'Died with KeyboardInterrupt')
/usr/lib/python3.8/site-packages/process_tests.py:247: in wait_for_strings
    raise AssertionError("Waited %0.2fsecs but %s did not appear in output in the given order !" % (
E   AssertionError: Waited 10.00secs but ['Died with KeyboardInterrupt'] did not appear in output in the given order !
--------------------------------------------------------------------------- Captured stdout call ---------------------------------------------------------------------------
*********** OUTPUT ***********

######### ProcessID=2975649, ThreadID=140492733548096 #########
File: "/usr/lib64/python3.8/threading.py", line 890, in _bootstrap
  self._bootstrap_inner()
File: "/usr/lib64/python3.8/threading.py", line 932, in _bootstrap_inner
  self.run()
File: "/home/tkloczko/rpmbuild/BUILDROOT/python-manhole-1.8.0-2.fc35.x86_64/usr/lib/python3.8/site-packages/manhole/__init__.py", line 238, in run
  self.connection_handler(self.client)
File: "/home/tkloczko/rpmbuild/BUILDROOT/python-manhole-1.8.0-2.fc35.x86_64/usr/lib/python3.8/site-packages/manhole/__init__.py", line 305, in handle_connection_repl
  handle_repl(_MANHOLE.locals)
File: "/home/tkloczko/rpmbuild/BUILDROOT/python-manhole-1.8.0-2.fc35.x86_64/usr/lib/python3.8/site-packages/manhole/__init__.py", line 354, in handle_repl
  dump_stacktraces()
File: "/home/tkloczko/rpmbuild/BUILDROOT/python-manhole-1.8.0-2.fc35.x86_64/usr/lib/python3.8/site-packages/manhole/__init__.py", line 633, in dump_stacktraces
  for filename, lineno, name, line in traceback.extract_stack(stack):

######### ProcessID=2975649, ThreadID=140492741940800 #########
File: "/usr/lib64/python3.8/threading.py", line 890, in _bootstrap
  self._bootstrap_inner()
File: "/usr/lib64/python3.8/threading.py", line 932, in _bootstrap_inner
  self.run()
File: "/home/tkloczko/rpmbuild/BUILDROOT/python-manhole-1.8.0-2.fc35.x86_64/usr/lib/python3.8/site-packages/manhole/__init__.py", line 207, in run
  client.join()
File: "/usr/lib64/python3.8/threading.py", line 857, in start
  self._started.wait()
File: "/usr/lib64/python3.8/threading.py", line 559, in wait
  return signaled
File: "/usr/lib64/python3.8/threading.py", line 309, in wait
  return gotit

######### ProcessID=2975649, ThreadID=140492971534144 #########
File: "/home/tkloczko/rpmbuild/BUILD/python-manhole-1.8.0/tests/helper.py", line 244, in <module>
  time.sleep(0.3)  # give the manhole a bit enough time to start
#############################################


Python 3.8.12 (default, Dec 17 2021, 08:35:49)
[GCC 11.2.1 20211203 (Red Hat 11.2.1-7)] on linux
Type "help", "copyright", "credits" or "license" for more information.
(ManholeConsole)
>>> FOOBAR
>>>
******************************
*********** OUTPUT ***********
Manhole[2975649:1641271800.0385]: Patched <built-in function fork> and <built-in function forkpty>.
Manhole[2975649:1641271800.0385]: Manhole UDS path: /tmp/manhole-2975649
Manhole[2975649:1641271800.0386]: Waiting for new connection (in pid:2975649) ...
Manhole[2975649:1641271800.0752]: Started ManholeConnectionThread thread. Checking credentials ...
Manhole[2975649:1641271800.0752]: Accepted connection on fd:4 from PID:2975520 UID:1001 GID:1001

******************************
_____________________________________________________________________ test_environ_variable_activation _____________________________________________________________________
tests/test_manhole.py:493: in test_environ_variable_activation
    wait_for_strings(proc.read, TIMEOUT,
/usr/lib/python3.8/site-packages/process_tests.py:247: in wait_for_strings
    raise AssertionError("Waited %0.2fsecs but %s did not appear in output in the given order !" % (
E   AssertionError: Waited 10.00secs but ['Not patching os.fork and os.forkpty. Oneshot activation is done by signal'] did not appear in output in the given order !
--------------------------------------------------------------------------- Captured stdout call ---------------------------------------------------------------------------
*********** OUTPUT ***********

******************************
________________________________________________________________________________ test_uwsgi ________________________________________________________________________________
tests/test_manhole.py:529: in test_uwsgi
    with TestProcess(
/usr/lib/python3.8/site-packages/process_tests.py:127: in __init__
    self.proc = subprocess.Popen(
/usr/lib64/python3.8/subprocess.py:858: in __init__
    self._execute_child(args, executable, preexec_fn, close_fds,
/usr/lib64/python3.8/subprocess.py:1704: in _execute_child
    raise child_exception_type(errno_num, err_msg, err_filename)
E   FileNotFoundError: [Errno 2] No such file or directory: 'uwsgi'
============================================================================= warnings summary =============================================================================
../../../../../usr/lib/python3.8/site-packages/_pytest/config/__init__.py:1183
  /usr/lib/python3.8/site-packages/_pytest/config/__init__.py:1183: PytestDeprecationWarning: The --strict option is deprecated, use --strict-markers instead.
    self.issue_config_time_warning(

tests/test_manhole.py:3
  /home/tkloczko/rpmbuild/BUILD/python-manhole-1.8.0/tests/test_manhole.py:3: DeprecationWarning: the imp module is deprecated in favour of importlib; see the module's documentation for alternative uses
    import imp

-- Docs: https://docs.pytest.org/en/stable/warnings.html
========================================================================= short test summary info ==========================================================================
SKIPPED [1] tests/test_manhole.py:402: condition: not is_module_available("signalfd")
SKIPPED [1] tests/test_manhole.py:413: condition: not is_module_available("signalfd")
SKIPPED [1] tests/test_manhole.py:502: condition: not is_module_available("signalfd")
FAILED tests/test_manhole.py::test_connection_handler_exec[str] - AssertionError: Waited 10.00secs but ['TETE'] did not appear in output in the given order !
FAILED tests/test_manhole.py::test_connection_handler_exec[func] - AssertionError: Waited 10.00secs but ['TETE'] did not appear in output in the given order !
FAILED tests/test_manhole.py::test_daemon_connection - AssertionError: Waited 10.00secs but ['DIED.', 'Died with KeyboardInterrupt'] did not appear in output in the give...
FAILED tests/test_manhole.py::test_non_daemon_connection - AssertionError: Waited 10.00secs but ['Died with KeyboardInterrupt'] did not appear in output in the given ord...
FAILED tests/test_manhole.py::test_environ_variable_activation - AssertionError: Waited 10.00secs but ['Not patching os.fork and os.forkpty. Oneshot activation is done b...
FAILED tests/test_manhole.py::test_uwsgi - FileNotFoundError: [Errno 2] No such file or directory: 'uwsgi'
===================================================== 6 failed, 36 passed, 3 skipped, 2 warnings in 157.19s (0:02:37) ======================================================

"manhole-cli" can NOT connect when we use "socket_path" in the server

"manhole-cli" requires a PID to connect. This doesn't work when the manhole server uses a specified name. "manhole-cli" REQUIRES the parameter to be a number or "/tmp/manhole-NUMBER". Also, "NUMBER" is used to send signals.

So, in order to use "manhole-cli":

  1. We can not use arbitrary names in the unix socket name.
  2. The unix socket MUST be in "/tmp/".
  3. If we relax the regexp in "manhole-cli" to be able to use arbitrary unix socket names, we can not send signals to the process.

Stress test failure: test_fork_exec on pypy-signalfd-nocover

Seems this keeps failing:

GLOB sdist-make: /home/ionel/osp/python-manhole/setup.py
pypy-signalfd-nocover inst-nodeps: /home/ionel/osp/python-manhole/.tox/dist/manhole-0.6.2.zip
pypy-signalfd-nocover runtests: PYTHONHASHSEED='572890346'
pypy-signalfd-nocover runtests: commands[0] | py.test -vv -k test_fork_exec
============================================================================================================ test session starts ============================================================================================================
platform linux2 -- Python 2.7.6[pypy-2.3.0-final] -- py-1.4.25 -- pytest-2.6.3 -- /home/ionel/osp/python-manhole/.tox/pypy-signalfd-nocover/bin/pypy
plugins: cov, capturelog
collected 27 items

tests/test_manhole.py::test_fork_exec FAILED

================================================================================================================= FAILURES ==================================================================================================================
______________________________________________________________________________________________________________ test_fork_exec _______________________________________________________________________________________________________________
tests/test_manhole.py:137: in test_fork_exec
    wait_for_strings(proc.read, TIMEOUT, 'SUCCESS')
.tox/pypy-signalfd-nocover/site-packages/process_tests.py:220: in wait_for_strings
    seconds, strings
E   AssertionError: Waited 10.00secs but ('SUCCESS',) did not appear in output in the given order !
----------------------------------------------------------------------------------------------------------- Captured stdout call ------------------------------------------------------------------------------------------------------------
*********** OUTPUT ***********
Manhole[1411926750.8092]: Patched <built-in function fork> and <built-in function forkpty>.
Manhole[1411926750.8094]: Manhole UDS path: /tmp/manhole-12933
Manhole[1411926750.8099]: Waiting for new connection (in pid:12933) ...

******************************
========================================================================================================== short test summary info ==========================================================================================================
FAIL tests/test_manhole.py::test_fork_exec
================================================================================================= 26 tests deselected by '-ktest_fork_exec' =================================================================================================
=========================================================================================== 1 failed, 26 deselected, 2 warnings in 11.29 seconds ============================================================================================
In atexit handler.
ERROR: InvocationError: '/home/ionel/osp/python-manhole/.tox/pypy-signalfd-nocover/bin/py.test -vv -k test_fork_exec'
__________________________________________________________________________________________________________________ summary __________________________________________________________________________________________________________________
ERROR:   pypy-signalfd-nocover: commands failed

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.