Skip to content
This repository was archived by the owner on Jan 15, 2026. It is now read-only.

android: Fix adb background command PID detection - #581

Closed
marcbonnici wants to merge 2 commits into
ARM-software:masterfrom
marcbonnici:adb_bg_fix
Closed

marcbonnici wants to merge 2 commits into
ARM-software:masterfrom
marcbonnici:adb_bg_fix

Conversation

@marcbonnici

Copy link
Copy Markdown
Collaborator

Sometimes the reported PID for a background command would be reported
incorrectly. This was due to an assumption that the lowest PID
was always the command we were looking for, however this is not always
true and PID's could be recycled resulting in the PID of the search
command being reported instead.

To avoid this, filter the reported processes on the the grep command
itself to eliminate matching processes in the system and improve
detection of the PID of the desired background process.

Comment thread devlib/utils/android.py Outdated
# Handle the case where the command has already exited otherwise
# we should only be left with our desired command
if not pids:
pid = None

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we return None or something else here to indicate an invalid PID? Perhaps 0 to be type consistent?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my experience, returning None or a made up value rather than raising does a lot more harm than good, since anything that actually needs the PID will not work with the fake value.

I guess we can end up in this situation if the process does not use stdin/stout/stderr and just exits immediately. On the ssh paramiko connection, PID detection is done by adding a print $$ before exec-ing the command, which is maybe the way to go.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my experience, returning None or a made up value rather than raising does a lot more harm than good, since anything that actually needs the PID will not work with the fake value.

That's a fair point. My concern over explicitly raising an error is that a usecase I can see for backgrounding a command is that you just want it to execute and don't care if its a short or long running task. In the case that it is short running and we start raising errors this might break existing flows that don't care about the PID.

On the ssh paramiko connection, PID detection is done by adding a print $$ before exec-ing the command, which is maybe the way to go.

That seems like a good idea, I'll try switching to that mechanism so that we can try and avoid this issue.

@douglas-raillard-arm douglas-raillard-arm Feb 24, 2022 •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the case that it is short running and we start raising errors this might break existing flows that don't care about the PID.

I guess exceptions would have to be handled inside devlib mostly, e.g. when getting the PID to kill the command (although I think we would not do anything if the command is already finished). In any case, having a reliable PID would be a better if possible at all. It might complicate things in case of stream redirections though.

EDIT: we collect the PID no matter what, so it would indeed create issues if we raised exceptions and would make short commands unusable (at least in the current code organization)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed I think getting the reliable PID detection is the best way forward here. I've updated the PR with an attempt to align with the mechanism the SSH connection uses. From what I can tell the stream redirection was actually already taking place in this connection as well however the redirect_streams function was missing the modification to the returned stream. Please let me know if these changes look reasonable to you.

@marcbonnici marcbonnici mentioned this pull request Feb 22, 2022
@douglas-raillard-arm

Copy link
Copy Markdown
Collaborator

Was the problem really because of PID reuse or just that if the command terminates before the lookup of its PID, the current code would just get the PID of the grep command ?

In any case, that's a good catch, thanks for debugging that.

@marcbonnici

Copy link
Copy Markdown
Collaborator Author

Was the problem really because of PID reuse or just that if the command terminates before the lookup of its PID, the current code would just get the PID of the grep command?

Perhaps reuse might not apply in this case and there was some delay introduce in spawning the initial command instead, however the result was the reported PID for the background command was returning the PID of the command used to find the PID rather than the background progress itself e.g.

In [13]: bg = ta.background("sleep 10")
    ...: print('PID: {}.format(bg.pid))
    ...: print('PID Found ', str(bg.pid) in ta.execute("ps"))

23534 /system/bin/sh -c (/data/local/tmp/bin/busybox ps -A -o pid,args | grep BACKGROUND_COMMAND_UUID=704c9a73dabc46e2af5da97d0abe6b61); echo "\n$?"
23536 /system/bin/sh -c BACKGROUND_COMMAND_UUID=704c9a73dabc46e2af5da97d0abe6b61 sh -c 'sh -c '"'"'export PATH=/data/local/tmp/bin:$PATH && export LC_ALL=C && sleep 1'"'"'  '
23539 grep BACKGROUND_COMMAND_UUID=704c9a73dabc46e2af5da97d0abe6b61

PID: 23534  # Should be: 23536 
PID Found: False  

This explains the "Process Not found" errors when trying to kill the processes as the grep had already completed, along with the leftover sleep processes preventing progress.

Sometimes the reported PID for a background command would be reported
incorrectly. This was due to an assumption that the lowest PID
was always the command we were looking for, however this is not always
true and PID's could be recycled resulting in the PID of the search
command being reported instead.

To avoid this, echo the PID directly using $$ similar to how the
SSH connection handles this scenario.
Fix missing modification of the stream output to return
a `subprocess.PIPE` instead of `subprocess.DEVNULL` after
redirection the output.
Comment thread devlib/utils/android.py
uuid_ = uuid.uuid4().hex
uuid_var = 'BACKGROUND_COMMAND_UUID={}'.format(uuid_)
command = "{} sh -c {}".format(uuid_var, quote(command))
command = 'echo $$; sh -c {}'.format(quote(command))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's minor but we could use echo $$; exec sh -c to avoid a layer

Comment thread devlib/utils/misc.py
if stream == subprocess.DEVNULL:
suffix = '{}/dev/null'.format(redirection)
stream = subprocess.PIPE
elif stream == subprocess.STDOUT:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might need to double check the use of redirect_stream in ssh.py as well. Once the pipe is setup, it has to be consumed and closed properly. For Popen uses, it should be ok as Popen's context manager closes the streams on __exit__().

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also we might want to do that only for stdout, since stderr should still be DEVNULL as we don't print anything to it.

Comment thread devlib/utils/android.py
uuid_ = uuid.uuid4().hex
uuid_var = 'BACKGROUND_COMMAND_UUID={}'.format(uuid_)
command = "{} sh -c {}".format(uuid_var, quote(command))
command = 'echo $$; sh -c {}'.format(quote(command))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

echo is unreliable on some shells, should be printf "%s\n" $$

@douglas-raillard-arm

Copy link
Copy Markdown
Collaborator

@marcbonnici I've updated my async PR with a patch that fixes that without needing to deal with complex redirections: b967194

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants