This is part of the Semicolon&Sons Code Diary - consisting of lessons learned on the job. You're in the unix category.
Last Updated: 2024-11-21
I wrote a node
script that was supposed to run end-to-end tests for an emulated Android
app using a local node
server as a backend.
As such, the testing script booted up (directly and through sub-commands):
node
serverIdeally I wanted a clean slate whenever this script ended or was interrupted. This wasn't happening with my first attempts: instead, processes were always left hanging around between runs.
I started debugging one of the problematic processes using the following approach:
$ react-native run-android &; watch -n 1 pstree -p $$
The idea is to put the problematic code (the react-native
command) in the
background with &
and then repeatedly run pstree
on the current shell to see
what was happening.
The output was as follows:
\-+= 14112 jack /Applications/iTerm.app/Contents/MacOS/iTerm2 --server login -fp jack 38:52
\-+= 14113 root login -fp jack
\-+= 14114 jack -zsh
|-+= 59518 jack node /usr/local/bin/react-native run-android
| \-+- 59547 jack /usr/bin/java -Xmx64m -Xms64m -Xdock:name=Gradle -Xdock:icon=/Users/jack/code/consulting/project_r_app/and
roid/media/gradle.icns -Dorg.gradle.appname=gradlew -classpath /Users/jack/code/consulting/project_r_app/android/gradle/wrapper/gradl
e-wrapper.jar org.gradle.wrapper.GradleWrapperMain app:installDebug -PreactNativeDevServerPort=8081
| \--- 59557 jack (sh)
\-+=-59519 jack watch -n 1 pstree -p 14114
\-+- 59551 jack pstree -p 14114
Interpretation:
zsh
, fourth executed the react-native
command (pid=59518) in parallel (due
to &
) with the watch
command, which caused this very text to display.(One unexpected interesting thing about this approach was how much useful
information for general debugging is contained within the process itself - i.e.
I can see the installDebug
was called on Gradle
and that port 8081 was used)
Before going any further, let me supplement by listing all the relevant
processes being executed. I use ps -f
to get extra info (e.g. ppid
). I also
want this sorted based on the STIME
(start time) field so I ran ps -f | sort
-k5,2
to get:
UID PID PPID C STIME TTY TIME CMD
501 59518 14114 0 10:28AM ttys001 0:01.29 node /usr/local/bin/react-native run-android
501 59519 14114 0 10:28AM ttys001 0:02.78 watch -n 1 pstree -p 14114
501 59547 59518 0 10:28AM ttys001 0:01.91 /usr/bin/java -Xmx64m -Xms64m -Xdock:name=Gradle -Xdock:icon=/Users/jack/code/consulting/project_r_app/android/media/gradle.icns -Dorg.gradle.appname=gradlew -classpath /Users/jack/code/consulting/project_r_app/android/gradle/wrapper/gradle-wrapper.jar org.gradle.wrapper.GradleWrapperMain app:installDebug -PreactNativeDevServerPort=8081
501 59562 580 0 10:28AM ttys000 0:00.04 /Applications/iTerm.app/Contents/MacOS/iTerm2 --server login -fp jack
# Space added by me
501 59567 59566 0 10:28AM ttys000 0:00.55 -zsh
501 60198 59567 0 10:28AM ttys000 0:00.01 /bin/bash /Users/jack/code/consulting/project_r_app/node_modules/react-native/scripts/launchPackager.command
501 60210 60198 0 10:28AM ttys000 0:04.38 node /Users/jack/code/consulting/project_r_app/node_modules/react-native/scripts/../cli.js start
501 60223 60210 0 10:28AM ttys000 0:00.07 /Users/jack/.nvm/versions/node/v12.9.1/bin/node /Users/jack/code/consulting/project_r_app/node_modules/jest-worker/build/workers/processChild.js
501 60224 60210 0 10:28AM ttys000 0:00.07 /Users/jack/.nvm/versions/node/v12.9.1/bin/node /Users/jack/code/consulting/project_r_app/node_modules/jest-worker/build/workers/processChild.js
501 60225 60210 0 10:28AM ttys000 0:00.08 /Users/jack/.nvm/versions/node/v12.9.1/bin/node /Users/jack/code/consulting/project_r_app/node_modules/jest-worker/build/workers/processChild.js
The main thing to notice here is that the nameless process 59567
has no
parent pid.
Running pstree
on this nameless process gives:
-+= 59567 jack -zsh
\-+= 60198 jack /bin/bash /Users/jack/code/consulting/project_r_app/node_modules/react-native/scripts/launchPackager.command
\-+- 60210 jack node /Users/jack/code/consulting/project_r_app/node_modules/react-native/scripts/../cli.js start
|--- 60223 jack /Users/jack/.nvm/versions/node/v12.9.1/bin/node /Users/jack/code/consulting/project_r_app/node_modules/jest-worker/build/workers/processChild.js
|--- 60224 jack /Users/jack/.nvm/versions/node/v12.9.1/bin/node /Users/jack/code/consulting/project_r_app/node_modules/jest-worker/build/workers/processChild.js
\--- 60225 jack /Users/jack/.nvm/versions/node/v12.9.1/bin/node /Users/jack/code/consulting/project_r_app/node_modules/jest-worker/build/workers/processChild.js
i.e. it explains all the processes below the space in the call to ps -f
above.
So a few questions remain:
react-native
spring up a non-child process? Answer: it uses open
(The open command opens a file (or a directory or URL), just as if you had
double-clicked the file's icon)On macos:
if (process.platform === 'darwin') {
try {
return execa.sync(
'open',
['-a', terminal, launchPackagerScript],
procConfig,
);
} catch (error) {
return execa.sync('open', [launchPackagerScript], procConfig);
}
}
$!
. This works because $!
gets the pid of the most recent background job. $ open -a Terminal & echo $!
Another way is to search for the process by name with pgrep
.
There is a really useful command for searching processes pgrep
. This gives you
the matching pid
based on a partial match of the name. E.g. if the process
nvim
is running, I can do
$ pgrep nvim
or $ pgrep im
to get the process id of the process named nvim.
By default pgrep
only matches against the initial command and not any of the
arguments. Thus for the process /usr/bin/java -Xmx64m -Xms64m project_r
it
would not match. Instead I can include the arguments with the f flag: pgrep -f project_r
Or in my use-case here, with the following:
pgrep -f launchPackager.command