dimhold.by
← Writing

On my Windows laptop whotop found who holds each port. 11 of its answers quoted a word that was not there

I wrote whotop in August, after an evening when port 4310 was taken and I killed the wrong node. On 19 August I put the package on npm. The README promises one thing above the others: whotop says a value is unavailable and why instead of making a confident guess. Every role carries the rule that produced it.

I checked that promise on fixtures I recorded myself and on CI runners. I never took a real evening of my own machine and asked it, row by row, the one question the tool exists for. A few days ago I wrote about a night log of my laptop. The same log has the answer: 96 snapshots of whotop ls --all --json, version 0.4.1, on Windows, not elevated. I took the snapshot from 01:59 on 29 September, when my agent sessions had just stopped working. I looked only at the processes that hold a listening TCP socket.

There are 33 of them, out of 484 processes. whotop gave a role to 21: 20 node processes it calls dev servers and Postgres. The other 12 are unknown. 11 of those did not disclose a command line at all.

The port part works

On Windows whotop asks PowerShell for the process table and the socket table in one call. The owning pid of each socket comes from the same moment as the process list. The README calls a port reported against the wrong process the one mistake this tool must not make. On this night I found nothing in the log that contradicts the port column.

I did not plan this example. 5 processes on my laptop were started as astro preview --port 4321, at different times over 2 days. I never stopped them. Only one of them, pid 23620, holds 4321. The socket table puts the other 4 on 4322, 4323, 4324 and 4325. Another 5 were started with --port 4326 in the first 20 minutes after midnight and sit on 4326 to 4330. For 8 processes the port in the command line is not the port they hold.

the command line asks the socket table says astro preview --port 4321 4321 astro preview --port 4321 4322 astro preview --port 4321 4323 astro preview --port 4321 4324 astro preview --port 4321 4325 astro preview --port 4326 4326 astro preview --port 4326 4327 astro preview --port 4326 4328 astro preview --port 4326 4329 astro preview --port 4326 4330
The 10 astro preview processes started with --port 4321 or --port 4326, snapshot at 01:59. 8 of them hold a port that is not in their command line.

This is my August evening again. If I search the command lines for 4321, I get 5 candidates and 4 of them are wrong. whotop port 4321 gives one pid.

11 rows quote evidence that is not there

All 11 astro processes, these 10 and one more started with --port 4331, are labelled vite. The reason printed next to each says “known dev server in the command line (vite)”. There is no “vite” in any of these 11 command lines. They run astro.js preview.

I went to the code. The rule fires because the entry script is astro, which is fine. But the label and the reason are both built by a helper that looks for the first dev server token in the command line. When it finds none it returns the first token of the list anyway. That token is vite. The role is right. But the word in the label and in the reason comes from the fallback. The README says a classification you can not check is a guess with better manners. Here the command line was in the log, so I could check it.

The directories are only inferred

whotop does not read another process’s working directory on Windows. That would mean reading its memory, so it takes the longest absolute path from the command line and marks it (inferred). For the 11 astro rows it is the project directory, which looks right, although I can not verify it from the log either.

For 7 more rows it says nothing useful. 6 http-server processes and 1 serve run from the npx cache, so the only path in their command line is that cache. whotop cuts it before node_modules and reports the cache directory. The 6 http-server rows get the same directory and can not be told apart by it. One of them holds 4310, the port from my August evening.

One row is wrong because of a bug in reading the path. The ts-node-dev server on port 4400 has 2 paths in its command line, a hook file in D:/Temp and then the project path. My expression that finds paths allows spaces inside a path. The first match runs over the space and takes the drive letter of the second path with it. whotop never sees the second path and the row says D:/Temp. I reproduced it on the published package: without the hook path the same command gives the project directory.

-r D:/Temp/hook-0417.js D :\work\api\node_modules\...\wrap.js 1st match, ends at the colon never matched reported as the directory: D:/Temp same command without the hook path: D:\work\api
The path finder allows spaces inside a path. The first match takes the drive letter of the next path, so the project path is never read. Paths shortened.

Of 20 node rows 8 have a directory that is technically marked inferred and is no help when I am about to kill something. 3 more rows got only the name of their script file as a label, but there the reason says honestly that nothing else matched. The README describes the directory problem with claude.exe, whose install folder looked like a working directory. I fixed that case in August and did not look for others.

port evidence directory 4301 serve file name npx cache 4310 http-server npx cache 4311 http-server npx cache 4312 http-server npx cache 4313 http-server npx cache 4314 http-server npx cache 4315 http-server npx cache 4321 astro quotes vite 4322 astro quotes vite 4323 astro quotes vite 4324 astro quotes vite 4325 astro quotes vite 4326 astro quotes vite 4327 astro quotes vite 4328 astro quotes vite 4329 astro quotes vite 4330 astro quotes vite 4331 astro quotes vite 4400 ts-node-dev file name temp folder 8765 script file name
The 20 node rows with a listening port at 01:59. 11 labels quote a word that is not there, 8 directories say nothing about the project.

The service names stay on one screen

In this snapshot 163 of 484 processes hid their command line. The service registry, which whotop reads without elevation, named 115 of them. The warning in every snapshot says so. But I searched the whole night log for a service name and found none. The names are attached to the process, but only the interactive screen prints them. The listing, the port command and the JSON output drop them.

I reproduced it with a payload: a svchost.exe holding 135 with the service RpcSs. In the interactive screen it reads svchost.exe [RpcSs]. whotop port 135 shows unknown and advises an elevated shell. whotop ls RpcSs finds nothing. On my laptop 4 of the 12 unknown listeners were svchost, which exists to host services. The log can not tell me which services they were, because the JSON had already dropped the names.

What I did not check

I can not check the real working directory of any of these processes from the log. I did not run whotop elevated, so I do not know how many of the 11 silent listeners an elevated shell would name. Ports 139, 445 and 5357 belong to pid 4, System. whotop just says unknown there. The snapshot also has 25 sockets without an owning process, which no row shows.

It is one laptop and one snapshot, but the numbers do not move much over the night. 81 of 96 snapshots have 32 or 33 listeners with 21 named. The 11 vite labels are in 91 snapshots and the 7 npx directories in all 96.

None of the 3 problems is fixed in 0.4.1, the version on npm today.