This is part of the Semicolon&Sons Code Diary - consisting of lessons learned on the job. You're in the unix category.
Last Updated: 2025-11-17
My CPU was heating up after enabling notifications with the jest test runner:
$ jest --watch --notify
Here's the output of
$ top -o cpu
Processes: 424 total, 4 running, 420 sleeping, 2344 threads 15:57:24
PID COMMAND %CPU TIME #TH #WQ #PORT MEM PURG CMPRS PGRP PPID STATE BOOSTS %CPU_ME %CPU_OTHRS UID FAULTS COW MSGSENT MSGRECV SYSBSD SYSMACH CSW PAGEINS IDLEW POWER INSTRS CYCLES
42769 usernoted 39.3 02:02.94 3 2 145 30M 736K 0B 42769 1 running *0[1] 0.00000 39.32841 501 807098+ 287 32868+ 8236+ 38675+ 25356+ 152895+ 169 6 39.3 1714623073 1354092578
42871 terminal-not 28.2 01:21.19 7/1 5/1 175 10M+ 0B 0B 42052 42054 running *6[2] 7.84940 19.16940 501 409276+ 274 5318+ 4378+ 265903+ 26770+ 87984+ 13 731+ 28.3 1600982881 1031715039
42891 terminal-not 19.6 00:53.76 6 4 167 8640K 0B 0B 42052 42054 sleeping *3[2] 22.2848 10.17352 501 269991+ 270 3868+ 2946+ 103294+ 18324+ 56218+ 0 176 19.6 1082501253 721400427
What does this tell me?
usernoted and terminal-notifer are strange in not stopping.uid field gives the process owner. By running $ id in the terminal, I see that this is me: uid=501(jack)usernoted, the meaning of %CPU_ME being near 0 and $CPU_OTHERS being 39%csw is context switches. What is a context switch? "Storing the state of a process or of a thread, so that it can be restored and execution resumed from the same point later. This allows multiple processes to share a single CPU, and is an essential feature of a multitasking operating system."wq is work queue. "When such an asynchronous execution context is needed, a work item describing which function to execute is put on a queue. An independent thread serves as the asynchronous execution context. The queue is called workqueue and the thread is called worker."purg is purgeable memory size. Purgeable space refers to a particular type of storage space on macOS systems. Beginning with macOS Sierra, Apple introduced a new category of storage space to deal with temporary stuff. Usually it is Spotify and chrome etc.pgrp is process group. The terminal notifier entries have the value 42052, corresponding to the parent yarn test --watch in pstree below. When a signal is directed to a process group, the signal is delivered to each process that is a member of the group.MEM is physical memory usage. 30M is not that high... chrome had 1000M at the time. The + means the number was too long to display here.$ pstree
showed me what triggered the process (you can also use ps aux | grep PID)
|--= 42769 jack /usr/sbin/usernoted
\-+= 97503 jack /Applications/iTerm.app/Contents/MacOS/iTerm2
|-+= 98271 jack /Applications/iTerm.app/Contents/MacOS/iTerm2 --server login -fp jack
| \-+= 98272 root login -fp jack
| \-+= 98273 jack -zsh
| \-+= 42052 jack node /Users/jack/.nvm/versions/node/v10.14.1/bin/yarn test --watch
| \-+- 42053 jack /bin/sh /var/folders/25/f__zt0z94vl3t812lw8zxdhw0000gn/T/yarn--1565703324137-0.6366097452477053/node /Users/jack/code/oxnotes4/node_modules/.bin/jest --watch
| \-+- 42054 jack /Users/jack/.nvm/versions/node/v10.14.1/bin/node /Users/jack/code/oxnotes4/node_modules/.bin/jest --watch
| |--- 42871 jack /Users/jack/code/oxnotes4/node_modules/node-notifier/vendor/mac.noindex/terminal-notifier.app/Contents/MacOS/terminal-notifier -actions Run again,Exit tests -closeLabel "Close" -message "-&M-T️ 1 of 29 tests failed" -title "4% Failed" -ap
| |--- 42891 jack /Users/jack/code/oxnotes4/node_modules/node-notifier/vendor/mac.noindex/terminal-notifier.app/Contents/MacOS/terminal-notifier -actions Run again,Exit tests -closeLabel "Close" -message "-&M-T️ 1 of 29 tests failed" -title "4% Failed" -ap
| \--- 43048 jack /Users/jack/code/oxnotes4/node_modules/node-notifier/vendor/mac.noindex/terminal-notifier.app/Contents/MacOS/terminal-notifier -actions Run again,Exit tests -closeLabel "Close" -message "-&M-T️ 1 of 29 tests failed" -title "4% Failed" -ap
|-+= 42899 jack /Applications/iTerm.app/Contents/MacOS/iTerm2 --server login -fp jack
Here I can see that usernoted has a lower process number and is not a child
of terminal-notifier.
Since I'm here, what do the options to ps do?
ps alone is just for the current user. It's like 10 or 20 processes.ps a display information about other users' processes as well as your own but it skip any processes which do not have a controlling terminal.ps x include processes which do not have a controlling terminal.ps u displays processes for specified usernames. But it seems to be a no-op when I'm only interested by my own processes and does the same as raw psNext, how to see what is using a given (internet - thus the upcoming i option) port: $ lsof -i 3000 returns:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
ruby 45734 jack 11u IPv4 0x8e82e0828a793d27 0t0 TCP *:hbci (LISTEN)
When I look up the pid it's my Rails server sure enough.
What files were opened by this process? I can see this by running $ lsof with a
different option -p PROCESS_ID, i.e. $ lsof -p 42871. The output is now:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
terminal- 46262 jack cwd DIR 1,4 1664 1493425 /Users/jack/code/oxnotes4
terminal- 46262 jack txt REG 1,4 87688 8644841742 /Users/jack/code/oxnotes4/node_modules/node-notifier/vendor/mac.noindex/terminal-notifier.app/Contents/MacOS/terminal-notifier
terminal- 46262 jack txt REG 1,4 20688 8643421167 /Library/Preferences/Logging/.plist-cache.SyHvWEnb
terminal- 46262 jack txt REG 1,4 239616 8643002073 /private/var/db/timezone/tz/2019b.1.0/icutz/icutz44l.dat
terminal- 46262 jack txt REG 1,4 27143600 8623408143 /usr/share/icu/icudt62l.dat
terminal- 46262 jack txt REG 1,4 3479536 8630877366 /System/Library/CoreServices/SystemAppearance.bundle/Contents/Resources/Assets.car
terminal- 46262 jack txt REG 1,4 107968 8630912969 /System/Library/Caches/com.apple.IntlDataCache.le.kbdx
terminal- 46262 jack txt REG 1,4 87744 8623404141 /usr/lib/libobjc-trampolines.dylib
terminal- 46262 jack txt REG 1,4 4190688 8630877390 /System/Library/CoreServices/SystemAppearance.bundle/Contents/Resources/SystemAppearance.car
terminal- 46262 jack txt REG 1,4 4259840 8645029523 /private/var/folders/25/f__zt0z94vl3t812lw8zxdhw0000gn/0/com.apple.LaunchServices-231-v2.csstore
terminal- 46262 jack txt REG 1,4 889636 8620471251 /System/Library/Keyboard Layouts/AppleKeyboardLayouts.bundle/Contents/Resources/AppleKeyboardLayouts-L.dat
terminal- 46262 jack txt REG 1,4 213752 8630877378 /System/Library/CoreServices/SystemAppearance.bundle/Contents/Resources/FunctionRowAppearance.car
terminal- 46262 jack txt REG 1,4 510848 8630877394 /System/Library/CoreServices/SystemAppearance.bundle/Contents/Resources/VibrantLightAppearance.car
terminal- 46262 jack txt REG 1,4 1100896 8630901776 /usr/lib/dyld
terminal- 46262 jack 0u unix 0x8e82e0828ead6a07 0t0 ->0x8e82e0828ead7d8f
terminal- 46262 jack 1u unix 0x8e82e0828ead648f 0t0 ->0x8e82e0828ead7e57
terminal- 46262 jack 2u unix 0x8e82e0828ead710f 0t0 ->0x8e82e0828ead7047
Is there any thing interesting here?
fd, file descriptor, is there because files are not only opened as streams.
cwd is current working directory. txt because program text (code and
data). The numerical entries are file descriptors themselves. They are opened
in u for read and write mode. These three are stdin, stout, and
stderr. They will change if you pipe.type - DIR is directory. REG is regular file.For details about what kernel processes are being called, run $ dtruss -p 42871 with sudo.
Here's what I saw:
ulock_wake(0x1000002, 0x100105E00, 0x0) = -2 0
ulock_wait(0x1010002, 0x100105E00, 0x1A02) = 0 0
ulock_wake(0x1000002, 0x100105E00, 0x0) = -2 0
ulock_wait(0x1010002, 0x100105E00, 0xF02) = 0 0
ulock_wake(0x1000002, 0x100105E00, 0x0) = -2 0
ulock_wait(0x1010002, 0x100105E00, 0x1A02) = 0 0
ulock_wait(0x1010002, 0x100105E00, 0xF02) = 0 0
ulock_wake(0x1000002, 0x100105E00, 0x0) = -2 0
ulock_wait(0x1010002, 0x100105E00, 0xF02) = 0 0
ulock_wake(0x1000002, 0x100105E00, 0x0) = -2 0
ulock_wait(0x1010002, 0x100105E00, 0xF02) = 0 0
ulock_wake(0x1000002, 0x100105E00, 0x0) = -2 0
ulock_wake(0x1000002, 0x100105E00, 0x0) = -2 0
ulock_wait(0x1010002, 0x100105E00, 0x1A02) = 0 0
ulock_wait(0x1010002, 0x100105E00, 0xF02) = 0 0
ulock_wake(0x1000002, 0x100105E00, 0x0) = -2 0
ulock_wait(0x1010002, 0x100105E00, 0xF02) = 0 0
ulock_wake(0x1000002, 0x100105E00, 0x0) = -2 0
ulock_wait(0x1010002, 0x100105E00, 0xF02) = 0 0
ulock_wake(0x1000002, 0x100105E00, 0x0) = -2 0
__semwait_signal(0x1403, 0x0, 0x1) = -1 Err#60
kevent_id(0x7F9862D589B0, 0x7000060EFB60, 0x1) = 0 0
workq_kernreturn(0x100, 0x700006172B80, 0x1) = 0 Err#-2
bsdthread_ctl(0x100, 0x0, 0x8007) = 0 0
kevent_id(0x7F9862D3D430, 0x700006172610, 0x1) = 0 0
workq_kernreturn(0x100, 0x700006172B80, 0x1) = 0 Err#-2
kevent_id(0x7F9862D3D430, 0x700006172B80, 0x1) = 0 0
bsdthread_ctl(0x100, 0x0, 0x8007) = 0 0
munmap(0x102DDD000, 0x54000) = 0 0
__semwait_signal(0x1403, 0x0, 0x1) = -1 Err#60
kevent_id(0x7F9862D589B0, 0x7000060EFB60, 0x1) = 0 0
workq_kernreturn(0x100, 0x700006172B80, 0x1) = 0 Err#-2
bsdthread_ctl(0x100, 0x0, 0x8007) = 0 0
kevent_id(0x7F9862D3D430, 0x700006172610, 0x1) = 0 0
workq_kernreturn(0x100, 0x700006172B80, 0x1) = 0 Err#-2
kevent_id(0x7F9862D3D430, 0x700006172B80, 0x1) = 0 0
bsdthread_ctl(0x100, 0x0, 0x8007) = 0 0
munmap(0x102DDD000, 0x54000) = 0 0
__semwait_signal(0x1403, 0x0, 0x1) = -1 Err#60
kevent_id(0x7F9862D589B0, 0x7000060EFB60, 0x1) = 0 0
workq_kernreturn(0x100, 0x700006172B80, 0x1) = 0 Err#-2
bsdthread_ctl(0x100, 0x0, 0x8007) = 0 0
kevent_id(0x7F9862E7ED90, 0x700006172610, 0x1) = 0 0
workq_kernreturn(0x100, 0x700006172B80, 0x1) = 0 Err#-2
kevent_id(0x7F9862E7ED90, 0x700006172B80, 0x1) = 0 0
bsdthread_ctl(0x100, 0x0, 0x8007) = 0 0
munmap(0x102DDD000, 0x54000) = 0 0